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