TermiNexus
632bc877df
feat(window): 鼠标捕获与窗口最小化/最大化
...
鼠标:
- 内容区按下后记录 capture_win,按住期间的移动仍投 HOVER、松开投 RELEASE
(即使光标移出窗口)。此前 btn_down != 0 时完全不发 HOVER,滚动条
拖动拿不到位置更新。
- 标题栏按钮按下态改为通用 btn_win/btn_kind,支持关闭/最小化/最大化。
窗口:
- WinSlot 新增 maximized、restore_*、fb_cap 字段
- 最小化:隐藏窗口并把焦点交给最高可见窗口;任务栏点击恢复,
再点一次已聚焦窗口则最小化
- 最大化:保存还原几何,铺满工作区(不含任务栏)并发送 RESIZE 事件;
再次点击还原;拖动最大化窗口会先还原
- 像素缓冲按 fb_cap 复用,仅在容量不足时从 arena 扩容,避免反复
最大化/还原耗尽 bump arena
- 1920x1080 最大化需要约 7.3MB 内容缓冲:DESK_SHM_SIZE 8MB → 32MB
2026-10-27 12:43:19 +08:00
TermiNexus
859197e5e9
feat(kbd): USB 键盘按住自动重复
...
USB HID 键盘只在状态变化时上报;a3c1d22 为避免 Enter 洪流加入整份报告去重后,
按住不放不再产生任何字符。
- 记录当前按住的非 CapsLock 键与下一次补发时刻
- 同一份报告重复到达(按键仍按住)时,按 500ms 首次延迟、33ms 间隔
重新走 _decode_press,注入与按下沿相同的 ASCII/ANSI 字节
- 松开或换键时更新/清除重复目标;CapsLock 本身不重复
2026-10-27 12:43:19 +08:00
TermiNexus
b8ca4826a9
chore(kernel): 移除帧缓冲参数的影子副本校验
...
fb/width/height/pitch 偶发被写坏的根因(页表/1GB 大页拆分时写坏内存)已修复,
不再需要 fail-fast 影子防御:
- 删除 _ShadowFb/_ShadowW/_ShadowH/_ShadowPitch 与 _SetFrameShadow、_CheckFrameLocals
- 删除 after_usb / after_vga / pre_devfs_fb0 / pre_explorer 四处校验调用
- devfs_register_fb0 恢复直接使用 __init__ 里的局部几何
(原先为规避局部量被写坏,改为从 BootInfo 重取)
2026-10-27 11:54:41 +08:00
TermiNexus
f43951c1f8
fix(fat32): 以 . / .. 结尾的路径会误开第一个子目录
...
dir_open_path 在 follow_path 返回后仍按“父目录中的最后一项”回读目录项。
当路径以 “.” 或 “..” 结尾时,follow_path 已经把 dp 打开成目标目录,
这段回读会把目标目录的第一个子项当成最终项再打开:
根目录 cd .. → full=/.. → follow_path 打开根 → 回读根的第一项 APP
→ 实际进入 /APP,于是 ls 显示的是 app 里的 helloworld.asm。
现在检测末尾的 “.” / “..”,与根路径一样直接返回已打开的 dp。
2026-10-26 23:10:38 +08:00
TermiNexus
0c47d81767
feat(diag): 异常现场完整转储(裸串口通路 + 无错误码向量)
...
- isr.s:ISR_NOERR 也保存 RIP/CS/RFLAGS/RSP,_fault_err 用 -1 表示无错误码;
两类宏都补存 rbx/rbp/r12(用户态 #PF 时 r12 常为出错 store 的目标对象基址)
- idt.vp:新增不经驱动/缓冲的裸串口输出(asm.outb),打印 rip/rsp/cs/flags、
u 位、callee-saved、错误码;#UD 附带出错指令字节,#PF 附带 cr2
- 前 32 条异常打印完整现场,之后每次只补 '*',避免异常风暴刷爆串口
2026-10-26 22:41:14 +08:00
TermiNexus
8c31645c0f
fix(gfx): floorf/ceilf 参数显式收窄为 float
...
min/max 是 double 内建,结果直接传给 floorf/ceilf 时编译器禁止隐式
double->float,实际会把 f64 的低 32 位当 f32 传参,使三角形包围盒取值全错。
内核 vesafb 与 Scene3D 两处光栅化同步修正。
2026-10-26 22:41:09 +08:00
TermiNexus
624554df01
fix(sched): 通过 rdi 传递用户线程入口参数
...
用户线程入口的第一个形参在 rdi,但 drop_to_user_mode 这条路径从未设置过 rdi,
thread_create(fn, arg) 的 arg 在用户侧一直是垃圾值。iretq 只恢复
RIP/CS/RFLAGS/RSP/SS,因此在 iretq 前显式 mov rdi 即可。
2026-10-26 22:40:10 +08:00
TermiNexus
3583f78efc
refactor(kernel): 适配 ViperC 新语法
...
- 去掉不再需要的 import t, c
- class → struct(纯数据聚合)
- t.CArray[T, N] → [N]T
- static 修饰符移到类型前
- @__attribute(packed) → @__attribute__(packed)
- UHCI 中断回调参数改用 callable[[], int]
- 同步 CPUID 注释中的旧类型名
2026-10-26 22:39:56 +08:00
TermiNexus
e9d8024138
fix(sched): 压缩 _do_switch 注释,规避内联汇编 2047 字节静默截断
...
llc 报 <inline asm>:55:13 invalid operand + 孤立 mov 的真因:ViperC 的 _asm_append_cstr 只写入 ASM_TEXT_BUF_SIZE-1=2047 字节且静默丢弃后续内容。_do_switch 的 asm 模板(含 CRLF)为 2245 字节,第 2047 字节正好落在 mov [rsi + 40], rcx 的 mov 之后 ⇒ 生成的汇编串以孤立 mov 结尾,popfq/pop/ret 全部丢失。压缩块内 4 条中文注释(模板降到 1894 字节),指令与 clobber 未动。附:sha1_map 第 57 行 3e23dc8764e4a4dd:sched/sched.vp 与报错哈希一致;全仓 16 个含 asm 的 .vp、189 个模板中仅此一处超过 2047 字节。
2026-10-17 17:36:45 +08:00
TermiNexus
89385d1b42
fix(sched): 恢复立即数 store 的 qword ptr(去掉后操作数大小有歧义)
...
上一笔把 sched.vp 里所有 qword ptr 都去掉了,误伤 _thread_entry 构造初始栈帧的 8 条立即数 store:mov [rax - 64], 0x202 这类'内存←立即数'无法由另一侧推断大小,报 ambiguous operand size。寄存器配对的 mov(rcx/[rsi]、[rdx]/rcx 等)保持无 ptr 写法,立即数 store 恢复 qword ptr。
2026-10-17 17:21:20 +08:00
TermiNexus
937b51a0c5
fix(sched): 内联汇编去掉 qword ptr,操作数大小改由寄存器推断
...
llc 集成汇编器在 Intel 语法下对 mov rcx, qword ptr [rsi] 报 Expected 'PTR' or 'ptr' token(列号正落在 ptr 处),而生成的 .ll 文本本身是对的。改写为 mov rcx, [rsi] / mov [rdx], rcx / mov [rax], rsp 等形式:内存操作数大小可由另一侧寄存器(rcx/rsp)推断,语义完全等价。
2026-10-17 17:19:23 +08:00
TermiNexus
7d8a4ee6ff
fix(sched): 内联汇编改用 lea + 间接寻址,绕开 [rip + sym + 偏移] 不被集成汇编器接受
...
llc 集成汇编器对 mov qword ptr [rip + _isr_scratch + 8], rcx 这种'RIP 相对符号 + 常量偏移'的直接寻址报 invalid operand(把整条指令识别成缺源操作数)。改为先 lea rsi, [rip + _isr_scratch](lea 形式此前已验证可用),再按 [rsi + N] / [rdx + N] 访问,共 12 处(保存 6 + 恢复 6)。rsi/rdx 都在 _do_switch 的 push 列表内,切栈后写入会被随后的 pop 恢复,不影响新线程寄存器。
2026-10-17 17:17:28 +08:00
TermiNexus
645e4015ca
feat(sched): 线程切换保存/恢复 FPU/SSE(fxsave/fxrstor)
...
混合抢占后中断帧只保存通用寄存器,异步抢占可能落在 SSE 表达式中间,FP 密集 app(Gargantua/Scene3D)的 XMM 会被其它线程覆盖。① Thread 末尾内嵌 fpu_area[528] + fpu_ptr,用 (base+15)&~15 手工对齐出 512B FXSAVE 区(编译器类装饰器只认 packed,不认 aligned);Thread 由 64B 变为 600B,MAX_THREADS=16 ⇒ 线程表 9600B,Scheduler 约 9624B。② _do_switch:切 rsp 之前 fxsave [old],换栈后 fxrstor [new];仅用 RAX 取地址(其值已压栈),不改动 GPR 与中断状态。③ 新线程 FPU 默认镜像直接写内存(512B 清零、FCW=0x037F、MXCSR=0x1F80、MXCSR_MASK=0xFFFF),不执行 fninit 以免破坏创建者的实时 XMM。④ 内嵌于 Thread,随静态存储存活,无需额外回收。⑤ 协作式 yield 与抢占共用 _do_switch,因此两条路径都正确隔离 SSE。⑥ _thread_entry 的硬编码偏移同步为 +9600/×600,并加 fail-fast 校验(不符立即 FATAL,不静默跑飞)。
2026-10-17 17:15:14 +08:00
TermiNexus
f2d8e771c2
feat(sched): 协作 + 时间片抢占(IRQ0 返回用户态时切换)
...
原先 timer_tick 只置 needs_reschedule,唯一消费点是 SPAWN 阻塞循环,纯计算线程永不被切走。① 抢占点:isr32_handler 在 timer_handler(含 pic.eoi(0)/apic.eoi)返回之后、且 from_user != 0(ISR 入口读 CS 低两位)时调用 try_reschedule;栈切换仍在 _do_switch。只对 Ring3 返回路径生效,内核临界区/syscall 中途不切。② 中断帧保留在被切线程内核栈上,靠 rsp 切换自然保存/恢复,不拷贝、不在切换中途 iretq。③ 修正全局 _isr_scratch 跨线程覆盖:ISR_SAVE_CLOBBERED 把 rax/rcx/r8-r11 存在全局 scratch,抢占后跨线程会互相覆盖;_do_switch 现按 tid 保存/恢复(_isr_scratch_tbl[16*6])。④ ISR 走中断门 IF=0,_if_saved=0,切回后不 sti,由 iretq 用用户 RFLAGS 恢复;_yield_lock 防嵌套。⑤ 单核前提(_sched_ptr/tick_count 非 per-CPU)。⑥ yield_now 语义、SCHED_QUANTUM=10、Gargantua 分块均未改。已知缺口:中断帧未保存 XMM/MXCSR,FP 密集 app 在异步抢占下有 XMM 被覆盖风险,待补 fxsave/fxrstor。
2026-10-17 17:08:46 +08:00
TermiNexus
a3c1d221e6
fix(kbd,sched,scene3d): 键盘报告去重;用户线程 return 跳板修正;关窗时序
...
① Enter 洪流:usb_kbd._on_int_complete 的按下沿依赖 kbd_prev_keycodes,而状态提交在函数末尾第三趟循环;kbd_report 是常驻 DMA 缓冲,按键保持/NAK/重复完成都会让回调反复看到同一份报告 ⇒ 每次中断都注入一次 Enter,Terminal 刷出 [cmd] enter,explorer 被拖死。新增 kbd_prev_sig(modifier+6 keycode 的标量签名)整份报告去重;同一趟循环里先提交 prev 再判定;全 0 报告只更新状态;抽出 _rearm() 保证所有出口都重排中断 IN 且 DMA 读取先于重排。② explorer 限流:kb_pend[64] + _deliver_kb,单轮最多投递 64 字节,ring_push 返回 -1 时停在当前字节(不再当成功丢弃),pending 清空才补读;鼠标取包同样 64 包/轮上限。③ 关窗卡死根因:process.vp 的 _tramp_code 是 mov eax,34; int 0x80; jmp \$,而 Syscall.THREAD_EXIT=60 ⇒ 用户线程 return 后走 syscall 34(无匹配分支)返回 Ring3 在 jmp \$ 空转,空转不再产生 syscall,而内核只在 syscall 返回路径消费 needs_reschedule ⇒ 协作式调度彻底停摆,整机冻结。sched.create_user_thread 在跳板映射后把立即数低字节改为 60(幂等)。④ 次生:主线程先退会立即回收自己的用户栈,而 Scene3D 渲染线程还引用其栈上对象。Scene3D 渲染线程退出改为显式 process.thread_exit() 并置 _render_done;主线程 mainloop 返回后先 yield 等渲染线程退出(上限 10000 次)再 exit。⑤ window.flush 在槽位 used==0 时直接返回;vpui.Tk.redraw 入口判 win_id 与 is_active,窗口已回收时不碰 arena 不 flush。
2026-10-17 16:49:37 +08:00
TermiNexus
7e7204a09a
fix(mm,explorer): slab 页严格归属与分配位图;关闭按钮按下态与松手关闭
...
① [mm] BAD blk 的解码:site=2=free_block、reason=1=magic 失配;blk 实际是 slab_page+16(页在 0x1b56020),sz 是该页 free_head,而非块大小。被踩的是页头前 8~16 字节,最可能是无锁 buddy 在 usb.init(sched.enable 之后)与用户进程 MMAP 并发 malloc 时重复授予同一 8KB 块。② mm 加固:_slab_find_page 不再只看 magic,新增 _slab_page_owns(block_ptr 回指、指针在对象区内、按 obj_size 对齐、序号 < total_objs);slab_page 新增 alloc_bits,只有已分配对象可 free,重复释放/野指针被拒,避免 free_count 提前到 total_objs 把仍在用的页拆回 buddy;free_obj 拒绝已全空继续收对象;_create_page 失败路径清零 magic。③ hid_ascii 仅只读且已有边界检查,非本次堆损坏来源;usb_kbd 把 code >= 64 改为显式早返回。④ 关闭按钮:按下只置 close_down 并置脏(按钮画按下态:CLASSIC 40,40,70→20,20,45;WIN7/XP 红底 200,50,50→140,25,25;WIN10/11 补深灰底),松手且仍命中才发 CLOSE + used=0,移出则撤销按下态恢复;几何复用 _close_btn_* 同一套函数。
2026-10-17 16:23:44 +08:00
TermiNexus
eb953bb1d3
feat(keyboard): 接入 USB 键盘输入到 /dev/input/keyboard
...
现场证据:keyboard.init 的 0xF4 被 ACK,但按键时 [kbd] first IRQ1 从不出现;同时 [usb] device addr=1 class=03 proto=01 ep=1 说明真实按键来自 USB 键盘。usb_kbd 已通过 UHCI 中断回调收到报告并生成 kbd_buf 事件,但全仓无人消费(usb_kbd_thread 从未启动),所以 /dev/input/keyboard 永远读不到 USB 按键。① keyboard.vp 新增 inject_byte/inject_csi/inject_csi_tilde 三个公开注入函数,复用 _out_push/_emit_csi,不改 read_scancode/has_data/irq_handler 语义。② usb_kbd.vp 在 _on_int_complete 的按下沿(与上一帧 6 键码比较后新出现的键)按 boot protocol(枚举时已 SET_PROTOCOL(0):modifier+reserved+keycode[6])解码成 ASCII/ANSI,注入同一 out_buf:字母数字含 Shift/CapsLock、Enter\\n、Backspace\\b、方向键 ESC[A/B/C/D、Home/End ESC[H/F、Delete/PgUp/PgDn/Insert ESC[3~/5~/6~/2~,与 PS/2 路径一致;按住不重复、松开不产字节。③ 不新建线程、不启动 kbd_thread,USB 与 PS/2 共用同一队列,中断上下文内写入(IF=0)。④ 加一次性诊断 [usb_kbd] first key。
2026-10-17 15:43:48 +08:00
TermiNexus
4195b05f75
diag(keyboard): IRQ1 首达与 0xF4 ACK 打点
...
键盘仍无字符。加两条一次性诊断:① irq_handler 首次触发打 [kbd] first IRQ1(判断中断是否送达);② init() 的 0xF4 扫描使能命令按 ACK 结果打 ok/not ACKed(判断命令是否送到键盘)。两者组合可区分'命令没送达'、'中断没到'、'解码丢字节'三种情况。
2026-10-17 15:33:55 +08:00
TermiNexus
0f235d0d0b
fix(keyboard): 移除 devfs 注册时的重复 keyboard.init()
...
键盘只收到一个残留字节(ch=50)后就没有数据,符合'数据上报被关掉'。keyboard.init() 被调了两次:devfs_register_keyboard() 与 services/keyboard.vp 的 start()。init() 会读-改-写 8042 CCB 并安装 IRQ1,二次读改写可能把键盘时钟/数据上报关掉。改为只在 service 层初始化一次。
2026-10-17 13:54:52 +08:00
TermiNexus
03e1137189
fix(keyboard): 去掉重复初始化,键盘只由 service 层 init 一次
...
现场:explorer 能 open /dev/input/keyboard,但按下键后 [explorer] kbd 一次都不出现 —— 内核侧没有数据。devfs_register_keyboard() 与 services/keyboard.start() 各调了一次 keyboard.init(),而 init 会读-改-写 8042 的 CCB(含数据上报/中断掩码位);第二次执行时设备状态已变,容易把上报关掉。现在初始化只保留 service 层那一次(它更晚、是最终状态);kbd_thread 并未被启动(kbd_svc.th 从未创建),不存在抢数据的问题。
2026-10-17 13:36:38 +08:00
TermiNexus
850c4b8adc
fix(keyboard,explorer): 键盘解码单队列化消除读循环自旋;关窗清理悬垂状态
...
① 卡死/键盘无效:has_data() 把'原始待解码'与'已解码可读'混成一个计数,而 read_scancode() 只看已解码队列;一旦出现 has_data()>0 但本次取不到字节(断码/修饰键/E0 前缀),devfs 的读循环里 count 不前进而 has_data() 恒真 ⇒ asm.sti() 后无 yield 自旋,协作式调度下整机停死。改为解码下沉到 IRQ、只保留一个输出队列(has_data()==out_count,read_scancode() 返回 0 ⟺ 队列空),ANSI 序列整条原子入队;devfs 读循环以 read_scancode() 返回 0 为唯一终止条件。② 关窗:DESTROY 与关闭键路径清 drag_win,focus/_activate_from_taskbar 校验槽位存活;poll_requests 单轮最多处理一圈(head 已推进,剩余留到下轮,不丢消息也不自旋)。③ vpui:孤立 ESC 后紧跟的普通键不再被吞。
2026-10-17 13:20:22 +08:00
TermiNexus
266e6fbcfb
feat(keyboard): 恢复 Shift/CapsLock 与方向键等特殊键
...
旧内核桌面 desktop.vp 里有完整的键盘解码(修饰键状态、特殊键映射、重复),S3e 下沉后丢了,只剩一个简化 scancode_to_ascii(shift/caps 字段从未赋值、方向键返回 0 被丢)。① 内核 keyboard.vp:新增解码层 _decode_scancode + 输出队列,跟踪 Shift(0x2A/0x36)/CapsLock(0x3A)/Ctrl/Alt,Shift 输出大写与上档符号、Caps 只翻转字母区;E0 前缀的独立方向键/Home/End/Delete/PageUp/PageDown/Insert 输出 ANSI 序列(ESC[A/B/C/D、ESC[H/F、ESC[3~/5~/6~/2~);释放码只清状态不产字符。devfs 的 ASCII 字节流契约不变。② vpui:Tk 新增 _ansi_state/_ansi_param 与 _fold_ansi_key(),在 KEY 分发前折叠 ANSI 序列,映射到既有 VK_* 语义(Text/Entry 里那些分支不再是死代码)。Trim:Insert 无 VK_INSERT 定义、被吞掉;typematic 重复未恢复;USB 键盘未涉及。
2026-10-17 13:01:56 +08:00
TermiNexus
66dc11364a
fix(keyboard): 断码不再产出字符,修正每个键输出两个字符
...
scancode_to_ascii 先剥掉 bit7(KEY_RELEASED)再查表,导致按键释放也被翻译成同一个 ASCII —— 打 ls 得到 llss。断码直接返回 '\\0'。
2026-10-17 12:00:26 +08:00
TermiNexus
844fbff31c
fix(kernel,explorer): 内核不再画屏消除残留;CREATE 打印 style 诊断
...
蓝色残留根因:main.vp 里 VGA_drv.Print 在 __init__ 尾部执行,而它晚于 app spawn —— explorer 已经把桌面画上去了,内核随后又往 framebuffer 打一行测试文本/清屏,于是永远盖在桌面上(整屏刷一次也救不了,因为之后又被画)。S3e 之后屏幕由 explorer 独占合成,直接删除内核侧 VGA 驱动创建与 Print。另:handle_request 的 CREATE 分支加一条诊断打印(style/w/h),用于确认 style 是否真的透传到 explorer —— 5 条日志,定位后撤。
2026-10-17 11:03:05 +08:00
TermiNexus
e1d31c3dc9
diag(explorer,fb0): vblank 返回等待迭代数并打点;脏行合并为一段渲染
...
排查'仍闪烁':① FB_WAIT_VBLANK 改为返回等待消耗的迭代数(到上限 = 超时 = 当前没有 VGA 回扫信号),explorer 首次调用打印一次 —— 用来判定垂直回扫同步是否真的生效;② render() 原来按连续脏行分段、每段各渲染一次,同一帧的多次写屏会跨越多个扫描周期,现在把脏行合并成一段连续范围一次性渲染,缩小撕裂窗口。
2026-10-16 21:55:36 +08:00
TermiNexus
e41701e201
feat(fb0): 新增 FB_WAIT_VBLANK ioctl,合成前等垂直回扫
...
explorer 直接写 framebuffer 会与显示器扫描并发,造成撕裂/闪烁;而它在 Ring3 没有 IO 端口权限,不能像历史内核桌面那样自己 inb 0x3DA。所以在 /dev/fb0 上加 FB_WAIT_VBLANK(0x4601)由内核代等垂直回扫(带超时,无 VGA 时序时不死等),vpsdk 暴露 fb_wait_vblank(),desk_server.render() 写屏前调用一次。
2026-10-16 21:37:58 +08:00
TermiNexus
89c37cea22
fix(mouse): Y 增量取反成屏幕方向,修正上下移动反向
...
PS/2 协议的 Y 轴正方向是向上,而屏幕坐标 Y 向下为正,内核出口少了一次取反 —— 现场表现为鼠标上下移动反向。在 devfs 的 mouse_device.read 里统一转换成屏幕方向(消费方直接累加即可),与历史 desktop.vp 中 pkt_dy 的用法保持一致。只改内核,explorer 的 ELF 无需重编。
2026-10-16 21:33:31 +08:00
TermiNexus
6eb000d01d
fix(mouse): 鼠标设备改返回相对位移,光标位置归 explorer 累积
...
现场:鼠标一有输入,光标立刻跳到左上角。根因:devfs 的 mouse_device.read 在 size>=9 时填的是 mouse.get_x()/get_y(),而 mouse_x/mouse_y 这两个静态量从未被更新(irq_handler 只把解码后的包入队 packets[]),恒为 0;explorer 拿到 (0,0) 就照抄成光标位置。改法:内核只给出 PS/2 包里的相对位移 dx/dy(uint8 原始字节手工做有符号扩展,不依赖 int8 转换语义),光标位置作为 explorer 自己的状态累积并做边界钳制 —— 内核不知道屏幕几何,钳制本来就该在图形壳这一层。
2026-10-16 21:23:05 +08:00
TermiNexus
453c353e01
fix(sched): sched_yield 改为无条件切换,原实现只在时间片到期时才切
...
现场:app 用 4000 次 yield_cpu() 等 /dev/desk ready(explorer 负责 format 并置 ready=1),却全部超时——三个 app 都打出 [win] desk not ready (attach timeout) / create_window: connect FAILED / geometry win_id=-1,最后静默退出;而 explorer 的 [elf] wrapper pid=5 出现在所有 app 退完之后。根因:YIELD syscall 走 sched.Scheduler.try_reschedule(),而它只在 needs_reschedule(时间片到期,由 tick 置位)时才真正 _yield(),因此 N 次 sched_yield 全落在同一个时间片内一次都不切换 ⇒ 主动等待外设的进程被饿死、初始化者永远轮不上。修法:新增 Scheduler.yield_now() 无条件切换,YIELD 分支改用它;try_reschedule() 仍保留给中断/抢占路径使用。
2026-10-16 20:57:55 +08:00
TermiNexus
2dacfa85fb
fix(input): 启用 PS/2 鼠标初始化,桌面鼠标通路恢复
...
main.vp 里 mouse.init() 一直被注释,而 services/mouse.vp 的 start() 只做 USB 鼠标初始化 ⇒ PS/2 鼠标的 IRQ12 从未注册、采样率与数据上报从未使能。表现是 explorer 能 open /dev/input/mouse(fd=3)却永远收不到事件,桌面有键盘无鼠标。恢复调用。
2026-10-16 20:51:01 +08:00
TermiNexus
48017a3d66
chore(devfs): 清理定位期临时探针,directory_count/file_count 恢复 static
...
main.vp:删除 devfs 注册结果回读([main] devfs reg / files[k] / find_in_dir / serial0(before fb/desk) fd)、fb0 注册后回读([main] fb0 reg)、desk 共享区地址比对([main] desk shm)、serial0 查找结果([main] devfs serial0 fd)共 6 组诊断打印,去掉不再使用的局部量。保留 _CheckFrameLocals 系列(fail-fast 断言,不是探针)。devfs.vp:删除 framebuffer_device.ioctl 内的两条 [fb0] ioctl 诊断打印与随之无用的 import viperlib;directory_count/file_count 恢复 static(跨模块读取点已随探针删除)。fat32_types.vp 的 winread FAIL / dir read failed 保留为正式错误上报(错误必须立即可见),未改动。
2026-10-16 20:45:40 +08:00
TermiNexus
cfb76b1b94
fix(ide): PIO 数据就绪只认 DRQ,并在发命令前/失败后清掉残留扇区
...
现场(serial.log 113-136):winread FAIL abs=8192 res=1,紧接着读根目录扇区 8192 读回 ELF 文件头 ⇒ 目录项全是垃圾 ⇒ Hello/Terminal/Gargantua 三个 spawn 连锁失败,之后又自行恢复(间歇)。两个成因:① waitDataReady 把 DRDY 当作数据就绪(DRDY 空闲时恒为 1),在 BSY 刚清、DRQ 未置的窗口里误判成功,随后从 0x1F0 读到上一次传输的残留;② 一旦某次读提前失败返回、数据没被取走(DRQ 仍置位),ATA 规定设备会忽略命令块寄存器写入 ⇒ LBA 保留旧值 ⇒ 后续每次读都拿到同一块残留缓冲,形成连锁污染。改法:waitDataReady 改为必须 !BSY && DRQ;新增 flushStaleData(),在 readSector/writeSector 发命令前以及失败返回前把残留扇区取走丢弃。未改 includes,无需三重编译。
2026-10-16 20:39:56 +08:00
TermiNexus
4d0f29d533
fix(devfs): create_file 缺对象时直接失败,不再自建栈实例节点
...
把 files[] 改成指针数组后,else 分支的 new_file: devfs_file* = devfs_file() 暴露为栈实例逃逸,编译器以 LifetimeError 拦截(stack address escapes)。改为缺对象一律 return -1:设备节点必须由调用方以长寿命实例(模块级/堆)提供,不静默造占位对象。现有调用点(devfs_register_serial0/serial1/keyboard/mouse/fb0/desk)全部传对象,行为不变。
2026-10-16 20:34:02 +08:00
TermiNexus
441ea0c883
fix(devfs): files[] 改存指针,避免设备对象按值切片丢掉派生字段
...
现场:main.vp 探针读模块级 fb0_file 得 w=1360 h=768 pitch=5440(注册正确),而 explorer 侧 [fb0] ioctl 的 self 读得 w=0 h=0 pitch=0 fmt=0。IR 证据(devfs 编译单元 .text.ll 的 create_file):files[file_count] = *file_ptr 被生成成 llvm.load/llvm.store 整个 devfs_file 结构体(88 字节基类布局),派生字段 fb_phys/fb_size/fb_w/fb_h/fb_pitch/fb_format 从未被写入,保持 __init__ 的 memset 0;vptr 一并拷过去所以虚调用仍能进 framebuffer_device.ioctl,但读到的派生字段全是 0。这是对象切片而非编译器缺陷。
改法:files 由 [MAX_DEVICE_FILES]devfs_file 改为 devfs_file* 指针数组,create_file 只登记指针(设备对象由 devfs_register_* 以模块级实例持有,生命周期贯穿内核运行期);find_in_dir/list_in_dir 相应去掉取址;__init__ 的 memset 长度改为指针数组真实大小 8*MAX_DEVICE_FILES(原来按 devfs_file.__sizeof__() 清会越界清掉后面全局量)。顺带修好同样依赖派生字段的 /dev/desk(shm_device.shm_phys/shm_size)。
2026-10-16 20:32:12 +08:00
TermiNexus
3c595527bc
test(fb0): 注册后立刻回读设备对象字段的临时探针
...
区分两种真因:devfs_register_fb0 传参即为 0,还是注册正确后设备对象字段被踩成 0。_CheckFrameLocals 只校验 __init__ 的长寿命局部量,管不到设备对象里的字段,因此补一条紧跟注册之后的回读探针,与 explorer 侧 [fb0] ioctl 的 w/h 对读即可定位
2026-10-16 20:23:58 +08:00
TermiNexus
0fd1bf7567
fix(fb0): 几何登记为 0 + FbInfo 按值返回只回传一半 —— explorer 拿不到 framebuffer 几何
...
现场(serial.log 330-339):[fb0] ioctl fd=6 req=0x4600 arg=0x7fffffdffb78 w=0 h=0 ⇒ request 与 arg 传递都正常、写入回读也正常,真正的问题是设备对象里的 fb_w/fb_h/fb_pitch/fb_format/fb_phys/fb_size 全为 0;随后 explorer 以 pitch=0 调 mmap 失败退出。两个独立缺陷:① 内核侧——devfs_register_fb0 收到的是被清零的 width/height/pitch/fb_format/fb_size(main.vp 顶部注释记录的"__init__ 的 fb/width/height/pitch 局部量被偶发写坏"同类问题:这些长寿命局部量在 L121 之后、L304 之前被写坏,而 L366+ 的 _CheckFrameLocals 又不报,属时点相关),改为在调用点直接从 BootInfo 重取几何并补一条 _CheckFrameLocals(pre_devfs_fb0);② SDK 侧——fb_get_info 按值返回 32 字节 FbInfo,现场表现为只有前 16 字节有效、后 16 字节是调用方栈残留(size=1802724708 phys=0x130c30),改为出参 out + 返回 ioctl 返回码;③ explorer 侧改为判返回码与 pitch/size 非 0,失败即 _dbg 说明原因后 fail-fast 退出,不再带着 0 几何继续。
2026-10-16 20:20:36 +08:00
TermiNexus
35fc8b8ea8
chore(devfs): 临时探针 - fb0 ioctl 打印收到的 request/arg 与写入回读,定位 explorer fb 信息全 0
...
现场:explorer 打印 [explorer] fb 0x0 pitch=0 fmt=0 size=<垃圾> phys=0x130b90 —— 6 个字段里 3 个为 0、其余是残留,判定「这次 ioctl 没有真正写入用户缓冲」而非写错地址。两种可能:(a) syscall 派发把 request 传成别的值 ⇒ ioctl 直接 return -1;(b) 写入落在用户 VA 之外的地址空间。本探针在内核侧打印 fd/req/arg 与写入后回读的 w/h/pitch/fmt:若 req 不是 0x4600 ⇒ (a);若 req 正确且回读正常而 app 仍是残留 ⇒ (b)。app 侧应同时检查 ioctl 返回值(非 0 时绝不能用未写入的 info),下一步一并改。
2026-10-16 20:17:50 +08:00
TermiNexus
14011b1f2d
fix(devfs): find_child/list_subdirs 的 dirp 漏了指针星号,导致 devfs_open 返回 -1
...
现场:devfs_register_* 之后 files[] 计数与名字全对、find_in_dir 正常,但 files[k].parent_dir 被写成 0x7d218(内核启动栈区 0x60000-0x80000),devfs_open(/dev/serial0) 一律 -1,用户态 app 因此建窗失败、起来就退。根因:devfs_directory.find_child 里 dirp 标注为值类型 devfs_directory 却赋 &directories[d](指针),list_subdirs 同样是这个 typo;编译器按值类型处理该变量,dirp.parent/dirp.name 读错位数据,return dirp 返回的是栈上残留值(0x7d218),偶然残留槽恰为 &directories[n] 时又看似正常(keyboard 的 0x135018 即此巧合),故表现为时对时错、多数错。改为 devfs_directory*(同文件其余 6 处 &directories[]/&files[] 本就带星号)。
2026-10-16 20:14:39 +08:00
TermiNexus
745c0e0b83
fix(devfs): directory_count/file_count 去掉 static,允许跨模块读取用于诊断
...
编译器报 NameError: drivers.devfs.devfs.directory_count is a static symbol, cross-module use is forbidden(static 仅定义模块内可见)。这两个只是注册计数,改为普通模块级变量即可让 main.vp 的临时探针读取;文件内原有的 global directory_count, file_count 声明语义不变。
2026-10-16 20:01:10 +08:00
TermiNexus
9469ee8b4d
chore(main): 临时探针 - 打印 devfs 注册计数/files[] 名字/直接 find_in_dir,三分 devfs_open=-1 的成因
...
devfs_open(/dev/serial0) 在仅注册 serial0/serial1/keyboard/mouse 之后就是 -1,且对象布局已由 MLIR 证明正确(vptr 在偏移 0、files[32] 按值数组、create_file 的路径切分与整份拷贝写法正确)。故加三条探针:注册后打印 directory_count/file_count、逐个打印 files[k].name/private_data/parent_dir、并用 files[0].parent_dir 直接调 find_in_dir(serial0)。判读:counts=0 或名字空 ⇒ 注册没落表;名字正常而 find_in_dir 返回 None ⇒ 名字比较路径坏;两者都正常而 devfs_open 仍 -1 ⇒ 封装层路径切分/返回值问题。
2026-10-16 19:59:47 +08:00
TermiNexus
73c29a5bd4
fix(fat32): 目录项读取失败时打印 dir read failed 现场并 fail-fast
...
dir_read_entry 里 win_read 失败原本静默返回 FR_DISK_ERR:错误虽有上抛,但没有任何现场,上层只能看到 find end res=1(当成找不到文件)或把 win[] 里的残留字节当垃圾目录项(name=ELF、attr=00)解析,无法判断根因是读盘失败。现改为显式打印 [f32] dir read failed sec/off/dirsec/res 后再返回 FR_DISK_ERR。
2026-10-16 19:49:01 +08:00
TermiNexus
887736d397
chore(main): 临时 bisect 探针 —— 在注册 fb0/desk 之前也查一次 devfs serial0
...
上一条探针已否掉重叠假设:desk shm=0xa781000(8MB)与 devfs_files=0x135140 / devfs_dirs=0x134cb0 相距极远,不重叠。本次再补一条 before 探针:若 “before fb/desk” 那行 fd>=0 而之后那行 fd=-1,即证明是本轮新增的 fb0/desk 注册把 devfs 表搞坏;若两行都 -1,则问题在更早(注册顺序/serial0 本身)。
2026-10-16 19:44:13 +08:00
TermiNexus
7f071b495a
chore(main): 临时量出 desk 共享区与 devfs 注册表地址,验证 mm 是否把堆分到内核 .bss 上
...
MLIR 已排除编译器布局问题:devfs_file = (ptr, array<64xi8>, ptr, ptr)(vptr 在 0、基类字段在前)、framebuffer_device 在其后追加 6 个字段、files[32] 按值数组 —— 全对、无重叠。于是头号嫌疑转为:devfs_register_desk 里那块 8MB mm.malloc 与内核 .bss(devfs 的 directories/files/directory_count/file_count)重叠,把注册表踩掉 ⇒ devfs_open 查不到设备 ⇒ fd=-1(且 serial0_open 不会被调、无 [serial0] open: 打印)。本探针打印 desk shm 地址/size 与 devfs_files/devfs_dirs 地址,一眼可判重叠。定位后连同上一探针一并删除。
2026-10-16 19:42:30 +08:00
TermiNexus
2481336dec
chore(main): 临时加 devfs serial0 查找结果探针,定位“用户态写串口无输出/设备查找失败”
...
症状:三个 app 启动即 [syscall] exit called(create_window 失败 ⇒ is_active(-1) 为 0 ⇒ mainloop 立即返回)、explorer 的 [explorer] start 一行都没有、内核自己的 devfs 冒烟输出([serial0] open/write)也全缺 ⇒ 三条都指向 devfs_open 查不到设备。探针:[main] devfs serial0 fd=N(<0 即 lookup 失败,serial0_open 不会被调、也就不会有 [serial0] open 打印)。定位后删除。
2026-10-16 19:38:25 +08:00
TermiNexus
d6d07b25ee
feat(s5): 删除 TELL/SIZE 私有 syscall —— 用 lseek 的 SEEK_CUR/SEEK_END 表达
...
内核:SEEK 补真正的 whence(0=SET 1=CUR 2=END,END 时 offset 为距末尾的距离,offset>size 返回 -1);删除 TELL/SIZE 的枚举项、派发分支与 TELL/fsize 函数(fat32.tell/size 仍留给 SEEK 内部用)。
SDK:vpsdk/fs.vp 的 seek() 增加 whence 参数(默认 0,调用方不变);tell() 改为 lseek(fd,0,SEEK_CUR);fsize() 改为三次 lseek 往返(记住位置 → 跳末尾取大小 → 复位),不改变最终位置。
私有段只剩 5 项:OPENDIR/READDIR/CLOSEDIR(openat+getdents64)与 LOAD_SO/SO_CALL1(动态库加载下沉)。
2026-10-16 19:33:46 +08:00
TermiNexus
c4305301fc
fix(vpsdk): 恢复 window.vp 对 vpsdk.stdlib 的 import(app 裸名 malloc 的链接来源)
...
现象:ld.lld 报 undefined symbol: malloc,引用来自 Apps/HelloWorld 的 _start(4 处)。根因:malloc 是 vpsdk/stdlib.vp 的 CExport 裸名,app 侧那些裸名调用要靠 window.vp 的 import vpsdk.stdlib 把 stdlib 拉进链接图;S3d 重写 window.vp 时把它当未使用删掉了 ⇒ stdlib 不再进图 ⇒ malloc 无人定义。
同时删除 main.vp 里上一轮加的两处说明注释(按用户要求不保留)。
2026-10-16 19:20:26 +08:00
TermiNexus
d96b632226
chore(main): 删掉内核身份下调 slog.so log_info 的启动冒烟调用
...
S4 之后 log_info 走 open(/dev/serial0)+write 两个 syscall,而该调用点在 devfs_init/devfs_register_serial0 之前 ⇒ 那条日志必然丢失。直接删除这个早期验证用的冒烟调用(动态库路径仍被 HelloWorld 的 dynlib.load_so/so_call1 与内核 LOAD_SO/SO_CALL1 覆盖)。同步更新上方的自启动说明注释。
2026-10-16 19:19:00 +08:00
TermiNexus
5e1cd04478
feat(s4): 删除 SERIAL_PUTS 私有 syscall,用户态改走 /dev/serial0
...
内核:删掉 SERIAL_PUTS 的枚举项/派发分支/处理函数(内核自身的 serial.puts 是直接驱动调用,不受影响)。SDK:vpsdk/devfs.vp 新增 serial_puts()(惰性 open /dev/serial0 一次 + write),syscall.vp 删掉该成员。
调用点全部切到 devfs.serial_puts:vpsdk/stdio.vp 的 printf、vpsdk/vpui.vp 的 _serial_puts、Apps/Terminal 的 _sdbg、Apps/Explorer 的 _dbg、Libs/SerialLogger 的 log_info/log_warn(.so 同样走设备节点)。顺带清掉因此失效的 import(vpui 的 _syscall、Terminal 的 _syscall、Explorer 的 syscall)。
至此私有段只剩 7 项:TELL/SIZE/OPENDIR/READDIR/CLOSEDIR(可由 lseek/fstat/openat+getdents64 表达)与 LOAD_SO/SO_CALL1(动态库加载待下沉用户层)。
2026-10-16 19:17:39 +08:00
TermiNexus
13eee8e8ea
feat(s3e)!: 内核 UI 层整体下沉用户态 —— 删 11 个 UI syscall + services/desktop.vp
...
内核 intr/syscall.vp:删除 WAIT_WINDOW/CREATE_WINDOW/DRAW_TEXT/GET_WINBUF/WIN_FLUSH/POLL_EVENT/SET_CURSOR/SET_TITLE/SET_GEOMETRY/SET_RESIZABLE/GET_WIN_SIZE 的枚举项、派发分支与对应函数;去掉 services.desktop 依赖;EXIT 不再拆窗口(内核已不持有窗口语义)。
删除 VKernel/Kernel/services/desktop.vp(~2900 行内核桌面/合成/输入分发);main.vp 不再启动 desktop 线程,只保留一次 fb 局部量一致性校验。剩余 syscall = Linux 对齐段 + 8 个待 devfs 化的私有项(SERIAL_PUTS/TELL/SIZE/OPENDIR/READDIR/CLOSEDIR/LOAD_SO/SO_CALL1)。
SDK:yield_cpu() 从 WAIT_WINDOW(0,2) 改为 Linux 对齐的 sched_yield(内核同样是 try_reschedule);syscall.vp 同步删掉 11 个 UI 成员。
explorer:关闭按钮改为先通知 CLOSE 再把槽位 used=0 —— window.vp 的 is_active() 见 used==0 返回 0,app 的 while is_active() 自然退出;像素缓冲不复用(bump 分配)⇒ 无别名风险。至此输入只剩 explorer 一家在读(内核桌面已删),两个合成器之争结束。
2026-10-16 19:11:44 +08:00
TermiNexus
bb82c2fe75
fix(desk)!: 事件环改为单环+按 pid 过滤,并补 getpid(S3d 前置)
...
缺陷:原设计把事件环按 win_id 索引,但建窗答复 CREATED 必须在 app 知道 win_id 之前送达 —— 按 win_id 索引物理上不可能送达。改为**单一事件环**,每条事件带目标 pid,由 app 用自己的 pid 过滤(与内核旧接口 poll_event(buf, caller_pid) 同口径)。
DeskMsg 增 pid 字段(ring_push/ring_pop 同步搬运,漏搬则事件全被当成 pid=0);desk_evt_ring_off() 去掉 win_id 参数;desk_format 只初始化一个事件环;服务端 push_event 从窗口槽取 pid、建窗答复走新增的 push_event_pid(此时槽里还没有 pid)。
新增 GETPID(Linux 号 39,此前是"留空"项,现在真正实现):内核 GETPID() 返回 process.current(),SDK 侧 process.get_pid()。app 需要它才能过滤自己的窗口事件。
2026-10-16 18:58:43 +08:00