forth/tty/demo_tty.fth · 11236 字节 · 下载原文
\ demo_tty.fth —— Forth 第二层:**彩色控制台**(tty)
\ demo_tty.fth —— Forth layer two: **color console** (tty)
\
\ Forth 这边**没有 conio / crt / ANSI 辅助词**(`Lib/forth/gfx.fth` 与
\ On the Forth side there are **no conio / crt / ANSI helper words** —— `Lib/forth/gfx.fth` and
\ `Lib/forth/sys.fth` 里那些词体是 `asm("SYSCALL 80")` 一类写法,而**本前端不支持
\ `Lib/forth/sys.fth` have word bodies written as `asm("SYSCALL 80")` and the like, while **this frontend does not support
\ `asm()`** —— 用它会报「未定义的函数 'word_asm'」(**2026-10-04 复验仍成立**),所以那两个文件在这个前端下编不过、
\ the bare `asm` keyword** —— using it reports "undefined function 'word_asm'" (**still true as of 2026-10-04**), so those two files do not compile on this frontend,
\ 也不是给它们用的)。所以这一层**直接发 ANSI 转义序列**,一个字节一个字节地用
\ these two files are not meant for this frontend anyway. So this layer **emits ANSI escape sequences directly**, byte by byte with
\ `EMIT` 送出去 —— 与 `Examples/c/ansi_colors.c` 同一个做法、同一套效果。
\ `EMIT` —— the same approach and the same effect as `Examples/c/ansi_colors.c`.
\
\ 跑法(桌面 vmlcli):
\ dotnet scripts/vmlcli/bin/Release/net10.0/vmlcli.dll Examples/forth/demo_tty.fth
\
\ 画面(从上到下):
\ The screen (top to bottom):
\ ① 清屏(ESC[2J + ESC[H)+ 复位属性
\ ① clear the screen (ESC[2J + ESC[H) + reset the attributes
\ ② 8 个暗色前景(色号 0-7),背景 = 7 浅灰
\ ② 8 dark foreground colors (color numbers 0-7), background = 7 light gray
\ ③ 8 个亮色前景(色号 8-15),背景 = 0 黑
\ ③ 8 bright foreground colors (color numbers 8-15), background = 0 black
\ ④ 光标定位:同一行先写右半段、再回到左端写左半段 —— 证明是**定位**不是顺序输出
\ ④ cursor positioning: on the same row write the right half first, then go back to the left end and write the left half —— proving it is **positioning**, not sequential output
\ ⑤ 复位成白字黑底,正常结束
\ ⑤ reset to white on black, finishing normally
\
\ ═══════════════════════════════════════════════════════════════════════
\ ⚠ 这份文件为什么写得这么"素"(本前端的四条硬限制,全实测过)
\ ⚠ Why this file is written so "plainly" (four hard limits of this frontend, all measured in practice)
\
\ ⚠ **① 与 ② 都已在 2026-10-01 修好**(成因是同一个:用户词调用把返回地址 `R15`
\ ⚠ **① and ② were both fixed on 2026-10-01** (one root cause: the user-word call pushed the
\ 压在**数据栈上、插在实参中间**;现在改成**内存返回栈**。
\ return address `R15` onto the **data stack, between the arguments**; it now uses an **in-memory return stack**.
\ 本节保留下来是因为**这份例程的写法就是照它们写的**(辅助词全零参)——
\ This section is kept because **this example is written around them** (every helper takes zero parameters)--
\ 新写的代码不必再受这两条限制,见 `Examples/forth/demo_ui.fth`。)
\ new code need not obey them; see `Examples/forth/demo_ui.fth`.)
\
\ ① **只有零参 / 一参的词才可靠**。
\ ① **Only words with zero or one parameter are reliable**.
\ `: ADD2 ( a b -- c ) + ; 5 7 ADD2 .` 打出 **7**(应 12)
\ `: ADD2 ( a b -- c ) + ; 5 7 ADD2 .` prints **7**(should be 12)
\ ⇒ **两个以上参数会串位**。所以每个辅助词都是零参的,颜色、行列全部
\ ⇒ **more than two parameters go out of step**. So every helper word takes zero parameters, and colors, rows and columns are all
\ **以字面量形式**写在调用点。
\ **written as literals** at the call site.
\
\ ② **词体里禁止"带参数 + 再调别的词"**。(⚠ 2026-10-01 已修:同一条成因,见上。)
\ ② **word bodies must not "take a parameter and then call another word"**. (⚠ fixed on 2026-10-01, same root cause — see above.)
\ : TEN ( n -- t ) 10 / ;
\ : SGR ( n -- ) CSI DUP TEN ... 109 EMIT ; 33 SGR ." B" CR
\ 结果只发出 `ESC[33m`,**其后整个顶层程序被静默丢弃**(`B` 与 `CR` 都不见了)。
\ the result emits only `ESC[33m`, and **the whole top-level program after it is silently dropped** (both `B` and `CR` are gone).
\ 同一份程序把 `TEN` 去掉、改成词体内联算术,`B`/`CR` 虽然回来了,
\ In the same program, removing `TEN` and inlining the arithmetic in the word body brings `B`/`CR` back,
\ 但数字算成了乱码。⇒ 词体里只留"EMIT 若干字面量"这一种形态。
\ but the numbers come out garbled. ⇒ keep only the "EMIT a few literals" form inside a word body.
\
\ ③ ~~**取模 `MOD` 是坏的:它编成了加法**。~~
\ ③ ~~**Modulus `MOD` is broken: it compiles into addition**.~~
\ ✅ **已修好(2026-10-04 复验)**:`10 3 MOD` ⇒ **1**(旧记 13)、`100 7 MOD` ⇒ **2**(旧记 107)。
\ ✅ **Fixed (re-verified 2026-10-04)**: `10 3 MOD` => **1** (previously 13), `100 7 MOD` => **2** (previously 107).
\ 当年根因:生成器把 `TokenType.MOD` 映射成字符串 "MOD",而基类那张表只认 "%" ⇒ 落到 `_ => OpCode.ADD`。
\ Root cause at the time: the generator mapped `TokenType.MOD` to the string "MOD" while the base class's table
\ only recognized "%" ⇒ it fell through to `_ => OpCode.ADD`.
\ ⇒ 本文件仍照旧靠**字面量拼**两位数字(写法无害);新写的代码不必再绕,直接用 `MOD` 即可。
\ ⇒ This file still **assembles two-digit numbers from literals** (harmless); new code need not work around it — just use `MOD`.
\
\ ④ 词名避开已有库符号(`H` / `M` / `CSI` 这些都先查过 `Lib/forth/*.vml` 没有重名)。
\ ④ Word names avoid existing library symbols (`H` / `M` / `CSI` and the like were all checked against `Lib/forth/*.vml` for duplicates first).
\
\ 另外:`." …"` 里的中文没问题(走 stdout 那条会解码 UTF-8 的路),可以放心用。
\ Also: Chinese inside `." …"` is fine (it takes the stdout path that decodes UTF-8), so feel free to use it.
\ ═══════════════════════════════════════════════════════════════════════
\ ── ANSI 骨架辅助词(全部零参,词体里只有字面量 + EMIT)────────────
\ ── ANSI skeleton helper words (all zero-parameter; the body holds only literals + EMIT) ────────────
: ESC ( -- ) 27 EMIT ;
: CSI ( -- ) ESC 91 EMIT ;
: SEMI ( -- ) 59 EMIT ; \ ';'
: H ( -- ) 72 EMIT ; \ 'H' 光标定位结尾
\ 'H': the character that ends a cursor-positioning sequence
: SGRM ( -- ) 109 EMIT ; \ 'm' 颜色序列结尾
\ 'm': the character that ends a color sequence
: RESET ( -- ) CSI 48 EMIT SGRM ; \ ESC[0m
: CLS ( -- ) CSI 50 EMIT 74 EMIT CSI H ; \ ESC[2J ESC[H
\ ── ① 清屏 ──────────────────────────────────────────────────────
\ ── ① clear the screen ──────────────────────────────────────────────────
\ 界面语言:0 = 中文 / 1 = 英文(ui_scr_get_language 是 syscall,开局查一次存进 LANG)
\ UI language: 0 = Chinese / 1 = English (ui_scr_get_language is a syscall: query it once at startup into LANG)
VARIABLE LANG
ui_scr_get_language LANG !
RESET CLS
\ ── 标题:亮白字(97) + 蓝底(44):ESC[97;44m ─────────────────────
\ ── Title: bright white text (97) + blue background (44): ESC[97;44m ─────────────────────
CSI 57 EMIT 55 EMIT SEMI 52 EMIT 52 EMIT SGRM
." === Forth color console demo === (ANSI 97;44 = bright white on blue)" CR
\ ── ② 暗色 0-7(行 10-17,第 3 列;色 30-37,背景 47)────────────
\ ── ② dark colors 0-7 (rows 10-17, column 3; colors 30-37, background 47) ────────────
\ 行号 = 1<0+I>(I 走 0..7)、色号 = 3<0+I>
\ row number = 1<0+I> (I runs 0..7), color number = 3<0+I>
8 0 DO
CSI 49 EMIT 48 I + EMIT SEMI 51 EMIT H \ ESC[1<0+I>;3H
CSI 51 EMIT 48 I + EMIT SEMI 52 EMIT 55 EMIT SGRM \ ESC[3<0+I>;47m
LANG @ 0= IF ." 前景色 暗色(0-7),背景 = 7 浅灰" ELSE ." foreground dark (0-7), background = 7 light gray" THEN CR
LOOP
\ ── ③ 亮色 8-15(行 20-27,第 3 列;色 90-97,背景 40)──────────
\ ── ③ bright colors 8-15 (rows 20-27, column 3; colors 90-97, background 40) ──────────
\ 行号 = 2<0+I>(避开上面 10-17 那几行)、色号 = 9<0+I>
\ row number = 2<0+I> (avoiding the rows 10-17 above), color number = 9<0+I>
8 0 DO
CSI 50 EMIT 48 I + EMIT SEMI 51 EMIT H \ ESC[2<0+I>;3H
CSI 57 EMIT 48 I + EMIT SEMI 52 EMIT 48 EMIT SGRM \ ESC[9<0+I>;40m
LANG @ 0= IF ." 前景色 亮色(8-15),背景 = 0 黑" ELSE ." foreground bright (8-15), background = 0 black" THEN CR
LOOP
\ ── ④ 光标定位:第 29 行第 34 列先写,再回第 29 行第 1 列写 ──────
\ ── ④ cursor positioning: write at row 29 column 34 first, then back at row 29 column 1 ──────
CSI 50 EMIT 57 EMIT SEMI 51 EMIT 52 EMIT H \ ESC[29;34H
CSI 57 EMIT 51 EMIT SEMI 52 EMIT 49 EMIT SGRM \ ESC[93;41m 亮黄 / 红底
\ ESC[93;41m bright yellow on red background
LANG @ 0= IF ." <- 先写的(第 34 列)" ELSE ." <- written first (column 34)" THEN CR
CSI 50 EMIT 57 EMIT SEMI 49 EMIT H \ ESC[29;1H
CSI 57 EMIT 54 EMIT SEMI 52 EMIT 48 EMIT SGRM \ ESC[96;40m 亮青 / 黑底
\ ESC[96;40m bright cyan on black background
LANG @ 0= IF ." 后写的(第 1 列)-> " ELSE ." written second (column 1) -> " THEN CR
\ ── ⑤ 收尾:第 31 行第 1 列,复位成白字黑底(37 / 40)──────────
\ ── ⑤ wrap-up: at row 31 column 1, reset to white on black (37 / 40) ──────────
CSI 51 EMIT 49 EMIT SEMI 49 EMIT H \ ESC[31;1H
CSI 51 EMIT 55 EMIT SEMI 52 EMIT 48 EMIT SGRM \ ESC[37;40m
LANG @ 0= IF ." === done(已复位为白字黑底)===" ELSE ." === done (reset to white on black) ===" THEN CR