
简介这份资源围绕8253定时器、8259A中断控制器与8255可编程并行接口的协同使用给出一个完整的中断实验方案面向学习微机原理、接口技术及汇编语言的中高年级学生与自学者帮助理解中断请求产生、中断响应与外部设备控制的完整链路。压缩包内为1个docx文档约14KB集中呈现实验说明与关键代码便于对照阅读与复现。已有1264人学习下载具备一定的参考基础。文档中给出了数据段、堆栈段与代码段的组织方式包含中断向量表设置、中断屏蔽寄存器配置、8253通道初始化、8255输出控制以及中断服务程序INTPROC的完整逻辑并说明如何通过COUNT变量控制中断次数、逐次点亮发光二极管直至程序返回DOS。读者可据此掌握三个芯片的初始化参数配置、中断服务流程与屏蔽恢复处理形成可运行的实验参考。1. 中断实验为什么一条 IRET 能让整个系统“活”过来很多人第一次看 8253、8259A、8255 联调的中断实验注意力都放在“灯亮了没有”上结果程序跑起来要么只中断一次就卡死要么灯全亮完却不返回 DOS。真正反直觉的地方在于中断能不能稳定跑完八次不取决于中断服务程序写得多漂亮而取决于你有没有在正确的位置关中断、发 EOI、恢复屏蔽字。这套机制解决的是“外设如何在不打扰主程序节奏的前提下按自己的时间尺度插入执行”的问题。搞懂它你才算真正理解 CPU 与外设之间那条看不见的握手链路也才能看懂为什么 8259A 的屏蔽寄存器、8253 的计数初值、8255 的方式字三者必须按顺序初始化少一步就是玄学翻车。2. 中断链路拆解8253 的脉冲是怎么变成 CPU 的一条指令的2.1 从定时器溢出到 INTR 拉高8253 在链路里到底扮演什么角色这个实验里 8253 不是“计时给人看”的它是整条中断链路的信号源。8253 内部有三个独立的 16 位递减计数器每个通道有六种工作方式实验里通道 0 和通道 1 都被写入初值 1000控制字分别是00110111B和01110101B。这两个字节不是随便填的拆开看才有意义低两位选通道中间三位选工作方式高两位选读写格式。; 8253 初始化先写控制字再写计数初值 MOV DX, IOPORT283H ; 控制字寄存器端口 MOV AL, 00110111B ; 通道0先低后高读写方式3二进制 OUT DX, AL MOV AL, 01110101B ; 通道1先低后高读写方式2二进制 OUT DX, AL MOV DX, IOPORT280H ; 通道0 数据端口 MOV AL, 00H ; 初值低字节 OUT DX, AL MOV AL, 10H ; 初值高字节合起来 1000H OUT DX, AL00110111B里 bit7-6 为00表示通道 0bit5-4 为11表示先读写低字节再读写高字节bit3-1 为011表示方式 3方波发生器bit0 为0表示二进制计数。方式 3 的特点是计数到零后自动重装初值输出连续方波方波的下降沿正好可以作为 8259A 的 IRQ 输入触发信号。通道 1 用01110101B方式 2 是频率发生器输出负脉冲常用来做级联或分频。两个通道初值都设 1000H意味着计数 4096 次后翻转一次配合外部时钟就能得到实验要求的“每秒一次”节奏。这里有个容易被忽略的细节8253 的控制字必须先写再写初值。如果顺序反了计数器会按上一次残留的方式字解释你写入的数据现象就是中断周期完全不对甚至一次都不来。常见的实现思路是初始化函数里严格按“控制字 → 低字节 → 高字节”三段式写中间不插入其他端口操作。2.2 8259A 的屏蔽与 EOI为什么 CLI 和 STI 的位置决定成败8259A 是可编程中断控制器它把 8 路级联后 15 路中断请求排优先级后送给 CPU。实验里主片端口20H/21H从片端口A0H/A1H代码里对21H写AND AL,0FBH对A1H写AND AL,0F7H这是在清中断屏蔽寄存器IMR的对应位把 8253 所在的那一路放行。CLI ; 关中断改向量表和屏蔽字期间不允许被打断 IN AL, 21H ; 读主片 IMR AND AL, 0FBH ; 清 bit2放行 IRQ2级联从片的那一路 OUT 21H, AL IN AL, 0A1H ; 读从片 IMR AND AL, 0F7H ; 清 bit3放行从片 IRQ3 OUT 0A1H, AL STI ; 屏蔽字改完再开中断0FBH是11111011B清的是 bit20F7H是11110111B清的是 bit3。这两个位对应主片级联口和从片的具体中断源。为什么必须先CLI再改因为 IMR 是 8259A 内部寄存器读写不是原子操作如果改到一半来了中断CPU 可能按半新半旧的屏蔽字响应行为不可预测。这就是血泪经验凡是动中断向量表、动 IMR、动 8253 控制字前后都要用CLI/STI夹住。中断服务程序末尾的MOV AL,20H加OUT 20H,AL是发 EOI中断结束命令告诉 8259A 当前中断处理完了可以把优先级状态复位。如果漏了这条8259A 会认为该中断仍在服务中同级和低级中断永远被挡住现象就是“只中断一次后面全哑”。从片的中断还要额外向A0H发一次 EOI实验代码只对主片发了20H这在单从片场景下能跑通但严格来说从片也需要结束命令属于可以深挖的取舍点。2.3 8255 方式字与端口映射灯为什么会“一个一个”亮8255 负责把 CPU 的数据变成 LED 的亮灭。实验里控制字10001111B写到IOPORT28BH拆开是bit7 为 1 表示方式设置有效bit6-5 为00表示 A 组方式 0bit4 为 0 表示端口 A 输出bit3 为 0 表示端口 C 高四位输出bit2 为 0 表示 B 组方式 0bit1 为 0 表示端口 B 输出bit0 为 0 表示端口 C 低四位输出。整体就是“三个端口全输出、全方式 0”的最简配置。MOV DX, IOPORT28BH ; 8255 控制口 MOV AL, 10001111B ; 方式0A/B/C 全部输出 OUT DX, AL ; 中断服务程序里更新 LED 数据 MOV DX, IOPORT288H ; 8255 端口 A MOV AL, LIGHT ; 初值 80H即最高位亮 OUT DX, AL MOV CL, 01H SAR AL, CL ; 算术右移80H→40H→20H→…→01H MOV LIGHT, ALLIGHT初值80H对应二进制10000000每次中断算术右移一位灯就从一端向另一端依次点亮。SAR而不是SHR的选择有讲究SAR保留符号位对80H这种最高位为 1 的值右移后不会突然变成C0H之类的意外值移位轨迹干净。八次中断后LIGHT变成00HCOUNT从08H递减到 0主循环里的CMP COUNT,0成立程序跳到INTEND恢复屏蔽字并返回 DOS。端口地址IOPORT EQU 5400H-280H配合280H、283H、288H、28BH这种偏移写法本质是把基址和芯片内部寄存器偏移分开改基址时只动一行。这种写法在实验代码里很常见但要注意IOPORT288H和IOPORT28BH之间差了 3 个字节正好对应 8255 的 A、B、C、控制口四个连续地址偏移算错一位就会写到别的寄存器上现象是灯乱亮或者根本不亮。3. 中断服务程序的执行时序从 IRET 返回主循环的那几微秒发生了什么3.1 向量表、现场保护与 COUNT 递减的完整时序程序开头用INT 21H的AH25H功能把INTPROC的段基址和偏移写进中断向量表第73H号表项。MOV AX,2573H里25H是 DOS 设置中断向量的子功能73H是中断号。这个中断号不是随便选的它要和 8259A 实际送出的中断类型号对应否则 CPU 收到中断后跳到一个空向量直接跑飞。中断发生时的硬件时序大致是8253 输出脉冲 → 8259A 拉高 INTR → CPU 当前指令执行完 → 发 INTA 应答 → 8259A 把类型号放到数据总线 → CPU 查向量表 → 压栈 FLAGS、CS、IP → 跳转到INTPROC。服务程序里先CLI再MOV AX,DATA重设 DS是因为中断可能发生在主程序任意位置DS 不一定还指向数据段。接着用INT 21H的AH09H显示字符串再写 8255、移位LIGHT、发 EOI、DEC COUNT最后STI加IRET。IRET和普通RET的区别在于它会从栈里弹出 IP、CS、FLAGS 三个字恢复被中断时的完整现场。如果服务程序里压栈和出栈不平衡IRET弹出来的返回地址就是错的现象是程序跳到一个莫名其妙的地方或者直接死机。这是排查中断问题时第一个要看的地方数一数PUSH和POP是否成对。3.2 主循环空转与 COUNT 的可见性为什么 CMP 能等到第八次中断主循环只有三条指令CMP COUNT,0、JZ INTEND、JNZ LL。它本质上是一个忙等待反复读COUNT变量。COUNT在数据段中断服务程序里DEC AL后MOV COUNT,AL写回内存主循环每次CMP都重新从内存取值所以能看到中断带来的变化。这里依赖的是内存变量的可见性而不是寄存器缓存如果编译器或手写代码把COUNT提前读进寄存器再循环比较就会永远等不到 0。LL: CMP COUNT, 0 ; 每次循环都从内存重新读 COUNT JZ INTEND ; 减到 0 就退出 JNZ LL ; 否则继续空转等待下一次中断这种忙等待在实验环境里没问题因为主程序没有别的活要干。但在真实系统里空转浪费 CPU常见做法是用HLT让 CPU 休眠到下一次中断唤醒或者用信号量、事件标志让主任务阻塞。实验代码选择忙等是为了让“中断插入主流程”这件事看得更直白属于教学取舍。3.3 退出路径的屏蔽字恢复为什么 INTEND 里要 OR 80H程序退出前执行IN AL,21H、OR AL,80H、OUT 21H,AL把主片 IMR 的 bit7 置 1屏蔽掉最高优先级那一路再对A1H直接OUT写回。这一步的目的是把中断系统恢复到进入实验前的状态避免程序返回 DOS 后残留的屏蔽字影响后续程序。OR 80H而不是写死某个值是因为你只知道自己改了 bit2 和 bit3其他位是系统原来的配置不能一把覆盖掉。同样的思路出现在开头对 9054 芯片的配置上先IN AX,DX读回原值OR AX,0900H置位结束时AND AX,0F7FFH清位再写回。读-改-写是操作共享寄存器的标准手法直接OUT一个常量会把别人配好的位冲掉这种翻车在联调多芯片系统时特别常见。4. 避坑与排查中断实验里最容易翻车的五个点4.1 只中断一次就停EOI 漏发或发错端口现象是屏幕只显示一次字符串灯只亮一个之后主循环永远等不到COUNT归零。原因通常是服务程序末尾没有MOV AL,20H/OUT 20H,AL或者从片中断只给主片发了 EOI 没给从片发。8259A 在收到 EOI 前会一直认为当前中断在服务中同级和低级请求全被压住。解决办法是主片发20H到20H端口从片发20H到A0H端口顺序上先从片后主片更稳妥。4.2 灯全亮但不返回 DOSCOUNT 递减写错了位置现象是八个灯依次亮完程序卡住不退出。原因多半是DEC COUNT写在了MOV COUNT,AL之前或者AL在DEC前被其他指令覆盖导致内存里的COUNT根本没变。排查时在服务程序里临时把COUNT输出到某个空闲端口用逻辑分析仪看或者单步跟踪确认DEC前后AL的值。正确顺序是先MOV AL,COUNT再DEC AL再MOV COUNT,AL。4.3 中断周期完全不对8253 控制字和初值顺序颠倒现象是中断来得太快、太慢或者干脆不来。原因是先写了初值再写控制字计数器按旧方式字解释数据。8253 要求每个通道先写控制字锁定工作方式再按控制字里规定的读写格式写初值。排查时用示波器看 8253 输出引脚有没有预期频率的波形没有就回头检查控制字字节的每一位是否符合预期通道和方式。4.4 程序跑飞或死机向量表设置与 CLI/STI 配对错误现象是中断一来程序就跳到乱地址。原因是INT 21H设置向量时DS没指向代码段或者CLI之后忘了STI又或者服务程序里PUSH/POP不平衡导致IRET弹出错误返回地址。排查时先确认MOV AX,CS/MOV DS,AX在MOV AX,2573H之前执行再数一遍服务程序里的压栈出栈是否成对最后确认IRET前STI已经执行。4.5 端口偏移算错8255 写到了错误寄存器现象是灯乱亮、常亮或者完全不响应。原因是IOPORT288H、28BH这类偏移和 8255 内部 A/B/C/控制口映射对不上或者基址5400H-280H算错。排查时把每个OUT指令的端口地址打印出来对照 8255 数据手册的寄存器偏移表逐项核对特别注意控制口在最高偏移、端口 A 在最低偏移这个顺序。5. 设计取舍为什么教学代码敢用忙等待和简化 EOI这套代码最值得琢磨的不是它写了什么而是它故意没写什么。主循环用CMP/JNZ忙等而不是HLT是因为实验环境里 CPU 没有别的任务空转的代价可以忽略换来的是“中断插入主流程”这件事在单步调试时一目了然。真实系统里更常见的做法是主任务阻塞在信号量上中断服务程序只做最少的事——置标志、发 EOI、清中断源把显示字符串和移位这种耗时操作放到主循环里做避免中断嵌套过深。从片 EOI 的简化也是同一个逻辑实验只挂了一个从片中断源主片 EOI 发完从片的优先级状态虽然没复位但因为没有其他从片中断竞争现象上看不出问题。换成多从片源同时工作的场景漏发从片 EOI 就会导致低优先级中断被永久屏蔽。我一般会这么理解教学代码是“最小可工作闭环”它把边界条件砍到刚好能演示机制剩下的坑留给真实项目去踩。设计点教学代码做法真实系统常见做法取舍原因主循环等待忙等CMP/JNZHLT或阻塞在信号量教学要可单步观察真实要省 CPU中断服务内容显示字符串移位发 EOI只置标志、清中断源、发 EOI缩短中断关闭窗口降低嵌套风险从片 EOI只发主片从片、主片都发单源场景可简化多源必须完整屏蔽字恢复读-改-写保存原值、退出时整体恢复读-改-写够用保存原值更稳验证这套机制是否真的跑通最直接的办法是在服务程序入口和IRET前各翻转一个空闲 IO 口用逻辑分析仪量中断响应到退出的时间窗口再对照 8253 输出波形看周期是否一致。如果窗口时间随中断次数线性增长说明服务程序里有累积性的资源没释放如果窗口时间稳定但周期漂移问题就在 8253 初值或时钟源上。从那以后我每次调中断链路都强制先把 EOI 和屏蔽字恢复这两步单独验证一遍再去看业务逻辑希望帮到你。本文还有配套的精品资源点击获取