diff --git a/README.md b/README.md
index a13cb54..1ec555e 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,10 @@ unoccupied byte between the tail and the head.
| Name | Address | Size / B | Access |
|----------|---------|----------|--------------|
-| SYSREADY | 50h | 1 | atomic write |
+| 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/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;
}
}
diff --git a/emu.js b/emu.js
index ad4488a..11f497d 100644
--- a/emu.js
+++ b/emu.js
@@ -1,17 +1,17 @@
-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 SYSINTER = 0x114;
-const POLL_INTERVAL_MS = 20;
-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;
@@ -51,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')
@@ -85,15 +89,21 @@ 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');
}
}
}
fifo_next(idx) {
- return (idx + 1) & 0x1f;
+ return (idx + 1) & 0x7f;
}
handle_tx_data(head, tail) {
@@ -181,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);
}
@@ -273,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 379b181..2b4f3e4 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,24 +181,21 @@ CHAR . EMIT
THEN
; IMMEDIATE
-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
+000 CONSTANT TXBUF
+080 CONSTANT RXBUF
+100 CONSTANT TXHEAD
+104 CONSTANT TXTAIL
+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,13 +218,11 @@ DECIMAL
' WNF-HANDLER TO WNFHOOK
-CHAR . EMIT
-
\ Version number
0 CONSTANT VERSION-MAJOR
2 CONSTANT VERSION-MINOR
-1 CONSTANT VERSION-PATCH
+2 CONSTANT VERSION-PATCH
: PRINT-VERSION
CHAR v EMIT VERSION-MAJOR .
@@ -258,8 +230,6 @@ CHAR . EMIT
CHAR . EMIT VERSION-PATCH .
;
-CHAR . EMIT
-
\ Welcome banner
: BANNER
@@ -269,7 +239,7 @@ CHAR . EMIT
." |__,__/_/ .__(_)___/_//_/" 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
@@ -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/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))
diff --git a/wipforth.ws b/wipforth.ws
index 2e84e8b..5deb1e9 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
@@ -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
@@ -752,7 +761,7 @@
local.get head
i32.const 1
i32.add
- i32.const 1Fh
+ i32.const 7Fh
i32.and
i32.atomic.store8 0 0
@@ -768,7 +777,7 @@
local.tee tail
i32.const 1
i32.add
- i32.const 1Fh
+ i32.const 7Fh
i32.and
local.tee n
i32.const TXHEAD
@@ -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