第 66 环 · 撤回回执(把救回来的名字念出来)
第 65 环把删错的那一场救回来了:目录项写活、名字格写回、清单重排,ok=1 / sid / slot / cnt 一样不少。
可用户听到的只有「撤回成功」这类机器话——清单里多了一场,却不知道回来的是哪一场。对着唯一的对话窗口,这等于没说。
这一环让撤回也把名字念出来:「好,跑步计划回来了。」
名字本来就在现场。第 65 环删除时已经把「这一场自己的名字 6 字」写进了垃圾记录 0x220 的第 6~11 字;撤回引擎在恢复目录项时,也把这 6 字读进了片上的 nm0..nm5。回执不用回内存再找一遍——现场就在手里。
硬件自己排回执。rtl/undo_del.sv 新增 S_RCPT / S_RW 两态,在 restore + list 都成功之后:
6 + nlen + 12(nlen ≤ 12,最多 30 字节);0x230~0x23E(15 字 / 60 字节,空格补齐),与第 59/60/61/62/63/64 环共用同一个回执缓冲区;软硬接口。回执落点仍是 0x230;新增只读探针页 0x980 起:UD_BUSY / UD_DONE / UD_OK / UD_ERR / UD_SID / UD_SLOT / UD_CNT / UD_BYTES / UD_NAME / UD_RESTORED,0x984 SCREEN41;UD_NAME 的 bit5=named、bit4:0=nlen。ISA 一位没动,仍是 npu.undo(custom-2 funct3=011)。
名字只可能来自垃圾记录。删除时名字格已经被 free 清 0;撤回复原名字格是回执之后同一条链上的动作。测试台专门在删除后断言名字格为 0,再断言回执念出了名字——这条证据把「操作系统在软件层偷偷拼字」排除了。
单元仿真 tb_undo_del66(真 forget_sess + 真 sess_slots + 真 undo_del):
ok=1 / sid=0x92 / slot=2 / cnt=3,bytes=30 / named=1 / nlen=12,0x230~0x237 与「好,跑步计划回来了。」逐位相同,0x23F / 0x240 一个字没碰;bytes=24 / named=0 / nlen=0,0x230~0x235 与「好,这场回来了。」逐位相同;ok=0 / err=1,回执缓冲区原样不动(不闪屏)。ISA 协同仿真 tb_isa_undo66:程序 addi x5,0,2 → npu.forget x5 → npu.wait → npu.undo → npu.wait,再从 MMIO 0x9A0 读回回执字节数、写 SCREEN2。结果:ok=1 / sid=0x92 / cnt=3,UD_BYTES=30,UD_NAME=0x2C(named=1 / nlen=12),0x230~0x237 逐位对表,名字格删后被清、撤后写回,两次 npu.wait 都没冻死,程序走到 SCREEN=0x66。
回归:tb_undo_del(第 65 环老契约)、tb_isa_undo(第 65 环 ISA)、tb_min_soc64 全部复跑 RESULT=PASS;主通路 SCREEN=0xF905FD04 与第 25~65 环逐位一致(本环只在撤回引擎尾部加回执,计算面一个字没动)。
只有一个对话窗口的手机,删除和撤回都必须像人说话:说「删掉第二场」,AI 回「好,跑步计划删掉了。」;说「撤回」,AI 回「好,跑步计划回来了。」——名字在,用户才敢信这一场真的回来了。
这一环把「回执」也做进了接口契约:名字在删除时就被芯片留在现场,撤回时芯片自己把名字念出来。OS 仍然不备份会话、不拼 UTF-8、不读垃圾记录——芯片负责把现场收好、把话说明白。
撤回即续聊。这一环救回来了、也念出了名字,可用户还得再说一句才能接着聊。下一环让撤回后直接接上刚才那一场:名字念完,AI 就顺着上次的上下文说下一句。