From 7befe56597135e7f3e52ba1684777c0ef0fb15d9 Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Sat, 14 Mar 2020 19:10:39 -0400 Subject: [PATCH] forth: improve input flow Readline, instead of being triggered by the end of execution of the last compiled line is now triggered "just in time", by "WORD". This allows IMMEDIATE words reading input buffer to span multiple lines ( comments for example, but colon definitions will soon follow ). --- apps/forth/core.fth | 22 +++++++++++----------- apps/forth/dict.asm | 10 +++++++++- apps/forth/dictionary.txt | 17 ++++++++++++++--- apps/forth/main.asm | 24 ++++++++++-------------- apps/forth/util.asm | 16 ++++++++++++---- 5 files changed, 56 insertions(+), 33 deletions(-) diff --git a/apps/forth/core.fth b/apps/forth/core.fth index b01ddc7..2b8c7c2 100644 --- a/apps/forth/core.fth +++ b/apps/forth/core.fth @@ -7,18 +7,18 @@ : NOT 1 SWAP SKIP? EXIT 0 * ; : RECURSE R> R> 2 - >R >R EXIT ; : ( LIT@ ) WORD SCMP NOT SKIP? RECURSE ; IMMEDIATE -( Hello, hello, krkrkrkr... do you hear me? ) -( Ah, voice at last! Some lines above need comments ) -( BTW: Forth lines limited to 64 cols because of default ) -( input buffer size in Collapse OS ) -( NOT: a bit convulted because we don't have IF yet ) -( RECURSE: RS TOS is for RECURSE itself, then we have to dig ) -( one more level to get to RECURSE's parent's caller. ) -( IF true, skip following (fbr). Also, push br cell ref H, ) -( to PS ) +( Hello, hello, krkrkrkr... do you hear me? + Ah, voice at last! Some lines above need comments + BTW: Forth lines limited to 64 cols because of default + input buffer size in Collapse OS + NOT: a bit convulted because we don't have IF yet + RECURSE: RS TOS is for RECURSE itself, then we have to dig + one more level to get to RECURSE's parent's caller. + IF true, skip following (fbr). Also, push br cell ref H, + to PS ) : IF ['] SKIP? , ['] (fbr) , H 1 ALLOT ; IMMEDIATE -( Subtract TOS from H to get offset to write to IF or ELSE's ) -( br cell ) +( Subtract TOS from H to get offset to write to IF or ELSE's + br cell ) : THEN DUP H -^ SWAP C! ; IMMEDIATE ( write (fbr) addr, allot, then same as THEN ) : ELSE ['] (fbr) , 1 ALLOT DUP H -^ SWAP C! H 1 - ; IMMEDIATE diff --git a/apps/forth/dict.asm b/apps/forth/dict.asm index 0eabf72..febab0d 100644 --- a/apps/forth/dict.asm +++ b/apps/forth/dict.asm @@ -468,10 +468,18 @@ CURRENT_: .dw sysvarWord .dw CURRENT + .db "IN>" + .fill 4 + .dw CURRENT_ + .db 0 +INP: + .dw sysvarWord + .dw INPUTPOS + ; ( n -- ) .db "." .fill 6 - .dw CURRENT_ + .dw INP .db 0 DOT: .dw nativeWord diff --git a/apps/forth/dictionary.txt b/apps/forth/dictionary.txt index 6213fd0..9ad8d51 100644 --- a/apps/forth/dictionary.txt +++ b/apps/forth/dictionary.txt @@ -112,10 +112,21 @@ LIT@ x -- a Read following LIT and push its addr to a SCMP a1 a2 -- n Compare strings a1 and a2. See CMP *** I/O *** + +A little word about inputs. There are two kind of inputs: direct and buffered. +As a general rule, we read line in a buffer, then feed words in it to the +interpreter. That's what "WORD" does. If it's at the End Of Line, it blocks and +wait until another line is entered. + +KEY input, however, is direct. Regardless of the input buffer's state, KEY will +return the next typed key. + . n -- Print n in its decimal form -EMIT c -- Spit char c to stdout -KEY -- c Get char c from stdin +EMIT c -- Spit char c to output stream +IN> -- a Address of variable containing current pos in input + buffer. +KEY -- c Get char c from direct input PC! c a -- Spit c to port a PC@ a -- c Fetch c from port a -WORD -- a Read one word from stdin and push its addr +WORD -- a Read one word from buffered input and push its addr diff --git a/apps/forth/main.asm b/apps/forth/main.asm index 468c42f..bb5b934 100644 --- a/apps/forth/main.asm +++ b/apps/forth/main.asm @@ -102,13 +102,15 @@ forthMain: ld (CURRENT), hl ld hl, HERE_INITIAL ld (HERE), hl + ; Set (INPUTPOS) to somewhere where there's a NULL so we consider + ; ourselves EOL. + ld (INPUTPOS), hl + xor a + ld (hl), a forthRdLine: ld hl, msgOk call printstr forthRdLineNoOk: - call printcrlf - call stdioReadLine - ld (INPUTPOS), hl ; Setup return stack. After INTERPRET, we run forthExecLine ld ix, RS_ADDR ; We're about to compile the line and possibly execute IMMEDIATE words. @@ -146,24 +148,18 @@ forthExecLine: ; (we don't have RECURSE here. Calling interpret makes us needlessly use our ; RS stack, but it can take it, can't it? ) -; WORD DUP C@ (to check if null) SKIP? (skip if not null) EXIT COMPILE INTERPRET +; WORD COMPILE IN> @ C@ (to check if null) SKIP? (skip if not null) EXIT INTERPRET .db 0b10 ; UNWORD INTERPRET: .dw compiledWord .dw WORD - .dw DUP + .dw COMPILE + .dw INP + .dw FETCH .dw CFETCH .dw CSKIP - .dw .stop - .dw COMPILE - .dw INTERPRET .dw EXIT - -.stop: - .dw compiledWord - .dw DROP - .dw R2P - .dw DROP + .dw INTERPRET .dw EXIT msgOk: diff --git a/apps/forth/util.asm b/apps/forth/util.asm index b2b1a7d..b238cab 100644 --- a/apps/forth/util.asm +++ b/apps/forth/util.asm @@ -7,7 +7,8 @@ pad: ; Read word from (INPUTPOS) and return, in HL, a null-terminated word. ; Advance (INPUTPOS) to the character following the whitespace ending the ; word. -; Z set of word was read, unset if end of line. +; When we're at EOL, we call fetchline directly, so this call always returns +; a word. readword: ld hl, (INPUTPOS) ; skip leading whitespace @@ -16,6 +17,7 @@ readword: inc hl ld a, (hl) or a + ; When at EOL, fetch a new line directly jr z, .empty cp ' '+1 jr c, .loop1 @@ -39,9 +41,8 @@ readword: pop hl ; <-- lvl 1. our result ret ; Z set from XOR A .empty: - ld (hl), a - inc a ; unset Z - ret + call fetchline + jr readword ; Sets Z if (HL) == E and (HL+1) == D HLPointsDE: @@ -336,3 +337,10 @@ DEinHL: ld (hl), d inc hl ret + +fetchline: + call printcrlf + call stdioReadLine + ld (INPUTPOS), hl + ret +