Files
Interpreter/Doc/isa/指令与映像.md
T
Admin 6df3465b37 登记第三方子模块并预留 TUI 观察接口。
- third_party:tomlplusplus(v3.4.0)+ FTXUI(main@9221a3b)git 子模块
- vm/扫描周期.md:可观察性接口(v1 预留)——step/snapshot、只读轮询、快照 POD 化
- executor/执行器入口.md:TUI 观察界面(最后做)——TUI 属执行器,输出层可替换
- 初步计划 12.11 补 TUI 归属;C++11 残留引用同步为 C++20
2026-08-20 22:51:05 +08:00

239 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 指令与映像
isa 模块规范:指令集 + 映像 + 定宽类型。不是编译器,不把 `.st` 编成映像。
`compiler` 按本文件写出字节,`vm` 只认这些字节。两边不互链。
全工程定案见 [`初步计划.md`](../初步计划.md)。**类型、指令编码、映像布局以本文为准**,不要另写一份互相打架的表。头文件实现本文,不另当规范。
```text
STCompiler → compiler → isa
BytecodeExecutor → vm → isa
```
CMake 目标:`isa``STATIC`),无依赖。公开头:`isa/include/isa/`
---
## 做 / 不做
**做**
- 操作码与 32 位指令布局
- 映像头与段(函数字节码、全局 / FB 槽、工程哈希)
- `BOOL` / `INT` / `TIME` 宽度与溢出规则
- 编解码、工程哈希、一条指令的文本转储(反汇编,给 STCompiler `--disasm` 与测试打印)
**不做**
- toml 字段、关键字表、符号表、AST
- 扫描周期循环、I/O 采样
- 短路语义(`AND`/`OR` 指令只组合已算好的值;短路由 `compiler` 编成跳转)
指令表可作为内部 IR 的文本形态,不必当主语言。
---
## 特性
| 项 | 第一版 |
|---|---|
| `BOOL` | `uint8_t`,只允许 0/1 |
| `INT` | `int16_t`IEC `INT` |
| `TIME` | `int64_t` 毫秒(有符号);与 `INT` 不混用 |
| 整数溢出 | **饱和**(夹到 `INT` 最小/最大) |
| 字节序 | 小端 |
| 指令宽度 | 32 bit,一种主布局 |
| 寄存器 | 下标 0..255;每函数实际个数写在函数头 `nregs` |
| 跳转 | 相对 offset,单位是**指令条数**,不是字节 |
| `CALL` | 立即数 `fn_id`u16),无函数指针 |
| 映像魔数 | `STSC`,版本 `1` |
| 工程哈希 | 源文件路径排序后对内容做 FNV-1a 64(非密码学) |
| I/O 绑定 | **不进映像**;编译期把 `var` 收成槽号,channel/bit 由 STCompiler 写进 sidecar`.runtime.toml`),BytecodeExecutor 读 sidecar 采样 |
| 不做 | `REAL`、隐式宽化 |
---
## 类型
C++20 定宽:
- `BOOL``uint8_t`0/1
- `INT``int16_t`;加减乘除溢出饱和
- `TIME``int64_t` 毫秒,有符号
`compiler``vm` 都按同一规则,不再另选溢出策略。
`TIME``int64_t` 的理由(冻结,不再反复问):
- 有符号:`TOF` 剩余时间、两个时间点相减会出负值,免去无符号边界处理
- 64 位:为将来 `DATE_AND_TIME`(毫秒时间戳)与 `TON` / `TOF` / `TP` 的 64 位定时器预留;毫秒量程约 2.9 亿年,扫描周期场景永不溢出
- 代价:槽位 8 字节对齐。**槽位对齐规则**:`BOOL` 1 字节、`INT` 2 字节、`TIME` 8 字节;连续排列按类型对齐,需要时插 padding,偏移以本规则为准。
---
## 指令
定宽 32 bit
```text
[ op:8 | rd:8 | a:8 | b:8 ]
```
- 三寄存器:`a`/`b``ra`/`rb``MOVE`/`NOT` 只用 `rd`+`a`
- `LOADK``a|b``const_id`u16
- `JMP` / `JT` / `JF``a|b` 为有符号相对 offset(指令条数)
- `CALL``a|b``fn_id`u16
操作码一次列全(`CMP_xx` 冻下面六种):
```text
MOVE rd, rs
LOADK rd, const_id
NOT rd, rs
AND/OR rd, ra, rb (* 已算好的值;短路用跳转 *)
ADD/SUB/MUL/DIV rd, ra, rb
CMP_EQ/NE/LT/LE/GT/GE rd, ra, rb
JMP offset
JT/JF r, offset
LOAD_I / STORE_Q / LOAD_M / STORE_M
LOAD_GLOBAL / STORE_GLOBAL
CAL_TON / CAL_TOF / CAL_CTU
CALL fn_id
RET
```
局部 / `VAR` = 固定寄存器;表达式临时值往后编号;`nregs` 写进函数头。
`I`/`Q`/`M` 走映像指令,不是通用寄存器。
FB 实例固定布局,字段偏移编译期算死。
### 反汇编格式(disasm
`disasm` 输出一条指令的一行文本,格式冻结:
| 形态 | 格式 | 示例 |
|---|---|---|
| 二寄存器 | `MOVE rd, rs` / `NOT rd, rs` | `MOVE r1, r2` |
| 三寄存器 | `OP rd, ra, rb`(逻辑 / 算术 / 比较) | `ADD r1, r2, r3` |
| 常量 | `LOADK rd, imm` | `LOADK r0, 3` |
| 无条件跳转 | `JMP off` | `JMP +4` |
| 条件跳转 | `JT rd, off` / `JF rd, off` | `JT r0, -1` |
| I/Q/M/全局 | `LOAD_I rd, slot` 等;STORE 同形 `STORE_Q rs, slot` | `LOAD_I r0, 2` |
| FB 内置 | `CAL_TON instance` 等 | `CAL_TON 0` |
| 调用 | `CALL fn_id` | `CALL 1` |
| 返回 | `RET` | `RET` |
| 未知操作码 | `??? 0x<hex>` | — |
跳转偏移带符号、恒显正负号(`%+d`);寄存器一律 `r<n>`;常量/槽号/`fn_id` 无符号十进制。
---
## 映像
魔数 `STSC`,版本 `1`。全部整数小端。文件 = 头 + 五个段,段序:常量表 → 函数表 → 字节码 → FB 布局 → 数据。偏移记在头里,段必须连续、偏移单调不减,末段终点不超过文件长度。
### 头(72 字节)
| 偏移 | 宽 | 字段 |
|---|---|---|
| 0 | 4 | 魔数 `STSC` |
| 4 | 4 | 版本 `1` |
| 8 | 4 | `cycle_limit` |
| 12 | 4 | `dt_ms` |
| 16 | 8 | 工程哈希(FNV-1a 64 |
| 24 | 4 | 入口 `entry_fn_id` |
| 28 | 4 | 全局槽数 `n_globals` |
| 32 | 4 | I 槽数 `n_i` |
| 36 | 4 | Q 槽数 `n_q` |
| 40 | 4 | M 槽数 `n_m` |
| 44 | 4 | 常量数 `n_consts` |
| 48 | 4 | 函数数 `n_funcs` |
| 52 | 4 | `offset_const` |
| 56 | 4 | `offset_funcs` |
| 60 | 4 | `offset_code` |
| 64 | 4 | `offset_fb` |
| 68 | 4 | `offset_data` |
### 常量表一行(12 字节)
`[tag:u32][value:u64]``tag`0=BOOL、1=INT、2=TIME。`value` 按 tag 解释,未用高位为 0。
### 函数表一行(12 字节)
`[nregs:u32][code_offset:u32][code_len:u32]`;行下标即 `fn_id``code_offset` 相对字节码段起点,`code_len` 为指令条数。
### 字节码段
指令按 `u32` 连续排;`CALL``fn_id` 是函数表行下标。
### FB 布局段
第一版为空;每类型一行 `[field_count:u32][field_count × (tag:u32, offset:u32)]`offset 相对实例基址,12.8 落地 codegen 时冻精确表。
### 数据段
槽初值按序排:全局(`n_globals`)→ I → Q → M。槽宽与对齐:`BOOL` 1 字节 / `INT` 2 字节 / `TIME` 8 字节,每个槽起点对齐到自身宽度(需要时插 padding)。
工程哈希:收录源文件路径排序后对内容做 **FNV-1a 64**,只保证回放槽位一致,不是密码学哈希。FNV-1a 64basis `0xcbf29ce484222325`prime `0x100000001b3`
`[[io.input]]` / `[[io.output]]` 不进映像。
---
## 映像文件(`.stb`)与 sidecar
编译产物是单文件 `<name>.stb`:映像头 + 各段按本文布局连续排,魔数 `STSC`、版本 `1`、小端。`STCompiler` 写,`BytecodeExecutor` 读。
- `dt_ms` / `cycle_limit` 在映像头,执行器只认映像,不重复配置。
- I/O 绑定不进映像:`STCompiler` 另写 sidecar `<name>.runtime.toml``BytecodeExecutor` 读 sidecar 做采样与写回,不创造变量。
sidecar 格式(TOML 子集,冻结):
```toml
[[io.input]] # 或 [[io.output]]
var = "I0_0" # 已在 GVL 声明的名字
slot = 1 # 全局槽号(编译期已解析)
channel = 0
bit = 0
```
每行一个绑定;`var` 顺序不要求与映像槽序一致(`slot` 为准)。
---
## 目录与 CMake
```text
isa/
CMakeLists.txt
README.md # 代码目录入口,指向本文
include/isa/
Types.h # 宽度、饱和、槽值表示
Op.h # 操作码枚举 + 助记符
Instr.h # 32-bit 打包/拆字段(inline
Image.h # 头、段、函数表/常量表/FB 布局
Encode.h # 编解码、哈希、反汇编一行
src/
Encode.cpp
Image.cpp
```
```cmake
add_library(isa STATIC src/Encode.cpp src/Image.cpp)
target_include_directories(isa PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
```
`compiler` / `vm``target_link_libraries(... PUBLIC isa)`
---
## 实现顺序(对齐初步计划 12.0~12.1)
1. `Types.h`:三类型 + `sat_add` 等饱和函数(inline 即可)
2. `Op.h` + `Instr.h`:枚举与 32-bit 编解码
3. `Encode.cpp``encode` / `decode` / `disasm` 一条指令
4. `Image.h` + `Image.cpp`:头布局、FNV-1a、读写缓冲(先能写空映像 + 一条 `RET`
5. 测试随后在 `tests/` 加编解码往返;本阶段以头文件和库能链为准
不在本模块做:VM `switch`、toml、关键字、符号表。