From fe5c55cabf9c38d596100ed6a8366374d3d29740 Mon Sep 17 00:00:00 2001 From: Camden Dixie O'Brien Date: Fri, 20 Mar 2026 12:45:43 +0000 Subject: [PATCH 1/8] Increase TX and RX buffer size to 128 bytes --- README.md | 19 ++++++++++--------- emu.js | 20 +++++++++----------- prelude.f | 14 +++++++------- wipforth.ws | 16 ++++++++-------- 4 files changed, 34 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index a13cb54..d073c15 100644 --- a/README.md +++ b/README.md @@ -74,20 +74,21 @@ guile tests.scm | xmllint --format - | Name | Address | Size / B | Access | |--------|---------|----------|--------------| -| TXBUF | 00h | 32 | write | -| RXBUF | 20h | 32 | read | -| TXHEAD | 40h | 4 | atomic read | -| TXTAIL | 44h | 4 | atomic write | -| RXHEAD | 48h | 4 | atomic write | -| RXTAIL | 4Ch | 4 | atomic read | +| TXBUF | 000h | 32 | write | +| RXBUF | 080h | 32 | read | +| TXHEAD | 100h | 4 | atomic read | +| TXTAIL | 104h | 4 | atomic write | +| RXHEAD | 108h | 4 | atomic write | +| RXTAIL | 10Ch | 4 | atomic read | For both sending (`TX`) and receiving (`RX`), there are three registers: `xBUF`, `xHEAD` and `xTAIL`: -- `xBUF` registers are 32-byte FIFO ring buffers used for data +- `xBUF` registers are 128-byte FIFO ring buffers used for data - The `xHEAD` and `xTAIL` registers specify the start and end of data in the ring buffer, `xHEAD` being the offset of the first byte of - data, and `xTAIL` being the offset of the first byte *after* the data. + data, and `xTAIL` being the offset of the first byte *after* the + data. In order to be distinguishable from the empty state, the ring buffers must never be completely full -- there must always be *at least one* @@ -97,7 +98,7 @@ unoccupied byte between the tail and the head. | Name | Address | Size / B | Access | |----------|---------|----------|--------------| -| SYSREADY | 50h | 1 | atomic write | +| SYSREADY | 110h | 4 | atomic write | The `SYSREADY` register is used to indicate when the system has booted up and is ready for user input. diff --git a/emu.js b/emu.js index ad4488a..9df7f9e 100644 --- a/emu.js +++ b/emu.js @@ -1,17 +1,15 @@ -const TXBUF = 0x00; -const RXBUF = 0x20; -const TXHEAD = 0x40; -const TXTAIL = 0x44; -const RXHEAD = 0x48; -const RXTAIL = 0x4c; -const SYSREADY = 0x50; +const TXBUF = 0x000; +const RXBUF = 0x080; +const TXHEAD = 0x100; +const TXTAIL = 0x104; +const RXHEAD = 0x108; +const RXTAIL = 0x10c; -const TXBUF_SIZE = 32; -const RXBUF_SIZE = 32; -const PERIPHS_SIZE = 81; +const SYSREADY = 0x110; const POLL_INTERVAL_MS = 20; const DOT_INTERVAL_MS = 120; +const PERIPHS_SIZE = 0x200; const COLS = 80; const TAB_WIDTH = 8; @@ -93,7 +91,7 @@ class Emulator { } fifo_next(idx) { - return (idx + 1) & 0x1f; + return (idx + 1) & 0x7f; } handle_tx_data(head, tail) { diff --git a/prelude.f b/prelude.f index 379b181..4c7e2d4 100644 --- a/prelude.f +++ b/prelude.f @@ -210,13 +210,13 @@ CHAR . EMIT HEX -00 CONSTANT TXBUF -20 CONSTANT RXBUF -40 CONSTANT TXHEAD -44 CONSTANT TXTAIL -48 CONSTANT RXHEAD -4C CONSTANT RXTAIL -50 CONSTANT SYSREADY +000 CONSTANT TXBUF +080 CONSTANT RXBUF +100 CONSTANT TXHEAD +104 CONSTANT TXTAIL +108 CONSTANT RXHEAD +10C CONSTANT RXTAIL +110 CONSTANT SYSREADY DECIMAL diff --git a/wipforth.ws b/wipforth.ws index 2e84e8b..a0cd37a 100644 --- a/wipforth.ws +++ b/wipforth.ws @@ -2,12 +2,12 @@ .import main "emu" "mem" ;; Peripheral registers -.def TXBUF 00h -.def RXBUF 20h -.def TXHEAD 40h -.def TXTAIL 44h -.def RXHEAD 48h -.def RXTAIL 4Ch +.def TXBUF 000h +.def RXBUF 080h +.def TXHEAD 100h +.def TXTAIL 104h +.def RXHEAD 108h +.def RXTAIL 10Ch .def DICT_START 0200h @@ -752,7 +752,7 @@ local.get head i32.const 1 i32.add - i32.const 1Fh + i32.const 7Fh i32.and i32.atomic.store8 0 0 @@ -768,7 +768,7 @@ local.tee tail i32.const 1 i32.add - i32.const 1Fh + i32.const 7Fh i32.and local.tee n i32.const TXHEAD -- 2.34.1 From c20e7e181b36ce54d84e5f7059343d3834e34a03 Mon Sep 17 00:00:00 2001 From: Camden Dixie O'Brien Date: Fri, 20 Mar 2026 12:46:21 +0000 Subject: [PATCH 2/8] Reduce poll interval to 5 ms --- emu.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/emu.js b/emu.js index 9df7f9e..86d6c40 100644 --- a/emu.js +++ b/emu.js @@ -7,10 +7,11 @@ const RXTAIL = 0x10c; const SYSREADY = 0x110; -const POLL_INTERVAL_MS = 20; const DOT_INTERVAL_MS = 120; const PERIPHS_SIZE = 0x200; +const POLL_INTERVAL_MS = 5; + const COLS = 80; const TAB_WIDTH = 8; -- 2.34.1 From 36429bf8bcd7a02ca765c0faeea125f61c5d0b5b Mon Sep 17 00:00:00 2001 From: Camden Dixie O'Brien Date: Fri, 20 Mar 2026 12:47:33 +0000 Subject: [PATCH 3/8] Rework tokenizer to avoid unecessary allocations --- asm.js | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/asm.js b/asm.js index 604b750..0d07149 100644 --- a/asm.js +++ b/asm.js @@ -14,20 +14,29 @@ class Tokenizer { this.buffer = []; this.comment = false; this.string = false; + this.cursor = 0; } + find(pred) { + for (let i = this.cursor; i < this.buffer.length; ++i) { + if (pred(this.buffer[i])) + return i; + } + return null; + } + skip() { - const idx = this.buffer.findIndex((cp) => !this.skips.has(cp)); - this.buffer = idx == -1 ? [] : this.buffer.slice(idx); + const idx = this.find((cp) => !this.skips.has(cp)); + this.cursor = idx == null ? this.buffer.length : idx; } next_string() { - const idx = this.buffer.findIndex((cp) => cp == this.string_quote); - if (idx == -1) { + const idx = this.find((cp) => cp == this.string_quote); + if (idx == null) { this.string = true; } else { - const string = this.buffer.slice(0, idx).join(""); - this.buffer = this.buffer.slice(idx + 1); + const string = this.buffer.slice(this.cursor, idx).join(""); + this.cursor = idx + 1; this.string = false; return { string: string }; } @@ -38,18 +47,20 @@ class Tokenizer { return this.next_string(); this.skip(); - if (this.buffer[0] == LINE_END) - return this.buffer.shift(); + if (this.buffer[this.cursor] == LINE_END) { + ++this.cursor; + return LINE_END; + } - if (this.buffer[0] == this.string_quote) { - this.buffer.shift(); + if (this.buffer[this.cursor] == this.string_quote) { + ++this.cursor; return this.next_string(); } - const idx = this.buffer.findIndex((cp) => this.delims.has(cp)); - if (idx != -1) { - const token = this.buffer.slice(0, idx).join(""); - this.buffer = this.buffer.slice(idx); + const idx = this.find((cp) => this.delims.has(cp)); + if (idx != null) { + const token = this.buffer.slice(this.cursor, idx).join(""); + this.cursor = idx; return token; } } -- 2.34.1 From 2c13ad4e1fde326679bbe7216c0f8b577e7d05fe Mon Sep 17 00:00:00 2001 From: Camden Dixie O'Brien Date: Fri, 20 Mar 2026 12:53:33 +0000 Subject: [PATCH 4/8] Handle prelude loading message in JS rather than Forth --- README.md | 5 ++++- emu.js | 35 +++++++++++++++++++++++++---------- prelude.f | 40 +++++----------------------------------- wipforth.ws | 18 ++++++++++++++++++ 4 files changed, 52 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index d073c15..1ec555e 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,9 @@ unoccupied byte between the tail and the head. | Name | Address | Size / B | Access | |----------|---------|----------|--------------| | SYSREADY | 110h | 4 | atomic write | +| SYSINTER | 114h | 4 | atomic read | The `SYSREADY` register is used to indicate when the system has booted -up and is ready for user input. +up and is ready for user input. `SYSINTER` is set (and notified on) +once the emulator has enabled user input and the system is +interactive. diff --git a/emu.js b/emu.js index 86d6c40..c9943d4 100644 --- a/emu.js +++ b/emu.js @@ -6,6 +6,7 @@ const RXHEAD = 0x108; const RXTAIL = 0x10c; const SYSREADY = 0x110; +const SYSINTER = 0x114; const DOT_INTERVAL_MS = 120; const PERIPHS_SIZE = 0x200; @@ -50,14 +51,18 @@ class Emulator { document.addEventListener('keydown', (e) => this.handle_keydown(e)); window.addEventListener('resize', () => this.handle_resize()); + this.forth = new Worker('boot.js', { type: 'module' }); + this.print("Assembling kernel "); - const dots = setInterval(() => this.print("."), DOT_INTERVAL_MS); - this.worker = new Worker('boot.js', { type: 'module' }); - this.worker.postMessage({ type: "load", mem: this.mem }); - this.worker.onmessage = (e) => { - clearInterval(dots); + this.dots = setInterval(() => this.print("."), DOT_INTERVAL_MS); + this.forth.postMessage({ type: "load", mem: this.mem }); + this.forth.onmessage = (e) => { + clearInterval(this.dots); this.print(" done\n"); - this.worker.postMessage({ type: "boot" }); + + this.print("Loading prelude "); + this.forth.postMessage({ type: "boot" }); + this.dots = setInterval(() => this.print("."), DOT_INTERVAL_MS); }; fetch('prelude.f') @@ -84,9 +89,15 @@ class Emulator { if (!this.input_enable) { const sysready = Atomics.load(this.mem_u8, SYSREADY); if (sysready != 0) { + clearInterval(this.dots); + this.print(" done\n"); + + Atomics.store(this.mem_u8, SYSINTER, 1); + Atomics.notify(this.mem_i32, SYSINTER / 4); + this.input_enable = true; + this.blink = true; this.flush_output(); - document.getElementById('cursor').classList.add('blinking'); } } } @@ -180,12 +191,14 @@ class Emulator { } e.preventDefault(); + this.blink = false; this.flush_output(); document.getElementById('cursor').classList.remove('blinking'); if (this.idle_timer) clearTimeout(this.idle_timer); this.idle_timer = setTimeout(() => { + this.blink = true; document.getElementById('cursor').classList.add('blinking'); }, CURSOR_IDLE_TIME_MS); } @@ -272,10 +285,12 @@ class Emulator { return row.map((c, x) => { const ec = this.html_escape(c); if (this.input_enable - && x == this.cursor.x && y == this.cursor.y) - return '' + ec + ''; - else + && x == this.cursor.x && y == this.cursor.y) { + const cl = this.blink ? 'class="blinking"' : ''; + return `` + ec + ''; + } else { return ec; + } }).join('').trimEnd(); }).join('\n'); this.output.innerHTML = html; diff --git a/prelude.f b/prelude.f index 4c7e2d4..16b1236 100644 --- a/prelude.f +++ b/prelude.f @@ -1,12 +1,5 @@ -76 EMIT 111 EMIT 97 EMIT 100 EMIT 105 EMIT 110 EMIT 103 EMIT 32 EMIT -112 EMIT 114 EMIT 101 EMIT 108 EMIT 117 EMIT 100 EMIT 101 EMIT 32 EMIT - : \ KEY 10 = 0BRANCH [ -20 , ] ; IMMEDIATE \ Now we have line comments :) -\ We'll periodically sprinkle these in so that it's clear to the user -\ that things are happening. -46 EMIT - \ Conditionals : IF @@ -27,8 +20,6 @@ SWAP ! ; IMMEDIATE -46 EMIT - \ Loops : BEGIN HERE @ ; IMMEDIATE @@ -43,8 +34,6 @@ HERE @ - , ; IMMEDIATE -46 EMIT - \ Recursive calls : RECURSE LATEST @ >CFA , ; IMMEDIATE @@ -61,8 +50,6 @@ ( ( Take that, C ) ) -46 EMIT - \ Printing utilities : CR 10 EMIT ; @@ -80,8 +67,6 @@ + EMIT ; -CHAR . EMIT - : . \ Handle negatives DUP 0< IF CHAR - EMIT NEGATE THEN @@ -110,8 +95,6 @@ CHAR . EMIT 2DROP ; -CHAR . EMIT - : TYPE ( addr len -- ) BEGIN DUP 0= IF 2DROP EXIT THEN @@ -120,8 +103,6 @@ CHAR . EMIT AGAIN ; -CHAR . EMIT - : C, HERE @ C! 1 HERE +! ; : ." @@ -151,8 +132,6 @@ CHAR . EMIT THEN ; IMMEDIATE -CHAR . EMIT - \ Misc utilities : NIP SWAP DROP ; @@ -171,8 +150,6 @@ CHAR . EMIT : [COMPILE] ' , ; IMMEDIATE -CHAR . EMIT - \ Constants, variables and values : CONSTANT @@ -204,8 +181,6 @@ CHAR . EMIT THEN ; IMMEDIATE -CHAR . EMIT - \ Peripheral register addresses HEX @@ -217,11 +192,10 @@ HEX 108 CONSTANT RXHEAD 10C CONSTANT RXTAIL 110 CONSTANT SYSREADY +114 CONSTANT SYSINTER DECIMAL -46 EMIT - \ A better word-not-found handler : ANY-RX? RXHEAD AC@ RXTAIL AC@ <> ; @@ -244,8 +218,6 @@ DECIMAL ' WNF-HANDLER TO WNFHOOK -CHAR . EMIT - \ Version number 0 CONSTANT VERSION-MAJOR @@ -258,8 +230,6 @@ CHAR . EMIT CHAR . EMIT VERSION-PATCH . ; -CHAR . EMIT - \ Welcome banner : BANNER @@ -277,8 +247,8 @@ CHAR . EMIT CR ; -." done" CR -BANNER - -\ Set SYSREADY high to enable user input +\ Set SYSREADY high and wait until interactive 1 SYSREADY AC! +SYSINTER WAIT DROP + +BANNER diff --git a/wipforth.ws b/wipforth.ws index a0cd37a..5deb1e9 100644 --- a/wipforth.ws +++ b/wipforth.ws @@ -635,6 +635,15 @@ end call next +.func wait +.elem codewords wait WAIT_CODEWORD + call pop + i32.const 0 + i64.const -1 + memory.atomic.wait32 2 0 + call push + call next + ;; Core utility words .func exit @@ -1226,6 +1235,15 @@ COPY: .word COPY_CODEWORD .def PREV _COPY +_WAIT: + .word PREV + .byte 4 + .utf8 "WAIT" + .align +WAIT: + .word WAIT_CODEWORD +.def PREV _WAIT + _EXIT: .word PREV .byte 4 -- 2.34.1 From 97fc43bf9304e8ae564b585c89faa40bbed709a7 Mon Sep 17 00:00:00 2001 From: Camden Dixie O'Brien Date: Fri, 20 Mar 2026 12:54:55 +0000 Subject: [PATCH 5/8] Add "Welcome to" to welcome message --- prelude.f | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prelude.f b/prelude.f index 16b1236..aa10607 100644 --- a/prelude.f +++ b/prelude.f @@ -239,7 +239,7 @@ DECIMAL ." |__,__/_/ .__(_)___/_//_/" CR ." /_/ " CR CR - ." Wipforth " PRINT-VERSION CR + ." Welcome to Wipforth " PRINT-VERSION ." !" CR ." Copyright (c) Camden Dixie O'Brien" CR CR ." Wipforth is freely available to use, modify and distribute for personal use" CR -- 2.34.1 From d202157a589d1cd93125929cc419b1d1e7a79868 Mon Sep 17 00:00:00 2001 From: Camden Dixie O'Brien Date: Fri, 20 Mar 2026 12:55:43 +0000 Subject: [PATCH 6/8] Reduce dot interval to 25 ms --- emu.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emu.js b/emu.js index c9943d4..11f497d 100644 --- a/emu.js +++ b/emu.js @@ -8,10 +8,10 @@ const RXTAIL = 0x10c; const SYSREADY = 0x110; const SYSINTER = 0x114; -const DOT_INTERVAL_MS = 120; const PERIPHS_SIZE = 0x200; const POLL_INTERVAL_MS = 5; +const DOT_INTERVAL_MS = 25; const COLS = 80; const TAB_WIDTH = 8; -- 2.34.1 From 7961c686390c442946bcea82f1a59b377fd6f1c3 Mon Sep 17 00:00:00 2001 From: Camden Dixie O'Brien Date: Fri, 20 Mar 2026 12:56:18 +0000 Subject: [PATCH 7/8] Reduce wait in e2e tests --- tests.scm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests.scm b/tests.scm index 3f4c9e1..284ee23 100644 --- a/tests.scm +++ b/tests.scm @@ -19,7 +19,7 @@ (define client (client-setup)) (navigate client "http://localhost:8080") -(sleep 5) +(sleep 1) (define-test kernel-assembles-successfully (let* ((display (get-display client)) -- 2.34.1 From cbe5733fcd1979c1c2470533e60b1e24e8ca8e34 Mon Sep 17 00:00:00 2001 From: Camden Dixie O'Brien Date: Fri, 20 Mar 2026 12:55:23 +0000 Subject: [PATCH 8/8] Bump version patch number --- prelude.f | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prelude.f b/prelude.f index aa10607..2b4f3e4 100644 --- a/prelude.f +++ b/prelude.f @@ -222,7 +222,7 @@ DECIMAL 0 CONSTANT VERSION-MAJOR 2 CONSTANT VERSION-MINOR -1 CONSTANT VERSION-PATCH +2 CONSTANT VERSION-PATCH : PRINT-VERSION CHAR v EMIT VERSION-MAJOR . -- 2.34.1