diff --git a/vm/include/vm/Machine.h b/vm/include/vm/Machine.h index 289b90d..feb4b35 100644 --- a/vm/include/vm/Machine.h +++ b/vm/include/vm/Machine.h @@ -1,17 +1,19 @@ /** * @file Machine.h - * @brief 寄存器虚拟机(12.9) + * @brief 寄存器虚拟机(V2:变长指令 / 槽表两级访问 / 栈方案 C / CAL 15 条) * @author * @date 2026-08-21 * - * @details 设计说明(详见 Doc/vm/扫描周期.md 与 Doc/vm/指令执行.md): - * - 只认 .stb 映像(vm 自带读实现;Machine::create 做型号/SHA-256 校验); - * 无 GC、无堆、无线程 - * - 扫描周期:MAIN pc=0 → RET;MAIN 帧跨周期保留,调用栈深度上限 64 - * - 帧:{ fn_id, regs[nregs], ret_pc, ret_fn_id };CALL 压帧复制 r0..r7, - * RET 复制回调用方(r0=结果) - * - 数据区:8 字节定宽槽(方案 a),指令 slot = 槽号,偏移 = slot × 8 - * - 可观察性(v1 预留):step / pc / fn_id / cycle_count / call_depth / reg + * @details 设计说明(详见 Doc/vm/扫描周期.md 与 Doc/isa/阶段4-执行侧V2.md): + * - 只认 V2 .stb(vm::Image);create 做结构 + SHA-256 + 型号(STATOR2)校验 + * - 变长取指:instr_len 前缀定长;译码按前缀分区 + func + uses_* 直推 + * - 数据访问两级:slot → 槽表[slot].addr → 值区按 func 读宽/扩展、写宽 + * - 栈方案 C:create 一次性预分配 max_stack;帧头 12B(fn_id/ret_pc/ret_fn_id) + * + regs(nregs×8,int64);压帧复制 r0..r7 全宽;字节 + 深度 64 双检查; + * MAIN 帧常驻栈底,周期结束回退 sp 到 MAIN 帧顶 + * - CAL 15 条(fb_id 分派):字段布局见 Doc/isa/指令集清单.md §8.1; + * RTC 用虚拟时钟(累计周期 × dt_ms,1970 纪元) + * - 可观察性:step / pc(字节)/ fn_id / cycle_count / call_depth / reg */ #pragma once @@ -22,6 +24,8 @@ #include "vm/Image.h" #include "isa/Instr.h" +#include "isa/Op.h" +#include "isa/Types.h" namespace vm { @@ -30,30 +34,29 @@ namespace vm { */ enum class Fault { None, ///< 正常 - CycleLimit, ///< 本周期指令数超过映像头 cycle_limit(用例 6) - StackOverflow, ///< 调用栈深度超过 64 - BadOp, ///< 非法操作码 / 寄存器越界 / CALL 目标越出函数表 - BadSlot, ///< slot × 8 + 8 越出数据区 + CycleLimit, ///< 本周期指令数超过映像头 cycle_limit + StackOverflow, ///< 调用栈字节越界或深度 > 64 + BadOp, ///< 非法操作码 / 寄存器越界 / CALL 目标越界 + BadSlot, ///< 槽号越界或值区访问越界 BadConst, ///< const_id 越出常量表 }; /** - * @brief 寄存器虚拟机:按扫描周期执行 .stb 映像。 - * @details 一个实例 = 一台机器:持有映像只读视图 + 数据区工作副本 + 调用栈。 - * MAIN 帧跨周期保留(PROGRAM 变量状态持久);每周期从 pc=0 执行到 - * MAIN 的 RET,期间调用栈可进可出(上限 64 帧)。 - * I 采样写入 / Q 读回由外部(executor)经 data() 完成,VM 只管数据区。 + * @brief 寄存器虚拟机(V2)。 + * @details 一个实例 = 一台机器:值区工作副本 + 预分配栈区 + 调用栈。 + * MAIN 帧常驻栈底(跨周期保留,PROGRAM 变量状态持久); + * 每周期从 pc=0 执行到 MAIN 的 RET。 */ class Machine { public: /** - * @brief 从映像字节构建机器(校验 + 拷贝数据段 + 建 MAIN 帧)。 + * @brief 从映像字节构建机器。 * @param image .stb 映像字节 * @param out 输出 Machine(未初始化) * @param err 错误输出;可为 nullptr(静默) - * @return true 成功;false(映像非法,err 已写) - * @details 校验顺序:解析 .stb(Image::from)→ SHA-256 → 型号标识匹配 - * (内建 kModelName + kModelVersion),任一不符直接拒绝。 + * @return true 成功;false(结构/SHA/型号任一不符,err 已写) + * @details 校验顺序:Image::from 结构 → SHA-256 → 型号 STATOR2; + * 通过后拷贝值区、预分配 max_stack 栈区、建 MAIN 帧。 */ static bool create(const std::vector& image, Machine* out, std::string* err); @@ -64,96 +67,79 @@ namespace vm { */ Fault run_cycle(); - // ---- 可观察性(v1 预留,12.11 供单步/回放/TUI)---- + // ---- 可观察性 ---- - /** - * @brief 执行一条指令,停在指令边界。 - * @return true 继续;false 周期结束(MAIN 的 RET)或发生故障 - */ + /// @brief 执行一条指令,停在指令边界;false = 周期结束或故障。 bool step(); - /// @brief 最近一次故障(step 返回 false 后查询)。 Fault fault() const; - /// @brief 周期是否已结束(MAIN 的 RET 后为 true)。 bool ended() const; - - /// @brief 映像头(dt_ms / cycle_limit 等)。 + /// @brief 映像头(dt_ms / cycle_limit / max_stack 等)。 const vm::ImageHeader& header() const; - - /// @brief 当前指令下标(相对当前函数字节码段)。 + /// @brief 当前 pc(字节偏移,相对当前函数字节码段)。 uint32_t pc() const; - /// @brief 当前函数 fn_id。 uint32_t fn_id() const; - /// @brief 本周期已执行指令数。 uint32_t cycle_count() const; - /// @brief 调用栈深度(MAIN 帧 = 1)。 uint32_t call_depth() const; - - /// @brief 数据区(I 采样写入 / Q 读回由外部做;8 字节定宽槽)。 + /// @brief 值区工作副本(I 采样写入 / Q 读回由外部做)。 uint8_t* data(); const uint8_t* data() const; - - /// @brief 数据区字节数。 + /// @brief 值区字节数。 size_t data_len() const; - /// @brief 当前帧寄存器值(i < nregs)。 int64_t reg(uint8_t i) const; - - /// @brief 当前帧寄存器数(函数头 nregs)。 + /// @brief 当前帧寄存器数。 uint32_t nregs() const; - - /// @brief 当前指令(pc_ 处;单步/观察用;pc 越界时返回 RET 哨兵)。 - isa::Instr cur_instr() const; + /// @brief 当前指令字节(pc 处;单步/观察用)。 + const uint8_t* cur_instr_bytes() const; + /// @brief 当前指令长度。 + uint32_t cur_instr_len() const; private: - /** - * @brief 调用帧。 - */ + /// 调用帧(regs 指向预分配栈区子块)。 struct Frame { - uint32_t fn_id = 0; ///< 本帧函数 - std::vector regs; ///< 寄存器文件(大小 = 函数头 nregs) - uint32_t ret_pc = 0; ///< CALL 的下一条(非 MAIN 帧有意义) - uint32_t ret_fn_id = 0; ///< 返回目标函数 + uint32_t fn_id = 0; ///< 本帧函数 + uint32_t nregs = 0; ///< 寄存器数 + int64_t* regs = nullptr;///< 寄存器文件(栈区内) + uint32_t ret_pc = 0; ///< CALL 的下一条(字节;非 MAIN 帧有意义) + uint32_t ret_fn_id = 0; ///< 返回目标函数 }; - vm::Image image_; ///< 映像只读视图(vm 自实现,指向外部缓冲) - std::vector data_; ///< 数据区工作副本(8 字节定宽槽) - std::vector frames_; ///< 调用栈([0] = MAIN,跨周期保留) - /** - * @brief 边沿检测上次输入(每槽 2 字节:[slot*2] 第一边沿、[slot*2+1] 第二边沿; - * CTU/CTD/R_TRIG/F_TRIG 用第一、CTUD 用两个),跨周期保留。 - */ - std::vector edge_prev_; - uint32_t pc_ = 0; ///< 当前 pc(相对本帧函数字节码段) - uint32_t cycle_count_ = 0; ///< 本周期指令数 - Fault fault_ = Fault::None;///< 最近一次故障 - bool ended_ = false; ///< step 后周期是否已结束 - - /// @brief 当前(栈顶)帧引用。 Frame& cur_frame(); - /// @brief 当前(栈顶)帧常量引用。 const Frame& cur_frame() const; - - /// @brief 译码执行一条;false = 故障或周期结束。 bool exec_one(); - - /// @brief 压帧(复制 r0..r7);fn_id 越界以 BadOp 表示。 Fault do_call(uint32_t fn_id); - - /// @brief 弹帧 / 周期结束(复制 r0..r7 回调用方)。 Fault do_ret(); + Fault do_cal(uint16_t fb_id, uint32_t slot); - /// @brief 执行内置 FB(TON/TOF/TP/CTU/CTD/CTUD/R_TRIG/F_TRIG)。 - Fault do_cal(uint8_t op, uint16_t slot); + /// 值区字段读写(实例 addr + 偏移,按 func 宽度)。 + bool read_value(uint32_t addr, uint8_t func, int64_t* out); + bool write_value(uint32_t addr, uint8_t func, int64_t v); + /// 算术(按 func:整数饱和 / 无符号 / 浮点 IEEE)。 + int64_t exec_arith(const char* name, uint8_t func, int64_t a, int64_t b); + /// 比较(按 func:有/无符号、浮点)。 + int64_t exec_cmp(const char* name, uint8_t func, int64_t a, int64_t b); + /// 比较结果(模板:有符号/无符号/浮点共用)。 + template + static int64_t cmp_result(const char* name, T a, T b); + /// 实例字段读写(slot → addr + off)。 + bool read_field(uint32_t slot, int32_t off, uint8_t func, int64_t* out); + bool write_field(uint32_t slot, int32_t off, uint8_t func, int64_t v); - /// @brief 读数据区槽(8 字节定宽;越界返回 false)。 - bool slot_get(uint16_t slot, int64_t* out) const; - - /// @brief 写数据区槽(8 字节定宽;越界返回 false)。 - bool slot_set(uint16_t slot, int64_t v); + vm::Image image_; ///< 映像只读视图 + std::vector data_; ///< 值区工作副本 + std::vector stack_; ///< 预分配栈区(max_stack 字节) + size_t sp_ = 0; ///< 栈顶(MAIN 帧之后) + std::vector frames_; ///< 调用栈([0] = MAIN,跨周期保留) + std::vector edge_prev_; ///< 边沿检测(每槽 2 字节,跨周期保留) + uint32_t pc_ = 0; ///< 当前 pc(字节,相对本帧函数字节码段) + uint32_t cycle_count_ = 0; ///< 本周期指令数 + uint64_t cycles_total_ = 0; ///< 累计周期数(RTC 虚拟时钟用) + Fault fault_ = Fault::None; ///< 最近一次故障 + bool ended_ = false; ///< step 后周期是否已结束 }; } diff --git a/vm/src/Machine.cpp b/vm/src/Machine.cpp index e247d31..9dbcdab 100644 --- a/vm/src/Machine.cpp +++ b/vm/src/Machine.cpp @@ -1,35 +1,29 @@ /** * @file Machine.cpp - * @brief 寄存器虚拟机实现(12.9:译码 switch + CALL/RET 帧栈 + CAL_* 定时器) + * @brief 寄存器虚拟机实现(V2:变长取指 / 槽表两级 / 栈方案 C / CAL 15 条) * @author * @date 2026-08-21 * - * @details 设计说明(详见 Doc/vm/指令执行.md): - * - 取指:函数表 → code_offset(字节)→ u32;执行后 pc 先 +1,跳转再 pc += off - * - 数据区 8 字节定宽槽:偏移 = slot × 8 - * - 寄存器越界检查仅对把字段当寄存器的指令;a|b 作偏移/槽号/常量 id 时跳过 + * @details 执行流程(exec_one): + * - 取指:函数表 → code_offset(字节)+ code_len(字节);pc 越界 → BadOp; + * instr_len 前缀定长搬字节 + * - 周期计数:cycle_count++;超 cycle_limit → CycleLimit + * - pc 先指向下一条(字节);跳转再 pc += off(相对下一条) + * - 译码:前缀分区 + func;寄存器越界按 uses_* 直推 + * - 分发:按指令名(if 链);算术/比较按 func(整数饱和/无符号/浮点 IEEE) */ #include "vm/Machine.h" #include +#include #include "isa/Encode.h" -#include "isa/Instr.h" -#include "isa/Op.h" -#include "isa/Types.h" namespace vm { /** - * @brief 从映像字节构建机器(校验 + 拷贝数据段 + 建 MAIN 帧)。 - * @param image .stb 映像字节 - * @param out 输出 Machine(未初始化) - * @param err 错误输出;可为 nullptr(静默) - * @return true 成功;false(映像非法,err 已写) - * @details 校验顺序:解析 .stb(Image::from)→ SHA-256 → 型号标识匹配; - * 通过后拷贝数据段为工作副本,初始化边沿检测缓冲(每槽 2 字节), - * 建 MAIN 帧(跨周期保留)并把执行状态复位。 + * @brief 从映像字节构建机器。 */ bool Machine::create(const std::vector& image, Machine* out, std::string* err) { @@ -40,7 +34,6 @@ bool Machine::create(const std::vector& image, Machine* out, } return false; } - // 12.13:型号匹配与 SHA-256 校验(不匹配/篡改直接拒绝) if (!out->image_.sha_ok()) { if (err) { *err = "image sha256 mismatch"; @@ -54,22 +47,40 @@ bool Machine::create(const std::vector& image, Machine* out, } return false; } - // 数据区工作副本(8 字节定宽槽) + // 值区工作副本 out->data_.assign(out->image_.data_bytes(), out->image_.data_bytes() + out->image_.data_len()); - out->edge_prev_.assign(out->data_len() / 8 * 2, 0); + // 边沿检测缓冲(每槽 2 字节) + out->edge_prev_.assign(out->image_.header().n_slots * 2, 0); + // 栈方案 C:预分配 max_stack + out->stack_.assign(out->image_.header().max_stack, 0); - // MAIN 帧(跨周期保留) + // MAIN 帧常驻栈底:帧头 12B + nregs×8 const uint32_t entry = out->image_.header().entry_fn_id; const vm::FuncRow main = out->image_.func_row(entry); - out->frames_.clear(); + const size_t main_need = 12 + static_cast(main.nregs) * 8; + if (main_need > out->stack_.size()) { + if (err) { + *err = "max_stack too small for MAIN frame"; + } + return false; + } Frame f; f.fn_id = entry; - f.regs.assign(main.nregs, 0); - out->frames_.push_back(std::move(f)); + f.nregs = main.nregs; + f.regs = reinterpret_cast(out->stack_.data() + 12); + f.ret_pc = 0; + f.ret_fn_id = entry; + for (uint32_t i = 0; i < main.nregs; ++i) { + f.regs[i] = 0; + } + out->frames_.clear(); + out->frames_.push_back(f); + out->sp_ = main_need; out->pc_ = 0; out->cycle_count_ = 0; + out->cycles_total_ = 0; out->fault_ = Fault::None; out->ended_ = false; return true; @@ -77,9 +88,6 @@ bool Machine::create(const std::vector& image, Machine* out, /** * @brief 跑一个扫描周期:MAIN pc=0 执行到 RET。 - * @return 故障(None = 正常完成) - * @details 循环执行 exec_one() 直到周期结束或故障;结束/故障后清空调用栈 - * (保留 MAIN 帧,PROGRAM 变量状态跨周期持久)。 */ Fault Machine::run_cycle() { cycle_count_ = 0; @@ -91,17 +99,16 @@ Fault Machine::run_cycle() { break; } } - // 周期结束:清调用栈(保留 MAIN 帧,PROGRAM 变量状态跨周期) + // 周期结束:回退 sp 到 MAIN 帧顶(保留 MAIN 帧) + const Frame& main = frames_[0]; + sp_ = 12 + static_cast(main.nregs) * 8; while (frames_.size() > 1) { frames_.pop_back(); } + ++cycles_total_; return fault_; } -/** - * @brief 执行一条指令,停在指令边界。 - * @return true 继续;false 周期结束(MAIN 的 RET)或发生故障 - */ bool Machine::step() { if (ended_ || fault_ != Fault::None) { return false; @@ -121,260 +128,431 @@ const uint8_t* Machine::data() const { return data_.data(); } size_t Machine::data_len() const { return data_.size(); } int64_t Machine::reg(uint8_t i) const { return cur_frame().regs[i]; } -/** - * @brief 当前帧寄存器数。 - * @return 栈顶帧 regs 大小(函数头 nregs) - */ uint32_t Machine::nregs() const { - return static_cast(cur_frame().regs.size()); + return cur_frame().nregs; +} + +const uint8_t* Machine::cur_instr_bytes() const { + const vm::FuncRow row = image_.func_row(cur_frame().fn_id); + if (pc_ >= row.code_len) { + return nullptr; + } + return image_.code_bytes() + row.code_offset + pc_; +} + +uint32_t Machine::cur_instr_len() const { + const uint8_t* p = cur_instr_bytes(); + return p != nullptr ? static_cast(isa::instr_len(p)) : 0; +} + +Machine::Frame& Machine::cur_frame() { return frames_.back(); } +const Machine::Frame& Machine::cur_frame() const { return frames_.back(); } + +// ---- 值区读写(两级访问:slot → addr → 值区按 func)---- + +/** + * @brief 读值区(addr 处按 func 宽度 + 扩展方向)。 + */ +bool Machine::read_value(uint32_t addr, uint8_t func, int64_t* out) { + const int w = isa::types::kFuncWidth[func]; + if (w <= 0 || addr + static_cast(w) > data_.size()) { + return false; + } + const uint8_t* p = data_.data() + addr; + if (func == 8) { // F32 位模式 + float f; + std::memcpy(&f, p, 4); + int32_t bits; + std::memcpy(&bits, &f, 4); + *out = static_cast(static_cast(bits)); + return true; + } + if (func == 9) { // F64 位模式 + double d; + std::memcpy(&d, p, 8); + uint64_t bits; + std::memcpy(&bits, &d, 8); + *out = static_cast(bits); + return true; + } + uint64_t v = 0; + for (int i = 0; i < w; ++i) { + v |= static_cast(p[i]) << (8 * i); + } + if (isa::types::kFuncSigned[func] && w < 8) { + const uint64_t sign = static_cast(1) << (8 * w - 1); + if (v & sign) { + v |= ~((static_cast(1) << (8 * w)) - 1); + } + } + *out = static_cast(v); + return true; } /** - * @brief 当前指令(pc_ 处)。 - * @return 指令字;pc_ 越出本函数字节码段时返回 RET 哨兵 - * @details 供单步/观察用;不修改任何执行状态。 + * @brief 写值区(addr 处按 func 宽度)。 */ -isa::Instr Machine::cur_instr() const { - const vm::FuncRow row = image_.func_row(cur_frame().fn_id); - const uint8_t* base = image_.code_bytes() + row.code_offset; - if (pc_ >= row.code_len) { - return isa::pack(isa::Op::RET, 0, 0, 0); +bool Machine::write_value(uint32_t addr, uint8_t func, int64_t v) { + const int w = isa::types::kFuncWidth[func]; + if (w <= 0 || addr + static_cast(w) > data_.size()) { + return false; } - return reinterpret_cast(base)[pc_]; + uint8_t* p = data_.data() + addr; + if (func == 8) { // F32:int64 低 32 位 → float 位模式 → 4 字节 + const uint32_t bits = static_cast(static_cast(v)); + float f; + std::memcpy(&f, &bits, 4); + std::memcpy(p, &f, 4); + return true; + } + if (func == 9) { // F64:int64 位模式 → 8 字节 + std::memcpy(p, &v, 8); + return true; + } + for (int i = 0; i < w; ++i) { + p[i] = static_cast((static_cast(v) >> (8 * i)) & 0xFFu); + } + return true; } -/// @brief 当前(栈顶)帧引用。 -Machine::Frame& Machine::cur_frame() { return frames_.back(); } +bool Machine::read_field(uint32_t slot, int32_t off, uint8_t func, int64_t* out) { + if (slot >= image_.header().n_slots) { + return false; + } + const uint32_t addr = image_.slot_addr(slot) + static_cast(off); + return read_value(addr, func, out); +} -/// @brief 当前(栈顶)帧常量引用。 -const Machine::Frame& Machine::cur_frame() const { return frames_.back(); } +bool Machine::write_field(uint32_t slot, int32_t off, uint8_t func, int64_t v) { + if (slot >= image_.header().n_slots) { + return false; + } + const uint32_t addr = image_.slot_addr(slot) + static_cast(off); + return write_value(addr, func, v); +} + +// ---- 指令执行 ---- /** * @brief 译码执行一条指令。 - * @return true 继续;false 故障(fault_ 已置)或周期结束 - * @details 流程:取指(越界 → BadOp)→ 周期计数(超 cycle_limit → CycleLimit)→ - * pc 先 +1(跳转按"相对下一条"叠加)→ 寄存器越界检查 → 按操作码分发。 - * 标量指令用 isa::types 饱和算术;LOAD/STORE 走 slot_get/slot_set; - * CALL/RET 走 do_call/do_ret;CAL_* 走 do_cal。 */ bool Machine::exec_one() { - // ---- 取指 ---- - const vm::FuncRow row = image_.func_row(cur_frame().fn_id); + Frame& fr = cur_frame(); + const vm::FuncRow row = image_.func_row(fr.fn_id); if (pc_ >= row.code_len) { fault_ = Fault::BadOp; return false; } - const uint8_t* base = image_.code_bytes() + row.code_offset; - const isa::Instr w = reinterpret_cast(base)[pc_]; + const uint8_t* code = image_.code_bytes() + row.code_offset + pc_; + const size_t ilen = isa::instr_len(code); + if (pc_ + ilen > row.code_len) { + fault_ = Fault::BadOp; + return false; + } - // ---- 周期指令计数 ---- ++cycle_count_; if (cycle_count_ > image_.header().cycle_limit) { fault_ = Fault::CycleLimit; return false; } + pc_ += static_cast(ilen); // 先指向下一条(字节) - // 先指向下一条(跳转指令再按"相对下一条"语义叠加偏移) - ++pc_; - - const isa::Op op = isa::op(w); - const uint8_t rd = isa::rd(w); - const uint8_t ra = isa::a(w); - const uint8_t rb = isa::b(w); - std::vector& regs = cur_frame().regs; - - // 寄存器越界检查(仅对把字段当寄存器的指令;a|b 作偏移/槽号/常量 id 时跳过) - const bool uses_rd = op == isa::Op::MOVE || op == isa::Op::NOT || op == isa::Op::AND || - op == isa::Op::OR || op == isa::Op::ADD || op == isa::Op::SUB || - op == isa::Op::MUL || op == isa::Op::DIV || op == isa::Op::CMP_EQ || - op == isa::Op::CMP_NE || op == isa::Op::CMP_LT || - op == isa::Op::CMP_LE || op == isa::Op::CMP_GT || - op == isa::Op::CMP_GE || op == isa::Op::JT || op == isa::Op::JF || - op == isa::Op::LOADK || op == isa::Op::LOAD_I || - op == isa::Op::LOAD_M || op == isa::Op::LOAD_GLOBAL || - op == isa::Op::STORE_Q || op == isa::Op::STORE_M || - op == isa::Op::STORE_GLOBAL; - const bool uses_ra = op == isa::Op::MOVE || op == isa::Op::NOT || op == isa::Op::AND || - op == isa::Op::OR || op == isa::Op::ADD || op == isa::Op::SUB || - op == isa::Op::MUL || op == isa::Op::DIV || op == isa::Op::CMP_EQ || - op == isa::Op::CMP_NE || op == isa::Op::CMP_LT || - op == isa::Op::CMP_LE || op == isa::Op::CMP_GT || - op == isa::Op::CMP_GE; - const bool uses_rb = uses_ra; - if ((uses_rd && rd >= regs.size()) || (uses_ra && ra >= regs.size()) || - (uses_rb && rb >= regs.size())) { + const isa::Decoded d = isa::decode(code, ilen); + const isa::OpFormat fmt = isa::format_of(d.prefix, d.op); + if (fmt == isa::OpFormat::RESERVED) { fault_ = Fault::BadOp; return false; } + // 寄存器越界(uses_* 前缀直推) + if ((isa::uses_rd(fmt) && d.rd >= fr.nregs) || + (isa::uses_rs1(fmt) && d.rs1 >= fr.nregs) || + (isa::uses_rs2(fmt) && d.rs2 >= fr.nregs)) { + fault_ = Fault::BadOp; + return false; + } + const uint8_t func = d.func; + int64_t v1 = 0, v2 = 0, r = 0; - switch (op) { - case isa::Op::MOVE: - regs[rd] = regs[ra]; - return true; - case isa::Op::LOADK: { - const uint16_t cid = isa::imm16(w); - if (cid >= image_.header().n_consts) { - fault_ = Fault::BadConst; - return false; - } - const vm::ConstEntry c = image_.const_entry(cid); - // 按类型标记解释常量值:BOOL 归一化 0/1,INT 符号扩展,TIME 全 64 位 - if (c.tag == isa::types::Bool) { - regs[rd] = c.value ? 1 : 0; - } else if (c.tag == isa::types::Int) { - regs[rd] = static_cast(c.value); - } else { - regs[rd] = static_cast(c.value); - } - return true; + // 按指令名分发 + const char* name = isa::mnemonic(d.prefix, d.op); + if (std::strcmp(name, "MOVE") == 0) { + // func 0..3 = 宽度 1/2/4/8B,高位清 0 + static const int kMoveWidth[4] = {1, 2, 4, 8}; + const int w = kMoveWidth[d.func & 3]; + int64_t v = fr.regs[d.rs1]; + if (w < 8) { + v &= (static_cast(1) << (8 * w)) - 1; } - case isa::Op::NOT: - regs[rd] = (regs[ra] == 0) ? 1 : 0; - return true; - case isa::Op::AND: - regs[rd] = (regs[ra] != 0 && regs[rb] != 0) ? 1 : 0; - return true; - case isa::Op::OR: - regs[rd] = (regs[ra] != 0 || regs[rb] != 0) ? 1 : 0; - return true; - case isa::Op::ADD: - regs[rd] = isa::types::sat_add(static_cast(regs[ra]), - static_cast(regs[rb])); - return true; - case isa::Op::SUB: - regs[rd] = isa::types::sat_sub(static_cast(regs[ra]), - static_cast(regs[rb])); - return true; - case isa::Op::MUL: - regs[rd] = isa::types::sat_mul(static_cast(regs[ra]), - static_cast(regs[rb])); - return true; - case isa::Op::DIV: - regs[rd] = isa::types::sat_div(static_cast(regs[ra]), - static_cast(regs[rb])); - return true; - case isa::Op::CMP_EQ: - case isa::Op::CMP_NE: - case isa::Op::CMP_LT: - case isa::Op::CMP_LE: - case isa::Op::CMP_GT: - case isa::Op::CMP_GE: { - const int64_t l = regs[ra]; - const int64_t r = regs[rb]; - bool res = false; - switch (op) { - case isa::Op::CMP_EQ: res = (l == r); break; - case isa::Op::CMP_NE: res = (l != r); break; - case isa::Op::CMP_LT: res = (l < r); break; - case isa::Op::CMP_LE: res = (l <= r); break; - case isa::Op::CMP_GT: res = (l > r); break; - case isa::Op::CMP_GE: res = (l >= r); break; - default: break; - } - regs[rd] = res ? 1 : 0; - return true; - } - case isa::Op::JMP: - pc_ += isa::off16(w); - return true; - case isa::Op::JT: - if (regs[rd] != 0) { - pc_ += isa::off16(w); - } - return true; - case isa::Op::JF: - if (regs[rd] == 0) { - pc_ += isa::off16(w); - } - return true; - case isa::Op::LOAD_I: - case isa::Op::LOAD_M: - case isa::Op::LOAD_GLOBAL: { - const uint16_t slot = isa::imm16(w); - if (!slot_get(slot, ®s[rd])) { - fault_ = Fault::BadSlot; - return false; - } - return true; - } - case isa::Op::STORE_Q: - case isa::Op::STORE_M: - case isa::Op::STORE_GLOBAL: { - const uint16_t slot = isa::imm16(w); - if (!slot_set(slot, regs[rd])) { - fault_ = Fault::BadSlot; - return false; - } - return true; - } - case isa::Op::CALL: - fault_ = do_call(isa::imm16(w)); - return fault_ == Fault::None; - case isa::Op::RET: - fault_ = do_ret(); - if (fault_ != Fault::None) { - return false; - } - return !ended_; // MAIN 的 RET → 周期结束(返回 false) - case isa::Op::CAL_TON: - case isa::Op::CAL_TOF: - case isa::Op::CAL_TP: - case isa::Op::CAL_CTU: - case isa::Op::CAL_CTD: - case isa::Op::CAL_CTUD: - case isa::Op::CAL_R_TRIG: - case isa::Op::CAL_F_TRIG: - fault_ = do_cal(static_cast(op), isa::imm16(w)); - return fault_ == Fault::None; - default: - fault_ = Fault::BadOp; + fr.regs[d.rd] = v; + return true; + } + if (std::strcmp(name, "LOADK") == 0) { + const uint16_t cid = static_cast(d.imm32 & 0xFFFF); + if (cid >= image_.header().n_consts) { + fault_ = Fault::BadConst; return false; + } + // 常量表 8B 原始值 → 按 func 读低 w 字节 + 扩展 + const uint64_t raw = image_.const_entry(cid).value; + const int w = isa::types::kFuncWidth[func]; + int64_t v = static_cast(raw); + if (func == 8) { + float f; + std::memcpy(&f, &raw, 4); + int32_t bits; + std::memcpy(&bits, &f, 4); + fr.regs[d.rd] = static_cast(static_cast(bits)); + return true; + } + if (func == 9) { + fr.regs[d.rd] = static_cast(raw); + return true; + } + if (w < 8) { + if (isa::types::kFuncSigned[func]) { + v = static_cast(static_cast(raw)); // 符号扩展(宽 ≤2) + if (w == 4) v = static_cast(static_cast(raw)); + if (w == 1) v = static_cast(static_cast(raw)); + } else { + v &= (static_cast(1) << (8 * w)) - 1; + } + } + fr.regs[d.rd] = v; + return true; } + if (std::strcmp(name, "LOAD") == 0) { + if (!read_value(image_.slot_addr(d.imm32), func, &v1)) { + fault_ = Fault::BadSlot; + return false; + } + fr.regs[d.rd] = v1; + return true; + } + if (std::strcmp(name, "STORE") == 0) { + if (!write_value(image_.slot_addr(d.imm32), func, fr.regs[d.rd])) { + fault_ = Fault::BadSlot; + return false; + } + return true; + } + if (std::strcmp(name, "LOAD_OFF") == 0) { + if (!read_field(d.imm32, d.off_hi, func, &v1)) { + fault_ = Fault::BadSlot; + return false; + } + fr.regs[d.rd] = v1; + return true; + } + if (std::strcmp(name, "STORE_OFF") == 0) { + if (!write_field(d.imm32, d.off_hi, func, fr.regs[d.rd])) { + fault_ = Fault::BadSlot; + return false; + } + return true; + } + if (std::strcmp(name, "NOT") == 0) { + // 逻辑非(恒产 0/1):BOOL 域语义;位串逐位取反需专用指令(v1 无) + fr.regs[d.rd] = (fr.regs[d.rs1] == 0) ? 1 : 0; + return true; + } + if (std::strcmp(name, "AND") == 0 || std::strcmp(name, "OR") == 0 || + std::strcmp(name, "XOR") == 0 || std::strcmp(name, "NAND") == 0 || + std::strcmp(name, "NOR") == 0 || std::strcmp(name, "XNOR") == 0) { + const uint64_t a = static_cast(fr.regs[d.rs1]); + const uint64_t b = static_cast(fr.regs[d.rs2]); + uint64_t x = 0; + if (std::strcmp(name, "AND") == 0) x = a & b; + else if (std::strcmp(name, "OR") == 0) x = a | b; + else if (std::strcmp(name, "XOR") == 0) x = a ^ b; + else if (std::strcmp(name, "NAND") == 0) x = ~(a & b); + else if (std::strcmp(name, "NOR") == 0) x = ~(a | b); + else x = ~(a ^ b); + fr.regs[d.rd] = static_cast(x); + return true; + } + if (std::strcmp(name, "NEG") == 0) { + if (func == 8) { + float f; + std::memcpy(&f, &fr.regs[d.rs1], 4); + f = -f; + int32_t bits; + std::memcpy(&bits, &f, 4); + fr.regs[d.rd] = static_cast(static_cast(bits)); + } else if (func == 9) { + double dd; + std::memcpy(&dd, &fr.regs[d.rs1], 8); + dd = -dd; + std::memcpy(&fr.regs[d.rd], &dd, 8); + } else { + fr.regs[d.rd] = -fr.regs[d.rs1]; + } + return true; + } + if (std::strcmp(name, "ADD") == 0 || std::strcmp(name, "SUB") == 0 || + std::strcmp(name, "MUL") == 0 || std::strcmp(name, "DIV") == 0) { + r = exec_arith(name, func, fr.regs[d.rs1], fr.regs[d.rs2]); + fr.regs[d.rd] = r; + return true; + } + if (std::strncmp(name, "CMP_", 4) == 0) { + r = exec_cmp(name, func, fr.regs[d.rs1], fr.regs[d.rs2]); + fr.regs[d.rd] = r; + return true; + } + if (std::strcmp(name, "JMP") == 0) { + pc_ += static_cast(d.imm32); + return true; + } + if (std::strcmp(name, "JT") == 0) { + if (fr.regs[d.rd] != 0) { + pc_ += static_cast(d.imm32); + } + return true; + } + if (std::strcmp(name, "JF") == 0) { + if (fr.regs[d.rd] == 0) { + pc_ += static_cast(d.imm32); + } + return true; + } + if (std::strcmp(name, "CALL") == 0) { + fault_ = do_call(d.imm32); + return fault_ == Fault::None; + } + if (std::strcmp(name, "RET") == 0) { + fault_ = do_ret(); + if (fault_ != Fault::None) { + return false; + } + return !ended_; + } + if (std::strcmp(name, "CAL") == 0) { + const uint16_t fb_id = static_cast(d.imm32 & 0xFFFF); + const uint32_t slot = d.imm32 >> 16; + fault_ = do_cal(fb_id, slot); + return fault_ == Fault::None; + } + if (std::strcmp(name, "NOP") == 0) { + return true; + } + fault_ = Fault::BadOp; + return false; } /** - * @brief 读数据区槽(8 字节定宽,小端)。 - * @param slot 槽号 - * @param out 输出值 - * @return false = slot × 8 + 8 越出数据区 + * @brief 算术(按 func:整数饱和 / 无符号 / 浮点 IEEE)。 */ -bool Machine::slot_get(uint16_t slot, int64_t* out) const { - const size_t off = static_cast(slot) * 8; - if (off + 8 > data_.size()) { - return false; +int64_t Machine::exec_arith(const char* name, uint8_t func, int64_t a, int64_t b) { + const bool is_add = std::strcmp(name, "ADD") == 0; + const bool is_sub = std::strcmp(name, "SUB") == 0; + const bool is_mul = std::strcmp(name, "MUL") == 0; + if (func == 8) { + float x, y; + std::memcpy(&x, &a, 4); + std::memcpy(&y, &b, 4); + float f = 0; + if (is_add) f = x + y; + else if (is_sub) f = x - y; + else if (is_mul) f = x * y; + else f = x / y; + int32_t bits; + std::memcpy(&bits, &f, 4); + return static_cast(static_cast(bits)); } - int64_t v = 0; - for (int i = 0; i < 8; ++i) { - v |= static_cast(data_[off + i]) << (8 * i); + if (func == 9) { + double x, y; + std::memcpy(&x, &a, 8); + std::memcpy(&y, &b, 8); + double d = 0; + if (is_add) d = x + y; + else if (is_sub) d = x - y; + else if (is_mul) d = x * y; + else d = x / y; + return static_cast(d); // 位模式直存 } - *out = v; - return true; + // 整数:按宽度与符号选饱和模板 + const int w = isa::types::kFuncWidth[func]; + const bool sgn = isa::types::kFuncSigned[func]; + if (w == 1 && sgn) { + const int8_t x = static_cast(a), y = static_cast(b); + return is_add ? isa::types::sat_add(x, y) + : is_sub ? isa::types::sat_sub(x, y) + : is_mul ? isa::types::sat_mul(x, y) + : isa::types::sat_div(x, y); + } + if (w == 1 && !sgn) { + const uint8_t x = static_cast(a), y = static_cast(b); + return is_add ? isa::types::sat_add(x, y) + : is_sub ? isa::types::sat_sub(x, y) + : is_mul ? isa::types::sat_mul(x, y) + : isa::types::sat_div(x, y); + } + if (w == 2 && sgn) { + const int16_t x = static_cast(a), y = static_cast(b); + return is_add ? isa::types::sat_add(x, y) + : is_sub ? isa::types::sat_sub(x, y) + : is_mul ? isa::types::sat_mul(x, y) + : isa::types::sat_div(x, y); + } + if (w == 2 && !sgn) { + const uint16_t x = static_cast(a), y = static_cast(b); + return is_add ? isa::types::sat_add(x, y) + : is_sub ? isa::types::sat_sub(x, y) + : is_mul ? isa::types::sat_mul(x, y) + : isa::types::sat_div(x, y); + } + if (w == 4 && sgn) { + const int32_t x = static_cast(a), y = static_cast(b); + return is_add ? isa::types::sat_add(x, y) + : is_sub ? isa::types::sat_sub(x, y) + : is_mul ? isa::types::sat_mul(x, y) + : isa::types::sat_div(x, y); + } + if (w == 4 && !sgn) { + const uint32_t x = static_cast(a), y = static_cast(b); + return is_add ? isa::types::sat_add(x, y) + : is_sub ? isa::types::sat_sub(x, y) + : is_mul ? isa::types::sat_mul(x, y) + : isa::types::sat_div(x, y); + } + if (sgn) { + const int64_t x = a, y = b; + return is_add ? isa::types::sat_add(x, y) + : is_sub ? isa::types::sat_sub(x, y) + : is_mul ? isa::types::sat_mul(x, y) + : isa::types::sat_div(x, y); + } + const uint64_t x = static_cast(a), y = static_cast(b); + return is_add ? isa::types::sat_add(x, y) + : is_sub ? isa::types::sat_sub(x, y) + : is_mul ? isa::types::sat_mul(x, y) + : isa::types::sat_div(x, y); } /** - * @brief 写数据区槽(8 字节定宽,小端)。 - * @param slot 槽号 - * @param v 要写入的值 - * @return false = slot × 8 + 8 越出数据区 + * @brief 比较(按 func:有/无符号、浮点 IEEE)。 */ -bool Machine::slot_set(uint16_t slot, int64_t v) { - const size_t off = static_cast(slot) * 8; - if (off + 8 > data_.size()) { - return false; +int64_t Machine::exec_cmp(const char* name, uint8_t func, int64_t a, int64_t b) { + if (func == 8) { + float x, y; + std::memcpy(&x, &a, 4); + std::memcpy(&y, &b, 4); + return cmp_result(name, x, y); } - for (int i = 0; i < 8; ++i) { - data_[off + i] = static_cast((v >> (8 * i)) & 0xFFu); + if (func == 9) { + double x, y; + std::memcpy(&x, &a, 8); + std::memcpy(&y, &b, 8); + return cmp_result(name, x, y); } - return true; + if (isa::types::kFuncSigned[func]) { + return cmp_result(name, a, b); + } + return cmp_result(name, static_cast(a), static_cast(b)); } /** - * @brief 调用:压新帧。 - * @param fn_id 目标函数(函数表下标) - * @return Fault(fn_id 越界 → BadOp;栈深 ≥ 64 → StackOverflow;成功 → None) - * @details 新帧寄存器清零,ret_pc = 当前 pc(已指向 CALL 下一条); - * 按调用约定把调用方 r0..r7 复制进新帧(实参已由调用点 MOVE 进 r1..r7); - * pc 复位到新函数起点。 + * @brief 调用:压新帧(栈方案 C)。 */ Fault Machine::do_call(uint32_t fn_id) { if (fn_id >= image_.header().n_funcs) { @@ -384,27 +562,38 @@ Fault Machine::do_call(uint32_t fn_id) { return Fault::StackOverflow; } const vm::FuncRow row = image_.func_row(fn_id); + const size_t need = 12 + static_cast(row.nregs) * 8; + if (sp_ + need > stack_.size()) { + return Fault::StackOverflow; + } + Frame& caller = cur_frame(); + uint8_t* p = stack_.data() + sp_; + uint32_t* hdr = reinterpret_cast(p); + hdr[0] = fn_id; + hdr[1] = pc_; // 已指向 CALL 下一条(字节) + hdr[2] = caller.fn_id; Frame f; f.fn_id = fn_id; - f.regs.assign(row.nregs, 0); - f.ret_pc = pc_; // 已指向 CALL 的下一条 - f.ret_fn_id = cur_frame().fn_id; - // 调用约定:复制当前帧 r0..r7 → 新帧(实参已由调用点 MOVE 进 r1..r7) - const size_t n = std::min(8, std::min(cur_frame().regs.size(), f.regs.size())); - for (size_t i = 0; i < n; ++i) { - f.regs[i] = cur_frame().regs[i]; + f.nregs = row.nregs; + f.regs = reinterpret_cast(p + 12); + f.ret_pc = pc_; + f.ret_fn_id = caller.fn_id; + for (uint32_t i = 0; i < row.nregs; ++i) { + f.regs[i] = 0; } - frames_.push_back(std::move(f)); + // 调用约定:复制 r0..r7 全宽 + const size_t n = std::min(8, std::min(caller.nregs, f.nregs)); + for (size_t i = 0; i < n; ++i) { + f.regs[i] = caller.regs[i]; + } + frames_.push_back(f); + sp_ += need; pc_ = 0; return Fault::None; } /** * @brief 返回:弹帧 / 周期结束。 - * @return Fault(成功 → None) - * @details MAIN 帧的 RET:ended_ = true(周期结束,不弹帧)。 - * 非 MAIN 帧:按调用约定把 r0..r7 复制回调用方(r0 = 结果,r1..r7 原样), - * pc 恢复为 ret_pc,弹帧。 */ Fault Machine::do_ret() { if (frames_.size() <= 1) { @@ -413,153 +602,122 @@ Fault Machine::do_ret() { } Frame& cur = cur_frame(); Frame& caller = frames_[frames_.size() - 2]; - // 调用约定:复制当前帧 r0..r7 → 调用方(r0 = 结果,r1..r7 原样返回) - const size_t n = std::min(8, std::min(cur.regs.size(), caller.regs.size())); + const size_t n = std::min(8, std::min(cur.nregs, caller.nregs)); for (size_t i = 0; i < n; ++i) { caller.regs[i] = cur.regs[i]; } pc_ = cur.ret_pc; + sp_ -= 12 + static_cast(cur.nregs) * 8; frames_.pop_back(); return Fault::None; } /** - * @brief 执行内置 FB(CAL_*)。 - * @param op 操作码(CAL_TON..CAL_F_TRIG,8 个) - * @param slot 实例起始槽号 - * @return Fault(槽越界 → BadSlot;非 CAL_* → BadOp;成功 → None) - * @details 内建 FB 布局冻结(Doc/compiler/符号表与链接.md): - * - TON/TOF/TP:in/pt/q/et(字段 0/1/2/3) - * - CTU:cu/r/pv/q/cv(0/1/2/3/4);CTD:cd/ld/pv/q/cv(同) - * - CTUD:cu/cd/r/lu/pv/qu/qd/cv(0..7) - * - R_TRIG / F_TRIG:clk/q(0/1) - * 字段偏移 = 字段序号 × 8(8 字节定宽槽);边沿存 edge_prev_(每槽 2 字节)。 + * @brief 执行内置 FB(CAL,fb_id 分派 15 条)。 */ -Fault Machine::do_cal(uint8_t op, uint16_t slot) { - // 内建 FB 布局冻结(Doc/compiler/符号表与链接.md): - // TON/TOF/TP:in/pt/q/et(字段 0/1/2/3) - // CTU:cu/r/pv/q/cv(0/1/2/3/4);CTD:cd/ld/pv/q/cv(同) - // CTUD:cu/cd/r/lu/pv/qu/qd/cv(0..7) - // R_TRIG / F_TRIG:clk/q(0/1) - // 字段偏移 = 字段序号 × 8(8 字节定宽槽);边沿存 edge_prev_ +Fault Machine::do_cal(uint16_t fb_id, uint32_t slot) { const int64_t dt = image_.header().dt_ms; int64_t v = 0; - - const bool is_ton = op == static_cast(isa::Op::CAL_TON); - const bool is_tof = op == static_cast(isa::Op::CAL_TOF); - if (is_ton || is_tof) { - // in=0 pt=1 q=2 et=3 - if (!slot_get(static_cast(slot + 0), &v)) return Fault::BadSlot; + if (fb_id <= 2) { // TON/TOF/TP:in@0 pt@8 q@16 et@24 + const bool is_ton = fb_id == 0; + const bool is_tp = fb_id == 2; + if (!read_field(slot, 0, 0, &v)) return Fault::BadSlot; const bool in = v != 0; - if (!slot_get(static_cast(slot + 1), &v)) return Fault::BadSlot; + if (!read_field(slot, 8, 7, &v)) return Fault::BadSlot; const int64_t pt = v; int64_t et = 0; - if (!slot_get(static_cast(slot + 3), &et)) return Fault::BadSlot; - int64_t q = 0; + if (!read_field(slot, 24, 7, &et)) return Fault::BadSlot; + if (is_tp) { + // TP:in 上升沿启动 PT 时长脉冲 + const bool prev = edge_prev_[slot * 2] != 0; + const bool rising = in && !prev; + int64_t q = 0; + if (rising) { + et = 0; + } + const bool timing = rising || (et > 0); + if (timing) { + et += dt; + if (pt > 0 && et >= pt) { + et = 0; + q = 0; + } else { + q = 1; + } + } else { + et = 0; + q = 0; + } + edge_prev_[slot * 2] = in ? 1 : 0; + if (!write_field(slot, 24, 7, et)) return Fault::BadSlot; + if (!write_field(slot, 16, 0, q)) return Fault::BadSlot; + return Fault::None; + } if (is_ton) { - // TON:in 真 → et += dt(到 pt 停)、q = et ≥ pt;in 假 → et = 0、q = 0 if (in) { et += dt; if (pt > 0 && et >= pt) { et = pt; } - q = (et >= pt) ? 1 : 0; + v = (et >= pt) ? 1 : 0; } else { et = 0; - q = 0; + v = 0; } - } else { - // TOF:in 真 → q = 1、et = 0;掉电 → et += dt、et ≥ pt → q = 0 + } else { // TOF if (in) { et = 0; - q = 1; + v = 1; } else { et += dt; - q = (pt > 0 && et >= pt) ? 0 : 1; + v = (pt > 0 && et >= pt) ? 0 : 1; } } - if (!slot_set(static_cast(slot + 3), et)) return Fault::BadSlot; - if (!slot_set(static_cast(slot + 2), q)) return Fault::BadSlot; + if (!write_field(slot, 24, 7, et)) return Fault::BadSlot; + if (!write_field(slot, 16, 0, v)) return Fault::BadSlot; return Fault::None; } - - if (op == static_cast(isa::Op::CAL_TP)) { - // in=0 pt=1 q=2 et=3 + 上次 in(edge_prev_[slot*2]) - if (static_cast(slot) * 2 + 1 >= edge_prev_.size()) return Fault::BadSlot; - if (!slot_get(static_cast(slot + 0), &v)) return Fault::BadSlot; - const bool in = v != 0; - if (!slot_get(static_cast(slot + 1), &v)) return Fault::BadSlot; - const int64_t pt = v; - int64_t et = 0; - if (!slot_get(static_cast(slot + 3), &et)) return Fault::BadSlot; - // TP(脉冲):in 上升沿启动 PT 时长的脉冲,期间 in 变化不影响 - const bool prev = edge_prev_[slot * 2] != 0; - const bool rising = in && !prev; - int64_t q = 0; - if (rising) { - et = 0; - } - const bool timing = rising || (et > 0); - if (timing) { - et += dt; - if (pt > 0 && et >= pt) { - et = 0; // 脉冲结束:et 归零,下周期不再计时 - q = 0; - } else { - q = 1; - } - } else { - et = 0; - q = 0; - } - edge_prev_[slot * 2] = in ? 1 : 0; - if (!slot_set(static_cast(slot + 3), et)) return Fault::BadSlot; - if (!slot_set(static_cast(slot + 2), q)) return Fault::BadSlot; - return Fault::None; - } - - if (op == static_cast(isa::Op::CAL_CTU) || - op == static_cast(isa::Op::CAL_CTD)) { - // CTU:cu/r/pv/q/cv;CTD:cd/ld/pv/q/cv - if (static_cast(slot) * 2 >= edge_prev_.size()) return Fault::BadSlot; - if (!slot_get(static_cast(slot + 0), &v)) return Fault::BadSlot; - const bool edge_in = v != 0; // cu(CTU)/ cd(CTD) - if (!slot_get(static_cast(slot + 1), &v)) return Fault::BadSlot; - const bool ld_in = v != 0; // r(CTU)/ ld(CTD) - if (!slot_get(static_cast(slot + 2), &v)) return Fault::BadSlot; + if (fb_id == 3 || fb_id == 4 || fb_id == 5 || fb_id == 6) { + // CTU/CTD:cu/cd@0 r/ld@1 pv@2 q@4 cv@6;DCTU/DCTD:pv@4 q@8 cv@12 + const bool is_d = fb_id == 4 || fb_id == 6; + const bool is_ctu = fb_id == 3 || fb_id == 4; + const uint8_t fw = is_d ? 5 : 3; // DINT(5) / INT(3) + if (!read_field(slot, 0, 0, &v)) return Fault::BadSlot; + const bool edge_in = v != 0; + if (!read_field(slot, 1, 0, &v)) return Fault::BadSlot; + const bool ld_in = v != 0; + if (!read_field(slot, is_d ? 4 : 2, fw, &v)) return Fault::BadSlot; const int64_t pv = v; int64_t cv = 0; - if (!slot_get(static_cast(slot + 4), &cv)) return Fault::BadSlot; + if (!read_field(slot, is_d ? 12 : 6, fw, &cv)) return Fault::BadSlot; const bool prev = edge_prev_[slot * 2] != 0; if (ld_in) { cv = pv; } else if (edge_in && !prev) { - cv += (op == static_cast(isa::Op::CAL_CTU)) ? 1 : -1; + cv += is_ctu ? 1 : -1; } edge_prev_[slot * 2] = edge_in ? 1 : 0; - const int64_t q = (op == static_cast(isa::Op::CAL_CTU)) - ? ((cv >= pv) ? 1 : 0) - : ((cv <= 0) ? 1 : 0); - if (!slot_set(static_cast(slot + 4), cv)) return Fault::BadSlot; - if (!slot_set(static_cast(slot + 3), q)) return Fault::BadSlot; + const int64_t q = is_ctu ? ((cv >= pv) ? 1 : 0) : ((cv <= 0) ? 1 : 0); + if (!write_field(slot, is_d ? 12 : 6, fw, cv)) return Fault::BadSlot; + if (!write_field(slot, is_d ? 8 : 4, 0, q)) return Fault::BadSlot; return Fault::None; } - - if (op == static_cast(isa::Op::CAL_CTUD)) { - // cu=0 cd=1 r=2 lu=3 pv=4 qu=5 qd=6 cv=7 - if (static_cast(slot) * 2 + 1 >= edge_prev_.size()) return Fault::BadSlot; - if (!slot_get(static_cast(slot + 0), &v)) return Fault::BadSlot; + if (fb_id == 7 || fb_id == 8) { + // CTUD/DCTUD:cu@0 cd@1 r@2 lu@3 pv@4/4 qu@6/8 qd@7/9 cv@8/12 + const bool is_d = fb_id == 8; + const uint8_t fw = is_d ? 5 : 3; + if (!read_field(slot, 0, 0, &v)) return Fault::BadSlot; const bool cu = v != 0; - if (!slot_get(static_cast(slot + 1), &v)) return Fault::BadSlot; + if (!read_field(slot, 1, 0, &v)) return Fault::BadSlot; const bool cd = v != 0; - if (!slot_get(static_cast(slot + 2), &v)) return Fault::BadSlot; + if (!read_field(slot, 2, 0, &v)) return Fault::BadSlot; const bool r = v != 0; - if (!slot_get(static_cast(slot + 3), &v)) return Fault::BadSlot; + if (!read_field(slot, 3, 0, &v)) return Fault::BadSlot; const bool lu = v != 0; - if (!slot_get(static_cast(slot + 4), &v)) return Fault::BadSlot; + if (!read_field(slot, 4, fw, &v)) return Fault::BadSlot; const int64_t pv = v; int64_t cv = 0; - if (!slot_get(static_cast(slot + 7), &cv)) return Fault::BadSlot; + if (!read_field(slot, is_d ? 12 : 8, fw, &cv)) return Fault::BadSlot; const bool prev_cu = edge_prev_[slot * 2] != 0; const bool prev_cd = edge_prev_[slot * 2 + 1] != 0; if (r) { @@ -574,28 +732,105 @@ Fault Machine::do_cal(uint8_t op, uint16_t slot) { edge_prev_[slot * 2 + 1] = cd ? 1 : 0; const int64_t qu = (cv >= pv) ? 1 : 0; const int64_t qd = (cv <= 0) ? 1 : 0; - if (!slot_set(static_cast(slot + 7), cv)) return Fault::BadSlot; - if (!slot_set(static_cast(slot + 5), qu)) return Fault::BadSlot; - if (!slot_set(static_cast(slot + 6), qd)) return Fault::BadSlot; + if (!write_field(slot, is_d ? 12 : 8, fw, cv)) return Fault::BadSlot; + if (!write_field(slot, is_d ? 8 : 6, 0, qu)) return Fault::BadSlot; + if (!write_field(slot, is_d ? 9 : 7, 0, qd)) return Fault::BadSlot; return Fault::None; } - - if (op == static_cast(isa::Op::CAL_R_TRIG) || - op == static_cast(isa::Op::CAL_F_TRIG)) { - // clk=0 q=1 - if (static_cast(slot) * 2 >= edge_prev_.size()) return Fault::BadSlot; - if (!slot_get(static_cast(slot + 0), &v)) return Fault::BadSlot; + if (fb_id == 9 || fb_id == 10) { // R_TRIG/F_TRIG:clk@0 q@1 + if (!read_field(slot, 0, 0, &v)) return Fault::BadSlot; const bool clk = v != 0; const bool prev = edge_prev_[slot * 2] != 0; - const int64_t q = (op == static_cast(isa::Op::CAL_R_TRIG)) - ? ((clk && !prev) ? 1 : 0) - : ((!clk && prev) ? 1 : 0); + const int64_t q = (fb_id == 9) ? ((clk && !prev) ? 1 : 0) + : ((!clk && prev) ? 1 : 0); edge_prev_[slot * 2] = clk ? 1 : 0; - if (!slot_set(static_cast(slot + 1), q)) return Fault::BadSlot; + if (!write_field(slot, 1, 0, q)) return Fault::BadSlot; + return Fault::None; + } + if (fb_id == 11) { // SR:S1@0 R@1 Q1@2(置位优先) + if (!read_field(slot, 0, 0, &v)) return Fault::BadSlot; + const bool s1 = v != 0; + if (!read_field(slot, 1, 0, &v)) return Fault::BadSlot; + const bool r = v != 0; + int64_t q = 0; + if (!read_field(slot, 2, 0, &q)) return Fault::BadSlot; + if (s1) { + q = 1; + } else if (r) { + q = 0; + } + if (!write_field(slot, 2, 0, q)) return Fault::BadSlot; + return Fault::None; + } + if (fb_id == 12) { // RS:SET@0 RESET1@1 Q1@2(复位优先) + if (!read_field(slot, 0, 0, &v)) return Fault::BadSlot; + const bool set = v != 0; + if (!read_field(slot, 1, 0, &v)) return Fault::BadSlot; + const bool reset = v != 0; + int64_t q = 0; + if (!read_field(slot, 2, 0, &q)) return Fault::BadSlot; + if (reset) { + q = 0; + } else if (set) { + q = 1; + } + if (!write_field(slot, 2, 0, q)) return Fault::BadSlot; + return Fault::None; + } + if (fb_id == 13) { // PWM:in@0 pt@8 duty@16(REAL) q@20 et@24 + if (!read_field(slot, 0, 0, &v)) return Fault::BadSlot; + const bool in = v != 0; + if (!read_field(slot, 8, 7, &v)) return Fault::BadSlot; + const int64_t pt = v; + if (!read_field(slot, 16, 8, &v)) return Fault::BadSlot; + float duty = 0.0f; + { + const uint32_t bits = static_cast(v); + std::memcpy(&duty, &bits, 4); + } + int64_t et = 0; + if (!read_field(slot, 24, 7, &et)) return Fault::BadSlot; + int64_t q = 0; + if (in) { + et += dt; + if (pt > 0 && et >= pt) { + et -= pt; // 周期回绕 + } + const int64_t hi = static_cast(static_cast(pt) * duty); + q = (et < hi) ? 1 : 0; + } else { + et = 0; + q = 0; + } + if (!write_field(slot, 24, 7, et)) return Fault::BadSlot; + if (!write_field(slot, 20, 0, q)) return Fault::BadSlot; + return Fault::None; + } + if (fb_id == 14) { // RTC:enable@0 date@4(DATE) tod@8(TOD)(虚拟时钟) + if (!read_field(slot, 0, 0, &v)) return Fault::BadSlot; + const bool enable = v != 0; + if (enable) { + const uint64_t total_ms = cycles_total_ * static_cast(dt); + const uint32_t day = static_cast(total_ms / 86400000ull); + const uint32_t tod = static_cast(total_ms % 86400000ull); + if (!write_field(slot, 4, 4, day)) return Fault::BadSlot; + if (!write_field(slot, 8, 4, tod)) return Fault::BadSlot; + } return Fault::None; } - return Fault::BadOp; } +// ---- 比较结果(模板:cmp_result)---- + +template +int64_t Machine::cmp_result(const char* name, T a, T b) { + if (std::strcmp(name, "CMP_EQ") == 0) return (a == b) ? 1 : 0; + if (std::strcmp(name, "CMP_NE") == 0) return (a != b) ? 1 : 0; + if (std::strcmp(name, "CMP_LT") == 0) return (a < b) ? 1 : 0; + if (std::strcmp(name, "CMP_LE") == 0) return (a <= b) ? 1 : 0; + if (std::strcmp(name, "CMP_GT") == 0) return (a > b) ? 1 : 0; + return (a >= b) ? 1 : 0; // CMP_GE +} + } // namespace vm