Compare commits

...

3 Commits

Author SHA1 Message Date
32c3c6e356 Use atomic wait/notify on RXTAIL in KEY 2026-03-02 18:53:48 +00:00
2a3949e09f Make terminal xHEAD and xTAIL registers 32 bits
This enables waiting on them with memory.atomic.wait32 (there is no
wait8) which is needed to avoid spinning when waiting for a key.
2026-03-02 18:51:42 +00:00
22e477adf7 Fix hex number input 2026-03-02 18:50:34 +00:00
4 changed files with 52 additions and 37 deletions

View File

@@ -44,12 +44,12 @@ You could use any HTTP server that sets these headers.
| Name | Address | Size / B | Access | | Name | Address | Size / B | Access |
|--------|---------|----------|--------------| |--------|---------|----------|--------------|
| TXBUF | 0 | 32 | write | | TXBUF | 00h | 32 | write |
| RXBUF | 32 | 32 | read | | RXBUF | 20h | 32 | read |
| TXHEAD | 64 | 1 | atomic read | | TXHEAD | 40h | 4 | atomic read |
| TXTAIL | 65 | 1 | atomic write | | TXTAIL | 44h | 4 | atomic write |
| RXHEAD | 66 | 1 | atomic write | | RXHEAD | 48h | 4 | atomic write |
| RXTAIL | 67 | 1 | atomic read | | RXTAIL | 4Ch | 4 | atomic read |
For both sending (`TX`) and receiving (`RX`), there are three For both sending (`TX`) and receiving (`RX`), there are three
registers: `xBUF`, `xHEAD` and `xTAIL`: registers: `xBUF`, `xHEAD` and `xTAIL`:
@@ -67,7 +67,7 @@ unoccupied byte between the tail and the head.
| Name | Address | Size / B | Access | | Name | Address | Size / B | Access |
|----------|---------|----------|--------------| |----------|---------|----------|--------------|
| SYSREADY | 68 | 1 | atomic write | | SYSREADY | 50h | 1 | atomic write |
The `SYSREADY` register is used to indicate when the system has booted 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.

18
emu.js
View File

@@ -1,14 +1,14 @@
const TXBUF = 0; const TXBUF = 0x00;
const RXBUF = 32; const RXBUF = 0x20;
const TXHEAD = 64; const TXHEAD = 0x40;
const TXTAIL = 65; const TXTAIL = 0x44;
const RXHEAD = 66; const RXHEAD = 0x48;
const RXTAIL = 67; const RXTAIL = 0x4c;
const SYSREADY = 68; const SYSREADY = 0x50;
const TXBUF_SIZE = 32; const TXBUF_SIZE = 32;
const RXBUF_SIZE = 32; const RXBUF_SIZE = 32;
const PERIPHS_SIZE = 69; // Nice const PERIPHS_SIZE = 81;
const POLL_INTERVAL_MS = 20; const POLL_INTERVAL_MS = 20;
@@ -27,6 +27,7 @@ class Emulator {
this.mem_u8 = new Uint8Array(this.mem.buffer); this.mem_u8 = new Uint8Array(this.mem.buffer);
for (let i = 0; i < PERIPHS_SIZE; ++i) for (let i = 0; i < PERIPHS_SIZE; ++i)
this.mem_u8[i] = 0; this.mem_u8[i] = 0;
this.mem_i32 = new Int32Array(this.mem.buffer);
this.decoder = new TextDecoder('utf-8'); this.decoder = new TextDecoder('utf-8');
this.encoder = new TextEncoder('utf-8'); this.encoder = new TextEncoder('utf-8');
@@ -103,6 +104,7 @@ class Emulator {
tail = this.fifo_next(tail); tail = this.fifo_next(tail);
} while (this.fifo_next(tail) != head && this.rx_queue.length != 0); } while (this.fifo_next(tail) != head && this.rx_queue.length != 0);
Atomics.store(this.mem_u8, RXTAIL, tail); Atomics.store(this.mem_u8, RXTAIL, tail);
Atomics.notify(this.mem_i32, RXTAIL / 4);
} }
print(str) { print(str) {

View File

@@ -63,18 +63,6 @@
46 EMIT 46 EMIT
\ Peripheral register addresses
: TXBUF 0 ;
: RXBUF 32 ;
: TXHEAD 64 ;
: TXTAIL 65 ;
: RXHEAD 66 ;
: RXTAIL 67 ;
: SYSREADY 68 ;
46 EMIT
\ Printing utilities \ Printing utilities
: CR 10 EMIT ; : CR 10 EMIT ;
@@ -214,6 +202,22 @@ CHAR . EMIT
CHAR . EMIT CHAR . EMIT
\ Peripheral register addresses
HEX
00 CONSTANT TXBUF
20 CONSTANT RXBUF
40 CONSTANT TXHEAD
44 CONSTANT TXTAIL
48 CONSTANT RXHEAD
4C CONSTANT RXTAIL
50 CONSTANT SYSREADY
DECIMAL
46 EMIT
\ A better word-not-found handler \ A better word-not-found handler
: ANY-RX? RXHEAD AC@ RXTAIL AC@ <> ; : ANY-RX? RXHEAD AC@ RXTAIL AC@ <> ;

View File

@@ -8,9 +8,9 @@
(global $TXBUF i32 (i32.const 0x0000)) (global $TXBUF i32 (i32.const 0x0000))
(global $RXBUF i32 (i32.const 0x0020)) (global $RXBUF i32 (i32.const 0x0020))
(global $TXHEAD i32 (i32.const 0x0040)) (global $TXHEAD i32 (i32.const 0x0040))
(global $TXTAIL i32 (i32.const 0x0041)) (global $TXTAIL i32 (i32.const 0x0044))
(global $RXHEAD i32 (i32.const 0x0042)) (global $RXHEAD i32 (i32.const 0x0048))
(global $RXTAIL i32 (i32.const 0x0043)) (global $RXTAIL i32 (i32.const 0x004c))
;; Forth registers ;; Forth registers
(global $rsp (mut i32) (i32.const 0)) (global $rsp (mut i32) (i32.const 0))
@@ -628,15 +628,24 @@
;; Serial I/O ;; Serial I/O
(func $key (local $head i32) (func $key (local $head i32)
;; Wait for RXBUF to be non-empty
loop $wait
global.get $RXHEAD global.get $RXHEAD
i32.atomic.load8_u i32.atomic.load8_u
local.tee $head local.tee $head
;; Wait for RXBUF to be non-empty
loop $wait (param i32)
global.get $RXTAIL global.get $RXTAIL
i32.atomic.load8_u i32.atomic.load8_u
i32.eq i32.eq
br_if $wait if
global.get $RXTAIL
local.get $head
i64.const -1
memory.atomic.wait32
local.get $head
br $wait
end
end end
;; Read byte at head position ;; Read byte at head position
@@ -1395,7 +1404,7 @@
;; ;;
;; DUP DUP 65 >= SWAP 90 <= AND \ Test if A-Z ;; DUP DUP 65 >= SWAP 90 <= AND \ Test if A-Z
;; 0BRANCH [60] \ Jump to invalid digit if not ;; 0BRANCH [60] \ Jump to invalid digit if not
;; 45 - \ Get digit value ;; 55 - \ Get digit value
;; ;;
;; DUP DUP 0>= SWAP BASE @ < AND \ Test if 0 <= value < BASE ;; DUP DUP 0>= SWAP BASE @ < AND \ Test if 0 <= value < BASE
;; 0BRANCH [8] \ Jump to invalid digit if not ;; 0BRANCH [8] \ Jump to invalid digit if not
@@ -1438,7 +1447,7 @@
"\b4\04\00\00" ;; 0BRANCH "\b4\04\00\00" ;; 0BRANCH
"\3c\00\00\00" ;; 60 "\3c\00\00\00" ;; 60
"\18\04\00\00" ;; LIT "\18\04\00\00" ;; LIT
"\2d\00\00\00" ;; 45 "\37\00\00\00" ;; 55
"\cc\02\00\00" ;; - "\cc\02\00\00" ;; -
"\08\02\00\00" ;; DUP "\08\02\00\00" ;; DUP
"\08\02\00\00" ;; DUP "\08\02\00\00" ;; DUP