ladder/tty/demo_tty.ld · 11787 字节 · 下载原文
(* demo_tty.ld —— Ladder 第二层:**彩色控制台**(tty)
demo_tty.ld -- Ladder layer two: **color console** (tty)
Ladder 是 IEC 61131-3 的**工业控制**语言(PLC 梯形图),它**没有** conio / crt /
Ladder is IEC 61131-3's **industrial control** language (PLC ladder diagram), and it **has no** conio / crt /
ANSI 辅助库:`Lib/ladder/gfx.ld` 里 `VGA_*` 那几个块用的是 DOS 时代的 syscall
ANSI helper library: the `VGA_*` blocks in `Lib/ladder/gfx.ld` use DOS-era syscall
号(80/81/82),`GFX_*` 那几个块**整个块体就是一个 `RET`**(空实现)。
numbers (80/81/82), and each `GFX_*` block is **just a `RET` for its whole body** (empty implementation).
而更要紧的是 —— 那些块名是 `[GFX_LINE]` 这种**裸标签**,前端调函数时会编成
More importantly, those block names are **bare labels** like `[GFX_LINE]`, while the frontend compiles a call to
`CALL LADDER_<名字>`,两边**对不上**,所以从 Ladder 里**根本调不到任何库函数**
`CALL LADDER_<name>`; the two sides **do not match**, so from Ladder **no library function can be called at all**
(实测:`R := ui_win_open("x", 320, 240);` ⇒ 「未定义的函数 'LADDER_UI_WIN_OPEN'」)。
(measured: `R := ui_win_open("x", 320, 240);` => "undefined function 'LADDER_UI_WIN_OPEN'").
于是这一层只能**直接发 ANSI 转义序列**,一个字节一个字节地送 ——
So this layer can only **emit ANSI escape sequences directly**, byte by byte --
与 `Examples/c/ansi_colors.c` 同一个做法、同一套效果
the same approach and the same effect as `Examples/c/ansi_colors.c`
(手机端「命令行」页由 `UI/Shared/AnsiMarkup.cs` 翻成彩色富文本)。
(on phone the "command line" page renders it as colored rich text via `UI/Shared/AnsiMarkup.cs`).
跑法(桌面 vmlcli):
dotnet scripts/vmlcli/bin/Release/net10.0/vmlcli.dll Examples/ladder/demo_tty.ld
画面(从上到下):
What is drawn (top to bottom):
① 清屏(ESC[2J + ESC[H)+ 复位属性
1. Clear screen (ESC[2J + ESC[H) + reset attributes
② 8 个暗色前景(色号 0-7),背景 = 7 浅灰
2. 8 dim foregrounds (colors 0-7), background = 7 light gray
③ 8 个亮色前景(色号 8-15),背景 = 0 黑
3. 8 bright foregrounds (colors 8-15), background = 0 black
④ 光标定位:同一行先写右半段、再回到左端写左半段
4. Cursor positioning: write the right half of a line first, then go back to the left end for the left half
⑤ 复位成白字黑底,正常结束
5. Reset to white on black and finish normally
◆ 写法:**ESC 用 `PRINT_CHAR 27` 发,序列的其余部分用 `PRINT_STR` 写**
* How to write it: **emit ESC with `PRINT_CHAR 27` and the rest of the sequence with `PRINT_STR`**
(两行一条序列,字符串里全是可见字符,不必往源码里塞控制字节):
PRINT_CHAR 27; (* ESC *)
PRINT_STR "[31;47m"; (* 前景 31 / 背景 47
-- foreground 31 / background 47 -- *)
另一种写法是把 ESC 直接**嵌进字符串字面量**(`PRINT_STR "<0x1B>[31m"`)——
Another way is to **embed ESC directly in the string literal** (`PRINT_STR "<0x1B>[31m"`) --
实测也能跑通,但源码里会出现一个不可见字节,编辑器里极易看漏或改坏,所以不采用。
measured to work, but it puts an invisible byte in the source that is easy to miss or corrupt in an editor, so it is not used.
◆ ~~⚠ **这一层写不出循环**~~ ✅ **已修好(2026-10-04 复验)** —— `WHILE … DO … END_WHILE` 与
~~(!) **this layer could not write a loop**~~ (fixed; re-checked 2026-10-04) -- `WHILE ... DO ... END_WHILE` and
`FOR i := a TO b DO … END_FOR` 都能用(实测 `WHILE I < 5 DO S := S + I; … END_WHILE` ⇒ S = 10、
`FOR i := a TO b DO ... END_FOR` both work (measured `WHILE I < 5 DO S := S + I; ... END_WHILE` => S = 10,
`FOR` 求和 ⇒ 15)。下面仍写成 16 条手工展开的彩色行(写法无害,固定行数本就无需循环)。
a `FOR` sum => 15). Below it is still written as 16 hand-unrolled colored lines (harmless; a fixed line count needs no loop).
本前端**仍有**一条缺陷(实测的最小复现):
The frontend **still has** one defect (the smallest reproduction, measured):
① ⚠ **标签 `LBL_x:` 会吃掉它后面的语句**(2026-10-04 复验仍成立):
1. (!) **A label `LBL_x:` swallows the statement after it** (re-checked 2026-10-04: still true):
S := 7; PRINT_INT S; ⇒ 打出 7 ✅
S := 7; PRINT_INT S; => prints 7 OK
S := 7; LBL_A: PRINT_INT S; ⇒ 什么都不打 ❌
S := 7; LBL_A: PRINT_INT S; => prints nothing FAIL
~~② **`IF <条件> THEN … END_IF` 的块体不执行**~~ ✅ **已修好(2026-10-04 复验)**:
~~2. **the body of `IF <cond> THEN ... END_IF` did not execute**~~ (fixed; re-checked 2026-10-04):
`IF S = 1 THEN … END_IF`(S:=1)与 `IF B THEN … END_IF`(B:=TRUE)的块体都正常执行。
the bodies of `IF S = 1 THEN ... END_IF` (S:=1) and `IF B THEN ... END_IF` (B:=TRUE) both run fine.
*)
(* 本程序一个变量都不用,所以 VAR 段也省了(实测 `PROGRAM … BEGIN … END_PROGRAM`
This program uses no variables, so the VAR section is omitted too (measured: `PROGRAM ... BEGIN ... END_PROGRAM`
不带 VAR 完全合法)。
with no VAR is perfectly legal). *)
PROGRAM DemoTty
VAR
(* 界面语言:0 = 中文 / 1 = 英文(宿主 syscall,开局查一次)。
UI language: 0 = Chinese / 1 = English (host syscall, queried once at startup). *)
LANG : INT;
END_VAR
BEGIN
LANG := ui_scr_get_language();
(* ① 清屏 + 复位属性
-- 1. Clear screen + reset attributes -- *)
PRINT_CHAR 27;
PRINT_STR "[0m";
PRINT_CHAR 27;
PRINT_STR "[2J";
PRINT_CHAR 27;
PRINT_STR "[H";
(* 标题:亮白字(97) + 蓝底(44)
-- Title: bright white text (97) + blue background (44) -- *)
PRINT_CHAR 27;
PRINT_STR "[97;44m";
PRINT_STR "=== Ladder color console demo === (ANSI 97;44 = bright white on blue)";
PRINT_CHAR 10;
(* ② 暗色 0-7(前景 30-37 / 背景 47 浅灰),定位到第 3-10 行第 3 列
-- 2. Dim colors 0-7 (foreground 30-37 / background 47 light gray), positioned at rows 3-10, column 3 -- *)
PRINT_CHAR 27;
PRINT_STR "[3;3H";
PRINT_CHAR 27;
PRINT_STR "[30;47m";
IF LANG = 0 THEN PRINT_STR "前景色 0 暗色,背景 = 7 浅灰" ELSE PRINT_STR "color 0 dim, background = 7 light gray" END_IF;
PRINT_CHAR 10;
PRINT_CHAR 27;
PRINT_STR "[4;3H";
PRINT_CHAR 27;
PRINT_STR "[31;47m";
IF LANG = 0 THEN PRINT_STR "前景色 1 暗色,背景 = 7 浅灰" ELSE PRINT_STR "color 1 dim, background = 7 light gray" END_IF;
PRINT_CHAR 10;
PRINT_CHAR 27;
PRINT_STR "[5;3H";
PRINT_CHAR 27;
PRINT_STR "[32;47m";
IF LANG = 0 THEN PRINT_STR "前景色 2 暗色,背景 = 7 浅灰" ELSE PRINT_STR "color 2 dim, background = 7 light gray" END_IF;
PRINT_CHAR 10;
PRINT_CHAR 27;
PRINT_STR "[6;3H";
PRINT_CHAR 27;
PRINT_STR "[33;47m";
IF LANG = 0 THEN PRINT_STR "前景色 3 暗色,背景 = 7 浅灰" ELSE PRINT_STR "color 3 dim, background = 7 light gray" END_IF;
PRINT_CHAR 10;
PRINT_CHAR 27;
PRINT_STR "[7;3H";
PRINT_CHAR 27;
PRINT_STR "[34;47m";
IF LANG = 0 THEN PRINT_STR "前景色 4 暗色,背景 = 7 浅灰" ELSE PRINT_STR "color 4 dim, background = 7 light gray" END_IF;
PRINT_CHAR 10;
PRINT_CHAR 27;
PRINT_STR "[8;3H";
PRINT_CHAR 27;
PRINT_STR "[35;47m";
IF LANG = 0 THEN PRINT_STR "前景色 5 暗色,背景 = 7 浅灰" ELSE PRINT_STR "color 5 dim, background = 7 light gray" END_IF;
PRINT_CHAR 10;
PRINT_CHAR 27;
PRINT_STR "[9;3H";
PRINT_CHAR 27;
PRINT_STR "[36;47m";
IF LANG = 0 THEN PRINT_STR "前景色 6 暗色,背景 = 7 浅灰" ELSE PRINT_STR "color 6 dim, background = 7 light gray" END_IF;
PRINT_CHAR 10;
PRINT_CHAR 27;
PRINT_STR "[10;3H";
PRINT_CHAR 27;
PRINT_STR "[37;47m";
IF LANG = 0 THEN PRINT_STR "前景色 7 暗色,背景 = 7 浅灰" ELSE PRINT_STR "color 7 dim, background = 7 light gray" END_IF;
PRINT_CHAR 10;
(* ③ 亮色 8-15(前景 90-97 / 背景 40 黑),定位到第 12-19 行第 3 列
-- 3. Bright colors 8-15 (foreground 90-97 / background 40 black), positioned at rows 12-19, column 3 -- *)
PRINT_CHAR 27;
PRINT_STR "[12;3H";
PRINT_CHAR 27;
PRINT_STR "[90;40m";
IF LANG = 0 THEN PRINT_STR "前景色 8 亮色,背景 = 0 黑" ELSE PRINT_STR "color 8 bright, background = 0 black" END_IF;
PRINT_CHAR 10;
PRINT_CHAR 27;
PRINT_STR "[13;3H";
PRINT_CHAR 27;
PRINT_STR "[91;40m";
IF LANG = 0 THEN PRINT_STR "前景色 9 亮色,背景 = 0 黑" ELSE PRINT_STR "color 9 bright, background = 0 black" END_IF;
PRINT_CHAR 10;
PRINT_CHAR 27;
PRINT_STR "[14;3H";
PRINT_CHAR 27;
PRINT_STR "[92;40m";
IF LANG = 0 THEN PRINT_STR "前景色 10 亮色,背景 = 0 黑" ELSE PRINT_STR "color 10 bright, background = 0 black" END_IF;
PRINT_CHAR 10;
PRINT_CHAR 27;
PRINT_STR "[15;3H";
PRINT_CHAR 27;
PRINT_STR "[93;40m";
IF LANG = 0 THEN PRINT_STR "前景色 11 亮色,背景 = 0 黑" ELSE PRINT_STR "color 11 bright, background = 0 black" END_IF;
PRINT_CHAR 10;
PRINT_CHAR 27;
PRINT_STR "[16;3H";
PRINT_CHAR 27;
PRINT_STR "[94;40m";
IF LANG = 0 THEN PRINT_STR "前景色 12 亮色,背景 = 0 黑" ELSE PRINT_STR "color 12 bright, background = 0 black" END_IF;
PRINT_CHAR 10;
PRINT_CHAR 27;
PRINT_STR "[17;3H";
PRINT_CHAR 27;
PRINT_STR "[95;40m";
IF LANG = 0 THEN PRINT_STR "前景色 13 亮色,背景 = 0 黑" ELSE PRINT_STR "color 13 bright, background = 0 black" END_IF;
PRINT_CHAR 10;
PRINT_CHAR 27;
PRINT_STR "[18;3H";
PRINT_CHAR 27;
PRINT_STR "[96;40m";
IF LANG = 0 THEN PRINT_STR "前景色 14 亮色,背景 = 0 黑" ELSE PRINT_STR "color 14 bright, background = 0 black" END_IF;
PRINT_CHAR 10;
PRINT_CHAR 27;
PRINT_STR "[19;3H";
PRINT_CHAR 27;
PRINT_STR "[97;40m";
IF LANG = 0 THEN PRINT_STR "前景色 15 亮色,背景 = 0 黑" ELSE PRINT_STR "color 15 bright, background = 0 black" END_IF;
PRINT_CHAR 10;
(* ④ 光标定位:第 21 行第 34 列先写,再回第 21 行第 1 列写
-- 4. Cursor positioning: write at row 21 column 34 first, then back to row 21 column 1 -- *)
PRINT_CHAR 27;
PRINT_STR "[21;34H";
PRINT_CHAR 27;
PRINT_STR "[93;41m";
IF LANG = 0 THEN PRINT_STR "<- 先写的(第 34 列)" ELSE PRINT_STR "<- written first (column 34)" END_IF;
PRINT_CHAR 10;
PRINT_CHAR 27;
PRINT_STR "[21;1H";
PRINT_CHAR 27;
PRINT_STR "[96;40m";
IF LANG = 0 THEN PRINT_STR "后写的(第 1 列)-> " ELSE PRINT_STR "written second (column 1) -> " END_IF;
PRINT_CHAR 10;
(* ⑤ 收尾:第 23 行第 1 列,复位成白字黑底(37 / 40)
-- 5. Wrap-up: row 23 column 1, reset to white on black (37 / 40) -- *)
PRINT_CHAR 27;
PRINT_STR "[23;1H";
PRINT_CHAR 27;
PRINT_STR "[37;40m";
IF LANG = 0 THEN PRINT_STR "=== done(已复位为白字黑底)===" ELSE PRINT_STR "=== done (reset to white on black) ===" END_IF;
PRINT_CHAR 10;
END_PROGRAM