【LC-3汇编实战】从键盘中断到操作系统雏形:一次完整的中断机制构建

📅 2026/7/14 13:27:58
【LC-3汇编实战】从键盘中断到操作系统雏形:一次完整的中断机制构建
1. LC-3中断机制全景解读第一次接触LC-3的中断机制时我盯着KBSR寄存器的IE位发了半小时呆。直到在仿真器里看到用户程序被键盘输入突然打断的瞬间才真正理解中断为何被称为操作系统的基石。LC-3虽然只有256个内存地址用于中断向量表但已经完整包含了现代操作系统的中断处理雏形。中断的本质是硬件对CPU的紧急呼叫。当键盘按下时硬件会做三件事设置KBSR的最高位准备就绪、检查IE位是否开启是否允许呼叫、若允许则通过中断向量x80发起呼叫。这时CPU会立即保存现场PC和PSR压栈跳转到中断服务程序ISR就像快递员突然放下手中的包裹去处理更紧急的签收。在LC-3上构建中断机制需要三个核心组件用户程序持续输出ICS字符串的普通任务键盘ISR处理键盘输入的紧急任务OS支持代码搭建舞台的幕后工作者实测中我发现一个关键细节LC-3的中断向量表起始地址是x0100但键盘中断向量x80对应的入口地址却要存放在x0180x0100 x80。这个偏移计算让我栽过跟头——有次误写成x0080导致中断永远无法触发。2. 用户程序被中断的苦力用户程序就像流水线上的工人机械地重复着输出工作。但为了让人类眼睛能看清输出必须加入延时逻辑。在LC-3上实现延时有个经典套路DELAY ST R1, SAVE_R1 ; 保存寄存器 LD R1, COUNT ; 加载倒计时值 LOOP ADD R1, R1, #-1 ; 计数器递减 BRp LOOP ; 正数则继续循环 LD R1, SAVE_R1 ; 恢复寄存器 RET SAVE_R1 .BLKW 1 COUNT .FILL #25000 ; 约0.5秒延时这个延时子程序通过空循环消耗CPU周期。我在调试时发现当COUNT设为50000时键盘响应会有明显延迟——这说明中断虽然能打断主程序但若ISR执行时间过长仍然会影响系统响应速度。用户程序交替输出两种ICS图案的设计也很巧妙ICS ICS ICS ICS ICS ICS ICS ICS ICS ICS ICS这种视觉差能直观验证程序是否在正常运行。有次我忘记在图案间插入换行符x0A结果屏幕显示乱成一团。3. 键盘中断服务程序低调的救火队员键盘ISR的工作流程就像消防员出警保存现场寄存器压栈检查KBDR获取输入字符通过DSR/DDR轮询方式输出字符不能用TRAP指令恢复现场寄存器出栈RTI指令返回最易出错的是第3步。LC-3的显示输出需要先检查DSR状态位POLL LDI R1, DSR ; 加载显示状态 BRzp POLL ; 未就绪则继续轮询 STI R0, DDR ; 就绪则输出字符我曾尝试去掉轮询直接输出结果字符丢失率高达30%。更坑的是ISR中若误用TRAP指令会导致无限递归——因为TRAP本身也是通过中断实现的。一个实用的调试技巧在ISR开始时先输出特定字符如#这样在终端能看到中断触发的准确时刻。有次我发现字符重复输出10次的逻辑失效最终排查是因为忘记初始化循环计数器R2。4. 操作系统支持代码看不见的脚手架没有操作系统支持的LC-3就像没有地基的房子。我们需要手动搭建三个关键设施4.1 栈空间初始化LD R6, STACK_TOP ; R6作为栈指针 STACK_TOP .FILL x3000x3000这个地址我修改过三次第一次x4000超出内存范围第二次x2000与程序地址冲突最终x3000才稳定工作。栈生长方向也很反直觉——LC-3的栈是向低地址增长的。4.2 中断向量表设置LD R1, VECTOR_ADDR LD R2, ISR_START STR R2, R1, #0 ; 将ISR地址存入向量表 VECTOR_ADDR .FILL x0180 ISR_START .FILL x2000这里有个隐藏知识点STR指令只能存储到PCoffset9范围内的地址因此必须先用LD加载基地址。我曾在STR指令里直接写x0180结果汇编器报错offset out of range。4.3 中断使能设置LD R3, IE_MASK STI R3, KBSR ; 设置中断使能位 IE_MASK .FILL x4000 ; 0100_0000_0000_0000x4000这个魔数让我困惑很久直到用二进制表示才明白它正是KBSR的第14位IE位的掩码。在调试时可以读取KBSR的值验证IE位是否设置成功。5. 中断机制的深层思考完成这个实验后我重新审视了中断对操作系统的意义。当用户程序在输出第五个ICS图案时被键盘中断打断现场保护流程如下PC当前指令地址压栈PSR程序状态寄存器压栈跳转到ISRISR执行完毕后RTI指令弹出PSR和PC这个过程实现了真正的无缝切换。我在PSR中特意设置了不同的优先级位验证了低优先级中断可以被高优先级中断嵌套的特性。中断向量表的设计更是精妙——通过硬件级的间接寻址使得操作系统可以在不修改用户程序的情况下动态更换ISR的实现。这让我联想到现代操作系统通过syscall表实现的功能热更新。最后分享一个血泪教训在同时调试用户程序和ISR时务必先用.ORIG明确指定各自的加载地址。有次我忘记写.ORIG x2000导致ISR代码覆盖了用户程序整个系统崩溃得莫名其妙。