CS253_L910:数字I/O与中断
CS253_L910:数字I/O与中断
考纲点 1.3:汇编程序功能分析 - 字符显示和过滤程序
这部分考点主要涉及通过直接内存访问进行字符输出,是实现字符显示和过滤的基础。
1. 直接屏幕访问 (Direct Access To Screen Memory)
参考幻灯片:8-9, 20
-
原理:
- 文本屏幕被映射到特定的内存地址 (通常彩色模式为
B8000h)。CPU可以直接读写这块内存来改变屏幕显示。 - 引用:“The Text Screen is memory mapped such that top left char is located in memory address B8000, the screen is 80 characters wide.” (幻灯片 8)
- 文本屏幕被映射到特定的内存地址 (通常彩色模式为
-
内存布局 (基于幻灯片 9, 20 的图片 "Direct write to screen memory"):
- 屏幕尺寸: 通常为 25 行 x 80 列。
- 字符存储: 每个屏幕字符占用 2字节。
- 奇数地址 (Odd addresses): 存储字符的 ASCII码 (C)。
- 偶数地址 (Even addresses): 存储字符的 属性字节 (A)。
- 存储顺序示意:
C A C A ...
- 属性字节 (Attribute Byte):
- 8位字节,定义字符显示效果。
- 高4位 (bits 7-4): 背景色 (Background: F R G B - 闪烁, 红, 绿, 蓝)。
- 低4位 (bits 3-0): 前景色 (Foreground: I R G B - 亮度, 红, 绿, 蓝)。
- 示例:“Attribute=12, Bright red” (属性值为12表示亮红色前景)。
-
汇编示例 (直接写屏 - 幻灯片 8):
mov ax, 0b800h ; 设置屏幕段基址 mov ds, ax ; (或使用 es) mov bx, [offset] ; 计算字符在屏幕上的偏移地址 mov al, 'A' ; 要显示的字符 mov [bx], al ; 将ASCII码写入显存 (属性字节通常在 [bx+1])解读: 这段代码展示了如何将字符'A'直接写入屏幕内存的特定位置。
-
TSR中的屏幕操作 (ISR中更新屏幕 - 幻灯片 20):
mov ax, 0b800h ; 屏幕内存基址 mov es, ax mov bx, 1680 ; 目标屏幕位置偏移 (例如第10行第40列) mov al, dl ; dl 中是要显示的字符ASCII码 mov es:[bx], al ; 更新屏幕字符解读: 此示例显示了如何在中断服务程序中更新屏幕特定位置的字符。
-
优缺点:
- 引用:“It is very fast but hardware specific.” (幻灯片 8)
- 优点: 速度快。
- 缺点: 硬件相关,可移植性差。
-
关于“过滤”: 虽然课件未提供完整过滤程序,但理解直接写屏机制后,可构思过滤逻辑:读取输入 -> 判断条件 -> 若符合则写入屏幕。
考纲点 3.4:触发器与三态设备 (在I/O端口中的应用)
这部分内容在课件中通过输入/输出端口的硬件实现来体现。
参考幻灯片:4-5
1. 输入端口 (Input Ports)
参考幻灯片:4 (图片 "Input Ports")
- 核心组件:三态缓冲器 (Tri-state buffers)
- 作用: 允许多个输入设备共享数据总线。只有被选中的设备才将数据放到总线上。
- 工作原理 (结合图示解读):
- 外部设备的数据线连接到三态缓冲器的输入端。
- 三态缓冲器的输出端连接到CPU的数据总线。
- 当CPU执行
IN指令并指定某端口地址时:- 地址译码器 (Address decoder): 识别到正确的端口地址,输出一个高电平选通信号。引用:“When the correct address is present the address decoders output goes high.”
- 控制信号: 该选通信号与CPU的I/O读信号 (
IO/Mem Request为I/O操作,R/W Request为读操作) 进行逻辑与操作。 - 使能三态缓冲器: 当上述条件均满足时,三态缓冲器的“输出使能 (Output enable)”端被激活。
- 数据传输: 被激活的三态缓冲器将外部设备的数据放到CPU的数据总线上。引用:“The Tri-State buffer is activated and data placed on the bus.”
- 相关指令:
IN acc, port(从端口读取数据到累加器)。
2. 输出端口 (Output Ports)
参考幻灯片:5 (图片 "Output Ports")
- 核心组件:D型触发器 (D Type Flip Flops) / 锁存器 (Latches)
- 作用: 锁存并保持CPU输出到外部设备的数据,为外部设备提供稳定的数据信号。
- 工作原理 (结合图示解读):
- CPU的数据总线连接到D型触发器的输入端 (D)。
- D型触发器的输出端 (Q) 连接到外部设备。
- 当CPU执行
OUT指令并指定某端口地址时:- 地址译码器 (Address decoder): 识别端口地址,输出选通信号。
- 控制信号: 该选通信号与CPU的I/O写信号 (
IO/Mem Request为I/O操作,R/W Request为写操作) 进行逻辑与操作。 - 锁存数据: 当上述条件均满足时,产生一个“锁存数据 (Latch Data)”信号,使D型触发器将CPU数据总线上的数据锁存到其内部,并通过输出端Q稳定输出。引用:“The data placed on data bus by the CPU is latched into the 8 flip flops.”
- 相关指令:
OUT port, acc(将累加器的数据写到端口)。- 课件原文:“OUT acc, port Writes a byte,word or double word from the port to the accumulator.” (此处课件原文有误,应为 from the accumulator to the port)。
考纲点 4.3:延时程序设计
课件主要通过 ISR设计注意事项和 TSR 中的示例来讨论延时。
参考幻灯片:14, 18
1. ISR中的延时注意事项
参考幻灯片:14 ("Re-vectoring Interrupts - WARNINGS")
- 避免在ISR中直接使用长延时:
- 引用:“If the service routine takes longer than 50mS to run then a second call to the interrupt will occur whilst the first is occuring. The isr should be quick and contain no delays.”
- 解读: ISR 执行时间过长可能导致中断嵌套、堆栈溢出或系统响应迟缓。
- 通过计数中断次数实现延时:
- 引用:“To create a delay, just count the number of times the interrupt has been called. (e.g. 5 seconds = 100 interrupt calls)”
- 解读: 这是一种更安全的方法,将延时任务分解到多次中断中完成。
2. 软件延时循环示例 (TSR中的 delay 子程序)
参考幻灯片:18 ("A long delay (about 3 seconds)")
delay: push cx
push bx
mov bx, 25000 ; 外层循环计数器
dly1: mov cx, 20000 ; 内层循环计数器
dly2: loop dly2 ; 内层循环 (消耗时间)
dec bx
jnz dly1 ; 外层循环
pop bx
pop cx
ret
-
原理: 通过执行大量的空操作(循环)来消耗CPU时间,从而达到延时的效果。
-
延时计算基础:
延时时间 = (总循环次数 × 平均每次循环所需机器周期数) / 时钟频率。
- 内层
loop指令执行cx(20000) 次。 - 外层
dec bx; jnz dly1执行bx(25000) 次。
- 内层
-
重要提示: 引用:“Remember: Do not make this part of isr, stack faults etc”
3. 时钟频率与延时关系
- 课件未直接给出公式,但基本原理是:
- 指令执行需要特定数量的机器周期。
- 机器周期时间 = 1 / CPU时钟频率。
- 总延时 = (总机器周期数) × (机器周期时间)。
- 设计延时程序时,需估算循环体指令的总机器周期,再根据所需延时和时钟频率计算循环次数。
考纲点 6:中断系统
这是 CS253_L910.pdf 的核心内容。
6.1 中断类型与机制
参考幻灯片:10, 12
-
中断基本概念:
- 中断是打断CPU正常指令序列去处理紧急事件的机制。
- 引用:“Most advanced microprocessors allow normal linear program operation to be interrupted by some external signal or by a special instruction in the program.” (幻灯片 10)
- 中断响应步骤 (通用):
- 暂停当前程序。
- 保存现场 (关键寄存器)。
- 转去执行中断服务例程 (ISR)。
- 恢复现场。
- 返回原程序继续执行。
参考幻灯片 10: "When Interrupted the microprocessor..."
-
8086的中断种类 (幻灯片 10 - "The three types of Interrupt 8086"):
- 硬件中断 (Hardware Interrupts):
- 由外部设备通过CPU的
INTR(可屏蔽) 或NMI(不可屏蔽,优先级更高) 引脚触发。 - 引用:“An external signal applied to the INTR (interrupt) pin or NMI (non-maskable interrupt) pin of the processor causes an interrupt.”
- 由外部设备通过CPU的
- 异常中断 (Exception Interrupts) / 内部中断:
- 由CPU内部执行指令时发生的错误引起 (如除零、溢出)。
- 引用:“Triggered by internal errors such as divide by zero, normally to print an error message.”
- 软件中断 (Software Interrupts):
- 由执行
INT n指令引起,常用于调用系统服务 (如BIOS/DOS功能)。 - 引用:“Caused by the execution of an assembly language INT command. Simply a call to a function in BIOS.”
- 由执行
- 硬件中断 (Hardware Interrupts):
-
硬件中断处理过程详解 (通过8259A - 幻灯片 12 - "Interrupt Processing Sequence"):
-
外部设备拉低
IRQx线,向8259A请求中断。 -
8259A通过
INTR线向CPU发送中断请求。 -
CPU通过
INTA(中断应答) 信号响应。 -
8259A收到
INTA后,将中断类型号 (0-255) 放到数据总线上。 -
CPU读取中断类型号。
-
CPU自动压栈:
标志寄存器 (Flags)、代码段寄存器 (CS)、指令指针 (IP)。
- 课件提及SP也压栈,但更准确的是Flags、CS、IP被硬件自动压栈。SP会因压栈操作而改变。
- 引用:“CPU hardware related registers onto the stack, including current CS:IP,SP and Flags.”
-
CPU根据中断类型号计算IVT中的地址 (中断号 × 4),获取ISR的入口地址 (新的CS:IP)。
-
CPU跳转并开始执行ISR。
-
ISR执行完毕,以
IRET指令结束。 -
IRET指令自动从栈中弹出IP、CS、Flags,恢复现场,主程序继续执行。
-
6.2 中断向量表 (IVT)
参考幻灯片:11
- 位置与大小:
- 位于RAM的最低1KB (0000:0000 到 0000:03FF)。
- 引用:“The Interrupt Vector Table is located in RAM at 0000:0000 to 0000:03FF, i.e. the first 1024 bytes of RAM.”
- 结构:
- 包含256个中断向量,每个向量占用 4字节。
- 每个向量存储对应ISR的入口地址:2字节IP (偏移地址),后跟2字节CS (段基址)。
- 引用:“Each interrupt vector is described by 4 bytes, two for the Code Segment (CS) and two for the Instruction Pointer (IP).”
- 查找机制:
- ISR入口地址在IVT中的偏移 = 中断类型号 × 4。
- 引用:“Vector Address = 4*Interrupt Number”
- 示例 (幻灯片 11 - 图片 "Part of The Interrupt Vector Table"):
Int 0 (Divide by zero): IVT地址00-03,内容1B02:2389(即 CS=1B02h, IP=2389h)。Int 8 (Timer Tick): IVT地址20-23,内容2810:1875。
好的,同学,我们来把这个“中断向量表 (IVT)”掰开了、揉碎了,讲到你觉得它一点都不抽象为止!
想象一下,你是一位大楼的总调度员 (CPU)。大楼里有很多部门 (中断服务程序 - ISR),比如“火警处理部”、“键盘输入处理部”、“打印机服务部”等等。
当大楼里发生一个紧急事件 (中断),比如某个地方着火了 (硬件中断),或者有人按了键盘 (硬件中断),或者你发现计算器算错了——除以零了 (异常中断),你作为总调度员,不能自己跑去处理所有事情,你需要快速找到负责这个特定事件的部门,让他们去干活。
中断向量表 (IVT) 就是你的“部门通讯录”或者“应急响应手册”。
-
“通讯录”放在哪里?有多大?(位置与大小)
- 位置: 这本“通讯录”非常重要,所以它被放在内存中一个固定且容易找到的地方——内存的最开始,从物理地址
00000h开始,一直到003FFh。用段地址和偏移地址表示就是0000:0000到0000:03FF。(这里的0000是段基址,0000到03FF是偏移)。 - 大小: 这块区域总共是 1024字节 (也就是1KB)。你想想,从
000h到3FFh,3FFh等于十进制的1023。所以0到1023正好是1024个字节。 - 引用解读: "The Interrupt Vector Table is located in RAM at 0000:0000 to 0000:03FF, i.e. the first 1024 bytes of RAM." —— 这句话就是在说,通讯录在内存的头1024个字节里。
- 位置: 这本“通讯录”非常重要,所以它被放在内存中一个固定且容易找到的地方——内存的最开始,从物理地址
-
“通讯录”里记了什么?怎么记的?(结构)
-
条目数量: 这本“通讯录”里可以记录 256个 不同部门的联系方式。为什么是256个?因为CPU能识别的中断类型号是从0到255,正好256个。
-
每个条目的格式:
每个部门的联系方式 (我们称之为一个“
中断向量
”或“
中断指针
”) 都不多不少,正好占用
4个字节
。
- 这4个字节里存的是对应部门 (ISR) 的“门牌号”和“办公室号”,也就是它的入口地址。
- 具体来说:
- 前2个字节:存的是 IP (Instruction Pointer - 指令指针),也叫偏移地址。 这是“办公室号”。
- 后2个字节:存的是 CS (Code Segment - 代码段寄存器),也叫段基址。 这是“楼层号”或“街道号”。
- CS:IP 合在一起,就能唯一确定一个内存地址,也就是那个ISR的第一条指令在哪里。
-
引用解读: "Each interrupt vector is described by 4 bytes, two for the Code Segment (CS) and two for the Instruction Pointer (IP)." —— 这里要注意,幻灯片说“two for CS and two for IP”,但实际存储时,通常是IP在前,CS在后。即:
IP_low, IP_high, CS_low, CS_high。当CPU读取后,会正确组合成CS:IP。 -
所以,
1024字节总空间 / 4字节每条 = 256条联系方式。完美对应0-255号中断。
-
-
怎么查“通讯录”?(查找机制)
-
当一个中断发生时,CPU会得到一个中断类型号 (Interrupt Number),比如0号中断 (除零错),8号中断 (时钟中断)。这个号码就像你要查找的“部门编号”。
-
CPU拿着这个“部门编号”,就要去“通讯录”(IVT)里找对应的联系方式。
-
查找公式:
该部门联系方式在通讯录中的起始偏移位置 = 中断类型号 × 4。
- 为什么要乘以4?因为每个部门的联系方式占4个字节啊!
-
引用解读: "Vector Address = 4*Interrupt Number" —— 这个“Vector Address”指的是中断向量在IVT这张大表里的起始偏移地址。
-
-
看个具体的例子,就不抽象了!
-
示例1:
Int 0 (Divide by zero)除零中断-
中断类型号是:
0 -
它在IVT中的查找地址是:
0 × 4 = 0。所以CPU会从IVT的第0个字节开始找,也就是内存地址0000:0000。 -
它会从
0000:0000处连续读取4个字节。 -
这4个字节的内容,比如幻灯片上写的是
1B02:2389。这意味着:
- 在内存地址
0000:0000和0000:0001存的是IP值2389h(低字节在前,高字节在后,所以内存里可能是89h存0000:0000,23h存0000:0001)。 - 在内存地址
0000:0002和0000:0003存的是CS值1B02h(内存里可能是02h存0000:0002,1Bh存0000:0003)。
- 在内存地址
-
所以,当发生除零错误时,CPU就会跳转到
CS=1B02h, IP=2389h这个地址去执行相应的错误处理程序。
-
-
示例2:
Int 8 (Timer Tick)时钟中断-
中断类型号是:
8 -
它在IVT中的查找地址是:
8 × 4 = 32(十进制),也就是20h(十六进制)。所以CPU会从IVT的第32个字节开始找,也就是内存地址0000:0020。 -
它会从
0000:0020处连续读取4个字节 (即0020h, 0021h, 0022h, 0023h)。 -
幻灯片上写的内容是
2810:1875。这意味着:
- IP =
1875h - CS =
2810h
- IP =
-
所以,每当系统时钟发出一个“滴答”中断时,CPU就会跳转到
CS=2810h, IP=1875h这个地址去执行时钟中断服务程序 (比如更新系统时间、任务调度等)。
-
-
总结一下,把IVT想象成一个表格:
| 中断号 (十进制) | 中断号 (十六进制) | IVT中起始地址 (0000:偏移) | 存储内容 (4字节) | 含义 (ISR入口 CS:IP) |
|---|---|---|---|---|
| 0 | 00h | 0000:0000 | (字节0-3) | 比如 1B02:2389 |
| 1 | 01h | 0000:0004 | (字节4-7) | ... |
| ... | ... | ... | ... | ... |
| 8 | 08h | 0000:0020 | (字节32-35) | 比如 2810:1875 |
| ... | ... | ... | ... | ... |
| 255 | FFh | 0000:03FC | (字节1020-1023) | ... |
所以,IVT一点都不抽象,它就是:
- 一个固定位置 (内存头1KB)。
- 一个固定结构 (256项,每项4字节存IP和CS)。
- 一个简单的查找方法 (中断号 × 4)。
- 唯一目的:告诉CPU,当某个中断发生时,你应该去哪里找对应的处理程序。
现在再回头看幻灯片上的描述,是不是感觉清晰多了?它就像是CPU处理各种“突发状况”时的“快速反应指南”。
6.3 栈在中断中的应用
参考幻灯片:12, 19, 21
-
FILO原理: 栈是后进先出 (First-In, Last-Out) 的数据结构,天然适合处理嵌套调用和中断。
-
中断返回地址存储:
- CPU在响应中断时,会自动将当前的 Flags, CS, IP 压入堆栈。这是为了ISR执行完毕后,
IRET指令能够找到正确的返回点和恢复处理器状态。 - 见6.1中断处理过程第6步。
- CPU在响应中断时,会自动将当前的 Flags, CS, IP 压入堆栈。这是为了ISR执行完毕后,
-
ISR中通用寄存器的保存与恢复:
-
程序员的责任: CPU硬件不自动保存通用寄存器 (AX, BX, CX, DX, SI, DI, BP, DS, ES 等)。如果ISR需要使用这些寄存器,必须由程序员在ISR的开头手动
PUSH它们,并在ISR结束前 (执行IRET之前) 按相反顺序POP恢复。 -
引用:“Interrupts don’t store general registers, you must.” (幻灯片 19)
-
示例 (幻灯片 19 - ISR开头):
isr: push ax push bx ; ... (保存其他需要的寄存器) push ds
-
-
示例 (幻灯片 21 - ISR结尾):
; ... (恢复其他寄存器) pop es pop ds ; ... pop bx pop ax iret
6.4 多源中断处理 (通过8259 PIC)
参考幻灯片:6-7, 12-14
-
8086的中断引脚限制: 仅有
INTR和NMI两个硬件中断输入。 -
Intel 8259 可编程中断控制器 (PIC):
-
作用: 扩展CPU处理硬件中断的能力,管理多个外部中断源,并进行优先级仲裁。
- 引用:“The 8259 provides the hardware required to interface with the 8086 interrupts.” (幻灯片 6)
-
级联 (Cascading - 幻灯片 6 - 图片 "The interrupt controller"):
- PC中常使用两片8259 (主片Master, 从片Slave) 级联,以支持更多中断源 (如15个IRQ)。
- 主片连接CPU的
INTR。从片连接到主片的一个IRQ输入 (通常是IRQ2)。 - 图示解读: 8259 #1 (Master) 处理 IRQ0-IRQ1 (直接到8086) 及 IRQ2-IRQ7 (IRQ2接从片)。8259 #2 (Slave) 处理 IRQ8-IRQ15。
-
核心功能 (幻灯片 7 - "8259A"):
- 优先级中断控制器 (Priority Interrupt Controller)。
- 当外部设备拉低某条
IRQ线时,8259A会向CPU请求中断,并在CPU响应后提供对应的中断类型号。 - 引用:“when an IRQ line is pulled low on the device, the interrupt number is placed on the PC bus.”
-
典型IRQ分配 (幻灯片 7 - "Typical IRQ designations"):
IRQ0: Timer TickIRQ1: KeyboardIRQ2: Cascade to second 8259A- ...
-
初始化 (幻灯片 12 - "8259A Initialisation"):
- 通常由BIOS在系统启动时完成。
- 设定8259A的工作模式、基地址 (主片20H, 从片A0H) 和IRQ到中断类型号的映射 (如 IRQ0 -> INT 08H)。
-
中断屏蔽寄存器 (IMR - Interrupt Mask Register - 幻灯片 13):
- 允许通过编程屏蔽(禁止)或允许特定的IRQ请求。
- 主片IMR端口
21H,从片IMR端口A1H。 - 引用:“Setting the interrupt mask register bit disables the interrupt.”
- 图示解读: 8位寄存器,每一位对应一条IRQ线。
-
中断结束命令 (EOI - End Of Interrupt - 幻灯片 14):
- ISR在执行
IRET前,必须向8259A发送EOI命令,通知其该中断已处理完毕,可以响应其他同级或低优先级的中断。 - 引用:“At the end of the interrupt you must tell the controller that the interrupt is completed.”
- 发送EOI (非特定EOI):
MOV AL, 020H; MOV DX, 020H; OUT DX, AL(向主8259的命令端口20H写入20H)。
场景:一家非常繁忙的餐厅的厨房
-
你 (CPU): 是这家餐厅的唯一大厨。你厨艺高超,但你只有两只手,一次能处理的菜品订单有限。
INTR引脚:是你厨房门口的一个普通服务铃。服务员按了,你知道有订单了,但不紧急,你可以忙完手头的再看。NMI引脚:是你厨房里的火警铃!这个一响,你必须立刻放下手头所有活去处理!(这个NMI我们先放一边,8259主要和INTR打交道)。
-
问题来了 (8086的中断引脚限制):
餐厅生意太好了!有负责点菜的服务员、负责酒水的服务员、负责甜点的服务员……好多好多人 (外部设备,如键盘、鼠标、硬盘、时钟等) 都想把订单交给你这个大厨。但你厨房门口只有一个普通服务铃 (INTR),如果大家都去按,你就不知道是谁的订单,也不知道哪个更紧急。乱套了! -
解决方案:聘请一位“领班” (Intel 8259 PIC - 可编程中断控制器)
为了解决这个混乱,餐厅老板聘请了一位非常能干的领班 (8259 PIC)。-
领班的作用 (扩展CPU处理硬件中断的能力,管理多个外部中断源,并进行优先级仲裁):
- 接收订单: 所有服务员 (外部设备) 不再直接去按大厨的铃,而是把订单条 (中断请求
IRQ) 交给这位领班。领班有很多个“订单夹”(IRQ0 - IRQ7,共8个)。 - 判断优先级: 领班会看这些订单哪个更紧急 (比如VIP客户的加急订单优先级就高)。
- 通知大厨: 如果领班手上有订单,并且他认为现在可以打扰大厨了,他就会去按大厨的那个普通服务铃 (
INTR)。 - 告诉大厨是谁的订单: 当大厨回应“谁啊?”(CPU发送
INTA中断应答信号)时,领班会告诉大厨这是“几号桌的什么订单”(把中断类型号给CPU)。这样大厨就知道该去做什么菜了。
- 引用解读:“The 8259 provides the hardware required to interface with the 8086 interrupts.” —— 领班就是那个连接众多服务员和大厨的“硬件接口”。
- 接收订单: 所有服务员 (外部设备) 不再直接去按大厨的铃,而是把订单条 (中断请求
-
-
生意太火爆,一个领班不够用!(级联 - Cascading)
餐厅规模扩大,服务员更多了 (设备更多了,比如超过8个)。一个领班管不过来。
于是老板又请了一位副领班 (从片 Slave 8259)。- 主领班 (Master 8259): 还是直接向大厨汇报。
- 副领班 (Slave 8259): 他也管理一批服务员的订单 (比如IRQ8 - IRQ15)。但是,副领班不直接找大厨,他把他的重要订单交给主领班的一个特定“订单夹”(通常是主领班的
IRQ2号订单夹)。 - 工作流程: 副领班收到一个紧急订单 -> 他通知主领班 (通过主领班的IRQ2) -> 主领班再综合判断,决定是否通知大厨。
- 这样,两个领班配合,就能管理更多订单了 (比如
7 (主) + 8 (从) = 15个不同的订单来源)。 - 图示解读: 8259 #1 (Master) 就是主领班,他自己管IRQ0, IRQ1, IRQ3-IRQ7的订单,他的IRQ2订单夹专门接收来自8259 #2 (Slave)副领班的“汇总订单”。副领班则管理IRQ8-IRQ15的订单。
-
领班的核心技能 (核心功能):
- 优先级管理: 这是领班最重要的技能!他知道哪些订单(IRQ)更重要,需要优先处理。
- 传递信息: 当服务员 (设备) 把订单条 (拉低IRQ线) 给他,他会去按大厨的铃 (向CPU发INTR)。大厨问是谁,他会准确报出“订单编号”(中断类型号)。
- 引用解读:“when an IRQ line is pulled low on the device, the interrupt number is placed on the PC bus.” —— 服务员把订单条插到领班的夹子上 (拉低IRQ线),领班就会把这个订单对应的“统一编号”(中断类型号) 准备好,等大厨问的时候告诉大厨。
-
常见的订单类型和来源 (典型IRQ分配):
就像餐厅里固定的:IRQ0: 通常是“厨房内部定时器”的订单 (Timer Tick) - 比如每隔一分钟提醒大厨检查一下汤。IRQ1: 通常是“点菜台”的订单 (Keyboard) - 客人点菜了。IRQ2: 这个订单夹特殊,是“副领班转交”的订单 (Cascade to second 8259A)。- 等等... 每个IRQ线通常连接一个特定的设备。
-
给领班“岗前培训” (初始化 - Initialisation):
新领班上岗前,餐厅经理 (BIOS程序) 会对他进行培训:-
告诉他他的“办公桌”在哪里 (设定8259A的端口地址,主领班通常在
20H和21H,副领班在A0H和A1H)。 -
告诉他工作模式 (比如订单优先级怎么排,是固定优先级还是循环优先级等)。
-
最重要的:发给他一本“订单-菜品对照表”
(IRQ到中断类型号的映射)。比如告诉他:
- IRQ0来的订单 (定时器),统一按“8号菜品”(INT 08H) 上报给大厨。
- IRQ1来的订单 (键盘),统一按“9号菜品”(INT 09H) 上报给大厨。
- 这样大厨拿到“菜品编号”就知道是哪个服务员的什么类型的订单了。
-
-
领班的“暂停接收此订单”牌子 (中断屏蔽寄存器 - IMR - Interrupt Mask Register):
有时候,某种订单太多了,或者暂时不重要,领班可以在对应的订单夹上挂一个“暂停接收”的牌子。- 主领班的牌子控制板在
21H端口,副领班在A1H端口。 - 这是个8位的控制板,每一位对应一个订单夹 (IRQ线)。如果某位设为
1,意思就是“这个订单夹来的单子我暂时不看,不处理,不往大厨那里报”。如果设为0,就是正常接收。 - 引用解读:“Setting the interrupt mask register bit disables the interrupt.” —— 在牌子控制板上把对应订单夹的灯按亮 (设为1),这个订单夹就“失效”了。
- 主领班的牌子控制板在
-
大厨做完一道菜要告诉领班 (中断结束命令 - EOI - End Of Interrupt):
当大厨 (或者说CPU执行完了某个中断服务程序ISR) 把领班交过来的一个订单处理完了,他必须告诉领班:“这个订单我搞定了,你可以把下一个订单给我了,或者让其他优先级没那么高的订单有机会了。”- 为什么必须? 如果不告诉领班,领班会以为大厨还在忙这个订单,就不会把其他同级或低优先级的订单给大厨,甚至可能导致新的同级中断也无法响应。
- 怎么告诉 (非特定EOI,告诉主领班): 大厨喊一声“20号搞定!” (
MOV AL, 020H; MOV DX, 020H; OUT DX, AL)。这里的020H是一个约定的命令码,发给主领班的命令端口020H。 - 引用解读:“At the end of the interrupt you must tell the controller that the interrupt is completed.” —— 必须告诉领班你完事了!
现在我们把餐厅的场景对应回电脑:
- 大厨 (CPU) 太忙,没法直接应付所有 服务员 (外部设备)。
- 领班 (8259 PIC) 站出来,接收所有 订单 (IRQ请求),排好优先级,然后一次一个地通过 服务铃 (INTR) 交给大厨,并告诉大厨这是 几号菜品 (中断类型号)。
- 如果服务员太多,就再来个 副领班 (从片8259) 协助管理。
- 领班可以根据需要,用 “暂停牌” (IMR) 暂时不接收某些订单。
- 大厨做完一道菜 (ISR执行完毕),一定要喊一声 “搞定!” (EOI),好让领班继续工作。
这样是不是感觉8259 PIC不再那么“高冷”了?它就是一个非常重要的、勤勤恳恳的“中断请求管理员”,确保CPU能够有序、高效地响应来自四面八方的硬件请求。
- ISR在执行
-
TSR (Terminate and Stay Resident) 程序 - 中断应用示例
参考幻灯片:15-21
这部分演示了如何通过挂接系统中断(如时钟中断)来实现程序在后台驻留并执行特定任务。
1. 基本思想
参考幻灯片:15 ("TSR’s")
- TSR程序在执行完毕后,其部分代码仍驻留在内存中。
- 通常通过截获(挂接)某个中断向量,当该中断发生时,转而执行TSR中的代码。
- 引用:“Routines in TSRs are normally invoked by interrupts.”
2. TSR实现步骤 (以挂接时钟中断INT 8为例)
参考幻灯片:16-18
-
保存原始中断向量 (幻灯片 16 - "Save the existing vector address"):
- 在修改IVT之前,必须先读取并保存目标中断号(如INT 8,其向量地址为
0000:0020h)原始ISR的入口地址 (CS和IP)。 - 代码解读: 使用
ES指向IVT段 (0000h),BX指向向量地址,读取ES:[BX](IP) 和ES:[BX+2](CS) 到自定义变量vtabip和vtabcs。
- 在修改IVT之前,必须先读取并保存目标中断号(如INT 8,其向量地址为
-
设置新的中断向量 (幻灯片 17 - "Set vector address to your service routine"):
- 将自定义ISR的入口地址 (如
OFFSET isr和当前代码段CS) 写入IVT中目标中断号对应的位置。 - 代码解读:
mov es:[bx], ax(ax存新IP),mov es:[bx+2], ax(ax存新CS)。
- 将自定义ISR的入口地址 (如
-
编写自定义ISR (幻灯片 19-21):
-
保存现场:
PUSH所有会被ISR修改的寄存器。 -
执行任务:
实现TSR的核心逻辑 (示例中是每隔几次中断调用,更新屏幕上的一个数字字符)。
- 幻灯片19代码解读: 使用
cntr计数,每5次中断执行一次屏幕更新。 - 幻灯片20代码解读: 直接写显存
0B800h来更新字符。
- 幻灯片19代码解读: 使用
-
发送EOI: 向8259 PIC发送中断结束命令 (如
OUT 20H, AL,AL=20H)。 -
恢复现场:
POP所有之前保存的寄存器。 -
返回: 执行
IRET。 -
(可选) 调用原ISR: 如果需要原中断功能继续执行,可以在自定义ISR中通过
JMP FAR PTR [原CS:IP]或类似方式调用之前保存的原始ISR地址。课件中提到了硬编码的JMP FAR指令 (db 0EAh ...)。
-
-
终止并驻留 (幻灯片 18 - "Now just TSR"):
- 使用DOS系统调用
INT 21H,功能号AH=31H。 AL通常设置为0 (返回码)。DX设置程序需要驻留的内存大小 (以段落 Paragraphs为单位,1段落=16字节。课件示例mov dx,1024可能指1024个段落,即16KB,或者是指字节数,具体需查阅DOS文档,但通常是段落数)。- 引用:
mov ah,031h ;TSR Entry code ah=31h,al=0h
mov al,00h
mov dx,1024 ;DX=no of bytes to remain resident(注释说bytes,但DX通常是段落数)
int 021h ;Terminate and stay resident.
- 使用DOS系统调用
习题—— 解答题
好的,同学!乐于效劳!我们来逐个解答这些题目,把知识点彻底搞明白。
模块一:直接屏幕访问 (考纲点 1.3)
【初级回忆】
- 彩色文本模式下,屏幕显示内存的起始段地址通常是多少?
- 答案:
0B800h
- 答案:
- 屏幕上每个字符(包括其属性)占用多少字节?
- 答案: 2字节
- 字符的ASCII码存储在奇数地址还是偶数地址?属性字节呢?
- 答案: ASCII码存储在奇数地址,属性字节存储在偶数地址。
*更准确地说,如果显存基址是B8000h,那么第一个字符的ASCII码在B800:0000(这里把B8000h理解为段:偏移的起始,但通常我们说B800h是段,偏移从0开始),其属性在B800:0001。但课件中描述的是“奇数地址存ASCII,偶数地址存属性”,这可能指相对于每个2字节单元的内部。更标准的理解是:每个字符单元占2字节,第一个字节是ASCII码,第二个字节是属性字节。所以,对于屏幕上的第N个字符(从0开始计数),其ASCII码在B8000h + N\*2,属性在B8000h + N\*2 + 1。
然而,如果严格按照幻灯片“奇数地址 (Odd addresses): 存储字符的 ASCII码 (C);偶数地址 (Even addresses): 存储字符的 属性字节 (A);存储顺序示意:C A C A ...”,这与常规的“ASCII后跟属性”相反。以幻灯片为准的话,答案是:ASCII码在奇数地址,属性字节在偶数地址。 我们以幻灯片为准。
- 答案: ASCII码存储在奇数地址,属性字节存储在偶数地址。
- 属性字节中,高4位定义什么?低4位定义什么?
- 答案: 高4位 (bits 7-4) 定义背景色 (F R G B - 闪烁, 红, 绿, 蓝)。低4位 (bits 3-0) 定义前景色 (I R G B - 亮度, 红, 绿, 蓝)。
【中级理解】
- 解释一下为什么直接访问屏幕内存会“速度快”但“硬件相关”?
- 答案:
- 速度快: 因为CPU直接读写内存,不需要通过操作系统或BIOS的中间调用,没有额外的函数调用开销和参数传递过程,操作非常直接,因此速度极快。
- 硬件相关: 因为屏幕内存的地址(如B8000h)是特定显卡硬件(如CGA, EGA, VGA文本模式)的规定。如果换了不同类型的显卡或者显卡工作在不同的模式下(如图形模式),这个地址可能就无效了,或者其含义完全不同。程序不具有可移植性。
- 答案:
- 如果要将字符 'X' 显示在屏幕的第0行(顶行)、第0列(最左边),其ASCII码应该写入哪个内存地址?其属性字节应该写入哪个地址?(假设段地址已设为0B800h,并遵循幻灯片奇数地址存ASCII,偶数地址存属性的规则)
- 答案:
- 第0行、第0列是屏幕的第一个字符位置。
- 根据幻灯片规则 (C A C A ...,奇数地址ASCII,偶数地址属性):
- 属性字节 (A) 在偶数地址:
0B800:0000 - ASCII码 (C) 在奇数地址:
0B800:0001
(如果按照更常见的 ASCII后跟属性 的规则,则是 ASCII 在 0B800:0000,属性在 0B800:0001)
- 属性字节 (A) 在偶数地址:
- 答案:
- 如果一个属性字节的值是
71h(十六进制),请描述其前景和背景色。(提示:前景亮度I=bit3, R=bit2, G=bit1, B=bit0;背景闪烁F=bit7, R=bit6, G=bit5, B=bit4。颜色顺序通常是蓝绿红)。- 答案:
71h=0111 0001(二进制)- 背景色 (高4位
0111):- Bit 7 (F - 闪烁): 0 (不闪烁)
- Bit 6 (R - 红): 1
- Bit 5 (G - 绿): 1
- Bit 4 (B - 蓝): 1
- 背景色是 R+G+B = 白色。所以背景是不闪烁的白色。
- 前景色 (低4位
0001):- Bit 3 (I - 亮度): 0 (正常亮度)
- Bit 2 (R - 红): 0
- Bit 1 (G - 绿): 0
- Bit 0 (B - 蓝): 1
- 前景色是 B = 蓝色。所以前景是正常亮度的蓝色。
- 结论: 白底蓝字。
- 答案:
【高级应用/分析】
-
请编写一个简短的汇编代码片段,将字符 'H' 以亮绿色前景、蓝色背景显示在屏幕的第10行,第20列。(假设屏幕为80列,行号和列号从0开始计数,并遵循幻灯片奇数地址存ASCII,偶数地址存属性的规则)。
-
答案:
-
计算属性字节:
-
背景:蓝色 (B=1, R=0, G=0, F=0) ->
00010000b=10h -
前景:亮绿色 (I=1, G=1, R=0, B=0) ->
00001010b=0Ah -
属性字节值 =
10h(背景) |
0Ah(前景) -- 这里幻灯片描述是高4位背景,低4位前景,所以是背景在高位,前景在低位。
- 背景 (bits 7-4): 蓝色 -> F=0, R=0, G=0, B=1 ->
0001xxxx - 前景 (bits 3-0): 亮绿色 -> I=1, R=0, G=1, B=0 ->
xxxx1010 - 属性字节 =
00011010b=1Ah
- 背景 (bits 7-4): 蓝色 -> F=0, R=0, G=0, B=1 ->
-
-
计算偏移地址:
- 每行80个字符,每个字符占2个字节。
- 第10行,第20列的字符是第
10 * 80 + 20 = 800 + 20 = 820个字符 (从0计数)。 - 该字符的内存块起始偏移是
820 * 2 = 1640(十进制) =668h(十六进制)。 - 属性字节地址 (偶地址):
1640(668h) - ASCII码地址 (奇地址):
1640 + 1 = 1641(669h)
MOV AX, 0B800H MOV ES, AX ; 设置附加段寄存器指向显存 MOV BX, 1640 ; 目标字符属性的偏移 (偶地址) MOV AL, 1AH ; 属性字节:蓝色背景 (0001xxxx), 亮绿色前景 (xxxx1010) -> 00011010b = 1Ah MOV ES:[BX], AL ; 写入属性字节 INC BX ; BX 指向 ASCII 码的偏移 (奇地址, BX = 1641) MOV AL, 'H' ; 要显示的字符 MOV ES:[BX], AL ; 写入 ASCII 码 -
-
-
“过滤程序”的概念是如何与直接写屏机制结合的?请构思一个简单的过滤逻辑(不需要写完整代码,描述思路即可),比如只允许数字字符显示在屏幕上。
- 答案:
- 结合方式: 过滤程序通常会接收输入字符(例如来自键盘或文件),然后根据特定条件判断该字符是否应该显示。如果判断结果为“是”,则通过直接写屏机制将该字符(及其期望的属性)写入屏幕内存的适当位置。如果为“否”,则不写入或写入一个替代字符(如空格)。
- 简单过滤逻辑 (只允许数字字符):
- 获取输入: 从某个来源(如键盘缓冲区,由
DL寄存器传入等)获取一个字符的ASCII码。 - 条件判断:
- 检查该ASCII码是否在 '0' (ASCII 30h) 和 '9' (ASCII 39h) 之间。
- 即:
IF (char_ascii >= '0') AND (char_ascii <= '9') THEN
- 处理:
- 如果符合条件 (是数字):
- 计算目标屏幕位置的偏移地址。
- 设置字符属性(如默认颜色)。
- 使用直接写屏方式,将该数字字符的ASCII码和属性字节写入屏幕内存。
- 更新下一个字符的屏幕写入位置(如光标右移)。
- 如果不符合条件 (不是数字):
- 可以选择忽略该字符(不写入任何内容,光标不移动)。
- 或者写入一个空格字符以覆盖之前可能存在的字符,然后光标右移。
- 或者发出某种提示音。
- 如果符合条件 (是数字):
- 获取输入: 从某个来源(如键盘缓冲区,由
- 答案:
模块二:触发器与三态设备 (I/O端口) (考纲点 3.4)
【初级回忆】
-
输入端口主要使用哪种核心组件?其主要作用是什么?
- 答案: 核心组件:三态缓冲器 (Tri-state buffers)。主要作用:允许多个输入设备共享数据总线,只有被选中的设备才将数据放到总线上。
-
输出端口主要使用哪种核心组件?其主要作用是什么?
- 答案: 核心组件:D型触发器 (D Type Flip Flops) / 锁存器 (Latches)。主要作用:锁存并保持CPU输出到外部设备的数据,为外部设备提供稳定的数据信号。
-
IN AL, DX指令是从端口读取数据还是向端口写入数据?数据流向是怎样的?- 答案: 是从端口读取数据。数据流向:从DX寄存器指定的I/O端口 -> CPU内部数据总线 -> AL寄存器。
-
课件中对
OUT acc, port指令的描述有一个小错误,请指出并更正。-
答案:
课件原文:“OUT acc, port Writes a byte,word or double word from the port to the accumulator.”
- 错误之处: 描述成了“from the port to the accumulator”(从端口到累加器)。
- 正确描述:
OUT port, acc指令是将数据从累加器 (acc) 写到端口 (port)。
-
【中级理解】
-
请用你自己的话解释为什么输入端口需要“三态”特性,而输出端口需要“锁存”特性。可以结合数据总线共享和外部设备速度来谈。
- 答案:
- 输入端口 (三态特性): 计算机的数据总线是共享资源,很多外部设备(如键盘、鼠标、网卡)都可能需要向CPU发送数据。如果所有设备一直都把自己的数据输出到总线上,就会发生数据冲突和混乱(就像多个人同时说话)。三态缓冲器有三种状态:高电平、低电平、高阻态(相当于断开连接)。当CPU想从某个特定设备读取数据时,只有该设备对应的三态缓冲器被使能(进入导通状态),将其数据放到总线上,其他设备的三态缓冲器则处于高阻态,不干扰总线。这样就实现了总线的有序共享。
- 输出端口 (锁存特性): CPU执行OUT指令将数据发送到数据总线上的时间可能非常短暂,CPU完成输出后可能立即去执行其他任务。而外部设备(如打印机、显示控制器)可能需要较长的时间来接收和处理这些数据。如果数据只在总线上出现一瞬间,外部设备可能来不及正确读取。D型触发器(锁存器)的作用就是在CPU发出写命令的那个时刻,把数据总线上的数据“锁存”起来,并持续稳定地输出给外部设备,直到下一次CPU向该端口写入新数据为止。这样就为外部设备提供了稳定的数据信号,解决了CPU与慢速外设之间的速度匹配问题。
- 答案:
-
当CPU执行
IN指令读取一个端口时,地址译码器和CPU的控制信号(如I/O读信号)是如何协同工作的以使能正确的三态缓冲器的?- 答案:
- CPU发出端口地址: CPU将要读取的端口地址放到地址总线上。
- 地址译码器工作: 地址译码器监测地址总线。当它识别到地址总线上的地址与某个特定输入端口匹配时,会输出一个高电平的选通信号,选中该端口。
- CPU发出控制信号: 同时,CPU会发出I/O读控制信号(例如,
IO/MEM#为高电平表示I/O操作,RD#为低电平表示读操作,或者一个统一的IORQ#信号变为有效)。 - 逻辑与操作产生使能信号: 该端口的选通信号与CPU的I/O读信号进行逻辑“与”操作。只有当端口被选中且CPU发出的是I/O读命令时,这个“与”门的输出才会有效。
- 使能三态缓冲器: 这个有效的输出信号会作为对应输入端口的三态缓冲器的“输出使能 (Output Enable)”信号,激活三态缓冲器。
- 数据传输: 被激活的三态缓冲器将外部设备的数据放到CPU的数据总线上,CPU随后读取这些数据。
- 答案:
-
如果一个输出端口的D型触发器的“锁存数据 (Latch Data)”信号一直无效(例如始终为低电平),CPU通过
OUT指令写入的数据能成功稳定地输出到外部设备吗?为什么?-
答案:
不能
成功稳定地输出到外部设备。
- 原因: D型触发器(锁存器)只有在“锁存数据”信号(通常是时钟脉冲或许可信号)的有效边沿(或有效电平期间,取决于具体锁存器类型)才会将其输入端(D)的数据捕获并传递到输出端(Q),并保持该状态。如果“锁存数据”信号一直无效,那么无论CPU在数据总线上放置什么数据,D型触发器都不会更新其输出。输出端Q将保持上一次锁存时的值,或者是一个不确定的初始状态。CPU通过OUT指令放到数据总线上的数据就像一阵风吹过,无法被D型触发器“抓住”并稳定地提供给外部设备。
-
【高级应用/分析】
-
想象一个简化的系统,有两个外部设备A和B都想通过同一个8位数据总线向CPU发送数据。请简述如何使用三态缓冲器和地址译码来实现CPU能选择性地读取设备A或设备B的数据。
- 答案:
- 分配端口地址: 为设备A和设备B分配不同的I/O端口地址,例如设备A对应端口
P_A,设备B对应端口P_B。 - 连接三态缓冲器:
- 设备A的8位数据输出连接到一个8位三态缓冲器 (Buffer_A) 的输入端。Buffer_A的输出端连接到CPU的8位数据总线。
- 设备B的8位数据输出连接到另一个8位三态缓冲器 (Buffer_B) 的输入端。Buffer_B的输出端也连接到CPU的同一个8位数据总线。
- 设计地址译码器:
- 地址译码器接收CPU地址总线上的端口地址。
- 当CPU发出端口地址
P_A时,地址译码器输出一个选通信号Select_A。 - 当CPU发出端口地址
P_B时,地址译码器输出一个选通信号Select_B。
- 生成使能信号:
- 将
Select_A与CPU的I/O读信号 (IOR#) 进行逻辑与操作,得到Buffer_A的输出使能信号Enable_A。 - 将
Select_B与CPU的I/O读信号 (IOR#) 进行逻辑与操作,得到Buffer_B的输出使能信号Enable_B。
- 将
- CPU读取操作:
- 当CPU执行
IN AL, P_A时:地址总线上是P_A,Select_A有效,IOR#有效,因此Enable_A有效。Buffer_A被激活,设备A的数据放到数据总线上,被CPU读入AL。此时Select_B无效,Enable_B无效,Buffer_B处于高阻态。 - 当CPU执行
IN AL, P_B时:地址总线上是P_B,Select_B有效,IOR#有效,因此Enable_B有效。Buffer_B被激活,设备B的数据放到数据总线上,被CPU读入AL。此时Select_A无效,Enable_A无效,Buffer_A处于高阻态。
通过这种方式,CPU可以通过指定不同的端口地址来选择性地读取设备A或设备B的数据,而不会发生数据冲突。
- 当CPU执行
- 分配端口地址: 为设备A和设备B分配不同的I/O端口地址,例如设备A对应端口
- 答案:
-
如果一个输出端口连接到一个需要稳定高电平至少100毫秒才能启动的外部设备,而CPU执行OUT指令后很快就去执行其他任务了。D型触发器在这里起到了什么关键作用来确保设备能正确启动?
-
答案:
D型触发器(锁存器)的关键作用是
数据保持
。
- 当CPU执行
OUT指令时,它会将一个控制信号(比如一个高电平,用于启动外部设备)放到数据总线上,并通过地址译码和I/O写信号使能该输出端口的D型触发器,将这个高电平锁存到触发器的输出端Q。 - 即使CPU在执行完
OUT指令后立即去处理其他任务,不再维持数据总线上的高电平,但由于D型触发器的锁存特性,其输出端Q会持续保持这个高电平状态。 - 这个稳定保持的高电平信号会一直提供给外部设备,直到CPU下一次向该端口执行
OUT指令并写入新的数据(例如一个低电平来停止设备)为止。 - 因此,只要CPU通过
OUT指令将启动信号(高电平)锁存到D型触发器中,这个高电平就会稳定地输出足够长的时间(远超CPU执行指令的时间),满足外部设备所需的100毫秒启动时间要求,从而确保设备能正确启动。D型触发器充当了CPU高速操作与外部设备慢速响应之间的缓冲和状态保持器。
- 当CPU执行
-
模块三:延时程序设计 (考纲点 4.3)
【初级回忆】
- 在中断服务程序 (ISR) 中直接使用长时间的循环延时有什么主要风险?
- 答案: 可能导致中断嵌套、堆栈溢出或系统响应迟缓 (甚至死机)。因为ISR执行期间通常会屏蔽其他同级或低优先级中断,长时间占用CPU会导致其他中断得不到及时响应。
- 除了软件循环,课件中提到了哪种更安全的在ISR中实现延时效果的方法?
- 答案: 通过计数中断次数来实现延时。
LOOP指令通常与哪个寄存器配合使用来实现循环计数?- 答案:
CX寄存器。
- 答案:
【中级理解】
-
为什么说“If the service routine takes longer than 50mS to run then a second call to the interrupt will occur whilst the first is occuring”?这种情况可能导致什么具体问题?
- 答案:
- 原因: 很多周期性中断(如时钟中断,通常每秒约18.2次,即约55ms一次)是按照固定频率发生的。如果一个中断服务程序(ISR)的执行时间超过了这个中断周期(例如,时钟中断周期是55ms,而ISR执行了60ms),那么在前一个ISR还没有执行完毕并返回(
IRET)之前,下一次同样的中断请求就已经到来了。 - 可能导致的问题:
- 中断嵌套(Re-entrancy issues): 如果CPU允许中断嵌套(即在执行一个ISR时响应新的中断),并且该ISR不是可重入的(即ISR内部使用了共享资源或静态变量而没有做保护),那么当同一个ISR被再次进入时,可能会破坏第一次调用时的数据,导致逻辑错误。
- 堆栈溢出 (Stack Overflow): 每次中断发生时,CPU会自动将Flags, CS, IP压栈。如果ISR内部还 PUSH 了其他寄存器,而中断不断嵌套(旧的没执行完,新的又来了),堆栈会持续增长,最终可能耗尽分配给堆栈的空间,导致堆栈溢出,系统崩溃。
- 系统响应迟缓/死锁: 如果ISR执行时间过长且屏蔽了其他中断,那么其他需要CPU服务的事件(如键盘输入、磁盘操作完成等)将无法得到及时处理,导致系统看起来卡顿或无响应。
- 原因: 很多周期性中断(如时钟中断,通常每秒约18.2次,即约55ms一次)是按照固定频率发生的。如果一个中断服务程序(ISR)的执行时间超过了这个中断周期(例如,时钟中断周期是55ms,而ISR执行了60ms),那么在前一个ISR还没有执行完毕并返回(
- 答案:
-
解释
delay子程序中内外两层循环是如何共同作用以产生较长延时的。- 答案:
- 内层循环 (
dly2: loop dly2):CX寄存器被初始化为一个较大的值 (如20000)。loop dly2指令会执行CX次空操作(实际上loop指令本身会消耗一定的CPU时钟周期)。这一层循环的目的是消耗一小段固定的时间。 - 外层循环 (
dly1: ... dec bx; jnz dly1):BX寄存器也被初始化为一个较大的值 (如25000)。外层循环每执行一次,就会完整地执行一遍内层循环。也就是说,内层循环所消耗的那一小段固定时间,会被重复执行BX次。 - 共同作用: 通过这种嵌套结构,总的延时时间约等于 (内层循环单次时间 × 外层循环次数)。因为
CX和BX的值都很大,所以总的循环次数是一个非常大的数字 (20000 × 25000 = 5亿次loop指令的执行,再加上外层循环的开销),从而消耗大量的CPU时间,达到较长延时的效果。
- 内层循环 (
- 答案:
-
CPU的时钟频率越高,执行相同的延时循环代码,实际延时时间会变长还是变短?为什么?
-
答案:
实际延时时间会
变短
。
- 原因: 指令的执行需要特定数量的机器周期(或时钟周期)。机器周期的时间是CPU时钟频率的倒数 (机器周期时间 = 1 / CPU时钟频率)。
- 当CPU时钟频率越高时,每个时钟周期的时间就越短。
- 延时循环代码的总机器周期数是固定的(由指令序列决定)。
- 总延时 = (总机器周期数) × (每个机器周期的时间)。
- 由于总机器周期数不变,而每个机器周期的时间变短了,所以总的延时时间也会相应变短。程序会“跑得更快”。
-
【高级应用/分析】
-
给定的
delay子程序中,内层循环CX初始值为20000,外层循环BX初始值为25000。假设LOOP指令本身消耗L个时钟周期,DEC BX消耗D个,JNZ消耗J个(跳转时)。请写出总延时大约消耗多少时钟周期的表达式(忽略PUSH/POP/RET和MOV的开销)。- 答案:
- 内层循环执行
CX(20000) 次LOOP指令。所以内层消耗20000 * L个时钟周期。 - 外层循环执行
BX(25000) 次。 - 每次外层循环,会执行一次完整的内层循环、一次
DEC BX、一次JNZ dly1。 - 所以,总时钟周期约等于:
BX_initial * (CX_initial * L + D + J)
= 25000 * (20000 * L + D + J) - 更精确一点,
JNZ最后一次不跳转时消耗的时钟周期可能不同,但对于如此大的循环次数,这个差异可以忽略不计。MOV CX, 20000也会在每次外层循环执行。如果也考虑它 (设为M_cx):
= 25000 * (M_cx + 20000 * L + D + J)
- 内层循环执行
- 答案:
-
如果要求你设计一个TSR程序,它需要在后台每隔大约1秒钟在屏幕特定位置更新一个字符。你会选择在ISR中使用软件延时循环,还是计数时钟中断次数?详细说明你的理由。
-
答案:
我会选择
计数时钟中断次数
。
- 理由:
- ISR执行效率和系统响应性: 时钟中断(如INT 8H,在PC上通常约18.2次/秒)的ISR应该尽可能快地执行完毕并返回,以确保系统其他部分的正常运行和其他中断的及时响应。在ISR中使用软件延时循环(尤其是长达1秒的延时)会长时间占用CPU,阻塞其他中断,导致系统性能急剧下降甚至死锁,这是绝对不可取的。课件也明确警告“The isr should be quick and contain no delays.”
- 精确性和稳定性: 软件延时循环的实际延时时间会受到CPU型号、时钟频率、系统总线速度甚至其他硬件活动(如DMA)的影响,不够精确且可移植性差。而时钟中断是由硬件产生的相对稳定的时间基准。通过对时钟中断进行计数,可以获得更可靠和可预测的延时。例如,如果时钟中断每秒发生18.2次,那么计数18或19次中断就大约是1秒。
- 避免中断嵌套和堆栈问题: 如前所述,ISR中的长延时很容易引发中断嵌套和堆栈溢出问题。计数中断次数则将延时任务分解到多次中断事件中,每次ISR执行时间都很短,避免了这些风险。
- 实现思路:
- 在TSR的ISR中设置一个静态计数器变量。
- 每次进入时钟中断ISR时,该计数器减1(或加1)。
- 判断计数器是否达到预设值(例如,如果从18减到0,或从0加到18)。
- 如果达到预设值,则执行屏幕更新字符的操作,并重置计数器。
- 无论是否执行更新操作,ISR都应快速结束并返回。
- 理由:
-
模块四:中断系统 (考纲点 6)
分节1:中断类型与机制 (6.1)
【初级回忆】
-
8086 CPU支持哪三种主要的中断类型?请各举一个例子。
- 答案:
- 硬件中断 (Hardware Interrupts): 由外部设备触发。例:键盘按下产生的中断 (通常通过IRQ1)。
- 异常中断 (Exception Interrupts) / 内部中断: CPU执行指令时发生错误。例:除以零错误 (INT 0)。
- 软件中断 (Software Interrupts): 由执行
INT n指令引起。例:调用DOS功能INT 21H。
- 答案:
-
当CPU响应一个硬件中断时,它会自动将哪些寄存器的内容压入堆栈?
- 答案: 标志寄存器 (Flags),当前代码段寄存器 (CS),当前指令指针 (IP)。
-
中断服务程序 (ISR) 通常以哪条指令结束?这条指令有什么作用?
-
答案:
以
IRET(Interrupt Return) 指令结束。
- 作用: 自动从堆栈中弹出 IP、CS、Flags 的值到对应的寄存器,从而恢复中断发生前的程序执行点和处理器状态,使主程序能继续执行。
-
【中级理解】
- 请详细描述从外部设备通过8259A向CPU请求中断,到CPU开始执行ISR之前的完整硬件中断处理步骤(至少列出5-6个关键步骤)。
- 答案:
- 外部设备请求中断: 外部设备通过拉低连接到8259A PIC的某条IRQ线 (如IRQx) 来发出中断请求。
- 8259A处理请求并通知CPU: 8259A检测到IRQ请求,如果该IRQ未被屏蔽且优先级高于当前正在服务的中断(或无中断服务),它会通过
INTR线向CPU发送中断请求信号。 - CPU响应中断: 如果CPU的IF标志位为1(允许中断),在当前指令执行完毕后,CPU会通过
INTA#(中断应答) 信号线回应8259A。通常会发两次INTA#信号。 - 8259A提供中断类型号: 在第二个
INTA#信号期间,8259A将与该IRQ对应的预设的8位中断类型号 (vector number, 0-255) 放到数据总线上。 - CPU读取中断类型号: CPU从数据总线上读取该中断类型号。
- CPU自动压栈: CPU将当前的标志寄存器 (Flags)、代码段寄存器 (CS) 和指令指针 (IP) 依次压入堆栈。同时,CPU清除IF位和TF位,以防止在进入ISR的初始阶段被其他可屏蔽中断打扰。
- CPU获取ISR入口地址: CPU使用读取到的中断类型号乘以4,得到该中断向量在中断向量表 (IVT) 中的地址。然后从该IVT地址处读取4个字节,这4个字节是对应ISR的入口地址 (先是IP,后是CS)。
- CPU跳转执行ISR: CPU将从IVT中获取到的新IP和CS值加载到IP和CS寄存器中,从而跳转到中断服务程序的起始位置开始执行。
- 答案:
- 软件中断
INT n和子程序调用CALL有什么相似之处和主要区别?- 答案:
- 相似之处:
- 改变执行流程: 两者都会使CPU暂停当前指令序列的执行,跳转到另一个地址去执行一段代码。
- 返回机制: 两者都有返回机制,执行完目标代码后可以返回到原来的执行点之后继续执行 (
INT n用IRET返回,CALL用RET返回)。 - 利用堆栈保存返回地址:
CALL会将下一条指令的IP (或CS:IP对于远调用) 压栈。INT n也会将Flags, CS, IP压栈。
- 主要区别:
- 触发方式:
CALL是程序中的一条普通指令,用于调用程序员定义的子程序。INT n也是一条指令,但它通常用于请求操作系统或BIOS提供的预定义服务,或者由硬件/异常间接触发(虽然这里特指软件中断指令)。 - 目标地址的来源:
CALL的目标地址直接在指令中指定(直接调用)或通过寄存器/内存间接指定。INT n的目标地址是通过中断类型号n在中断向量表 (IVT) 中查找到的。 - 压栈内容:
CALL(近调用) 只压栈IP,(远调用) 压栈CS和IP。INT n(以及所有中断) 会压栈Flags、CS和IP。 - 中断标志位(IF)的影响:
CALL指令的执行不受IF位影响。软件INT n指令的执行也不受IF位影响(它会执行)。但硬件中断的响应受IF位控制。INT n执行时,硬件会自动清除IF位(除非是陷阱门类型的中断)。 - 返回指令:
CALL使用RET或RETF返回。INT n(以及所有中断) 使用IRET返回,IRET会多弹出Flags寄存器。 - 通用性与抽象性:
CALL通常用于模块化程序设计。INT n提供了一种更通用的服务接口,调用者不需要知道服务例程的具体实现和地址,只需知道中断号即可,实现了更高层次的抽象。
- 触发方式:
- 相似之处:
- 答案:
【高级应用/分析】
-
如果CPU在响应中断时,硬件没有自动保存程序计数器 (IP) 和代码段寄存器 (CS),那么中断返回会遇到什么问题?ISR必须做什么额外的操作才能正确返回?
-
答案:
-
问题: 如果CPU硬件不自动保存IP和CS,那么当中断服务程序 (ISR) 执行完毕后,CPU将不知道应该返回到主程序的哪个位置继续执行。
IRET指令(或其他任何返回指令)将无法找到正确的返回地址,导致程序流程丢失,系统很可能会崩溃或执行到不可预知的代码区域。 -
ISR必须做的额外操作:
- 在ISR的入口处手动保存CS和IP: ISR的第一件事情就必须是读取当前的CS和IP寄存器的值,并将它们压入堆栈或者保存到预定义的内存位置。这需要特殊的指令序列,因为直接
PUSH CS是可以的,但PUSH IP不是直接的指令(通常通过CALL next_instr; POP AX; PUSH AX类似的技巧获取IP,或者CPU有其他机制获取)。 - 在ISR的出口处手动恢复CS和IP: 在ISR执行
IRET(或者一个修改过的返回指令) 之前,必须从堆栈或保存位置将之前保存的CS和IP值恢复到相应的寄存器中,或者确保堆栈顶是正确的CS和IP以供返回指令使用。
- 实际上,CPU硬件自动保存CS和IP是中断机制的核心和基础,如果硬件不做这件事,中断机制将变得非常复杂和低效,甚至难以实现。
- 在ISR的入口处手动保存CS和IP: ISR的第一件事情就必须是读取当前的CS和IP寄存器的值,并将它们压入堆栈或者保存到预定义的内存位置。这需要特殊的指令序列,因为直接
-
-
-
NMI (非可屏蔽中断) 和 INTR (可屏蔽中断) 的主要区别是什么?在什么情况下系统设计者会选择使用NMI?
-
答案:
-
主要区别:
- 可屏蔽性:
- INTR (Interrupt Request): 是可屏蔽中断。它的响应受到CPU标志寄存器中IF (Interrupt Flag) 位的控制。如果
IF=1,CPU可以响应INTR请求;如果IF=0(通过CLI指令设置),CPU会忽略INTR请求,直到IF位被重新置1 (通过STI指令)。 - NMI (Non-Maskable Interrupt): 是不可屏蔽中断。它不受IF标志位的影响。一旦NMI线上出现有效的请求信号,CPU在当前指令执行完毕后几乎总会响应它,除非正在响应另一个NMI或处于某些特殊状态(如关机序列)。
- INTR (Interrupt Request): 是可屏蔽中断。它的响应受到CPU标志寄存器中IF (Interrupt Flag) 位的控制。如果
- 优先级: NMI的优先级通常高于INTR。如果NMI和INTR同时到达,CPU会优先响应NMI。
- 触发方式: NMI通常是边沿触发(例如,从低到高的跳变),而INTR通常是电平触发(持续的有效电平)。
- 中断类型号: NMI有固定的中断类型号,通常是
INT 2。而INTR的中断类型号是由外部中断控制器 (如8259A) 在中断响应周期中提供给CPU的。 - 用途: INTR用于常规的外部设备中断,如键盘、鼠标、定时器、磁盘等。NMI用于处理非常紧急和严重的情况。
- 可屏蔽性:
-
系统设计者选择使用NMI的情况:
- 严重硬件故障检测: 如内存奇偶校验错误、总线错误、电源即将掉电等致命的硬件问题,需要CPU立即处理,不能被屏蔽。
- 调试目的: 有些调试工具或硬件监视器可能会使用NMI来强制中断CPU,以便检查系统状态。
- 系统复位或紧急关机前的处理: 在系统即将因严重错误而崩溃或需要紧急关机前,可以通过NMI执行一些关键的保存状态或记录错误信息的操作。
- 对时间要求极高的事件: 某些实时系统中,极少数对延迟有极端要求的事件可能会考虑NMI,但这种情况较少,因为NMI资源非常宝贵。
- 总的来说,NMI是为那些“无论如何都必须得到CPU关注”的最高优先级事件保留的。
-
-
分节2:中断向量表 (IVT) (6.2)
【初级回忆】
- 中断向量表 (IVT) 位于内存的哪个地址范围?总共多大?
- 答案: 位于RAM的最低1KB,地址范围从
0000:0000到0000:03FF(物理地址00000h到003FFh)。总共1024字节 (1KB)。
- 答案: 位于RAM的最低1KB,地址范围从
- IVT中每个中断向量占用多少字节?这几个字节分别存储什么信息?
- 答案: 每个中断向量占用4字节。存储对应ISR的入口地址:前2字节是IP (偏移地址),后2字节是CS (段基址)。
- 如何根据中断类型号计算其在IVT中的起始地址?
- 答案: ISR向量在IVT中的起始偏移地址 = 中断类型号 × 4。
【中级理解】
- 如果中断类型号是
10h(十六进制),那么对应的中断向量在IVT中的物理地址是多少?(假设段地址为0000h)- 答案:
- 中断类型号
10h= 十进制16。 - IVT中偏移地址 =
16 * 4 = 64(十进制) =40h(十六进制)。 - 由于IVT的段基址是
0000h,所以该中断向量的起始逻辑地址是0000:0040h。 - 其物理地址是
段基址 * 16 + 偏移地址 = 0 * 16 + 64 = 64(十进制),即00040h。
- 中断类型号
- 答案:
- 假设在IVT中地址
0000:0040h开始的4个字节依次是12h, 34h, 56h, 78h。请问这对应哪个中断类型号的ISR入口地址?该ISR的CS和IP分别是多少?(注意字节存储顺序,通常IP在前,CS在后,低字节在前)。- 答案:
- 中断类型号: IVT中偏移地址是
40h(十进制64)。中断类型号 =偏移地址 / 4 = 64 / 4 = 16(十进制) =10h(十六进制)。所以这对应INT 10h。 - ISR入口地址 (CS:IP):
- 存储顺序是 IP_low, IP_high, CS_low, CS_high。
- IP_low =
12h(在0000:0040h) - IP_high =
34h(在0000:0041h) - 所以 IP =
3412h。 - CS_low =
56h(在0000:0042h) - CS_high =
78h(在0000:0043h) - 所以 CS =
7856h。 - 该ISR的入口地址是
CS:IP = 7856:3412h。
- 中断类型号: IVT中偏移地址是
- 答案:
【高级应用/分析】
-
如果一个程序想要“挂接”系统时钟中断 (通常是INT 8),它需要修改IVT中的哪个地址范围的内容?它应该写入什么样的数据?
- 答案:
- 修改的地址范围:
- 系统时钟中断是
INT 8。 - 其在IVT中的偏移地址 =
8 * 4 = 32(十进制) =20h(十六进制)。 - 所以需要修改从
0000:0020h开始的4个字节,即0000:0020h,0000:0021h,0000:0022h,0000:0023h。
- 系统时钟中断是
- 应该写入的数据:
- 应该写入自定义的时钟中断服务程序 (NewISR) 的入口地址。
- 具体来说:
- 在
0000:0020h和0000:0021h写入 NewISR 的 IP (偏移地址) (低字节在前,高字节在后)。 - 在
0000:0022h和0000:0023h写入 NewISR 的 CS (段基址) (低字节在前,高字节在后)。
- 在
- 在修改之前,通常需要先读取并保存该位置原始的CS:IP值,以便在自定义ISR中调用或在程序退出时恢复。
- 修改的地址范围:
- 答案:
-
如果IVT中的某个向量指向了一个不存在或者错误的ISR地址(例如,指向数据区或者未初始化的内存),当对应中断发生时,系统可能会出现什么状况?
-
答案:
系统可能会出现非常严重的错误,通常会导致:
- 执行非法指令 (Invalid Opcode Exception): 如果CS:IP指向的内存区域包含的不是有效的机器指令(而是数据或垃圾值),CPU会尝试将这些数据当作指令来执行,这很可能导致“非法操作码”异常。如果这个异常本身的处理向量也有问题,可能陷入异常循环。
- 访问越界或受保护内存 (General Protection Fault / Segmentation Fault): 如果CS:IP指向一个不允许执行代码的段(如数据段设置了不可执行属性),或者IP超出了CS定义的段限长,或者访问了没有权限的内存区域,会触发保护性异常。
- 系统崩溃或死机 (System Crash / Freeze): 由于CPU开始执行不可预测的指令序列,很可能会破坏关键的系统数据结构、操作系统代码,或者进入一个无限循环,最终导致整个系统崩溃、死机或需要重启。
- 不可预料的行为 (Undefined Behavior): CPU可能会执行一些看似无害但逻辑上完全错误的指令,导致程序数据损坏,或者设备工作异常,但系统不一定立即崩溃,而是表现出各种奇怪的行为。
- 总之,IVT中向量的正确性至关重要,错误的向量是导致系统不稳定的常见原因之一。
-
分节3:栈在中断中的应用 (6.3)
【初级回忆】
- 除了CPU自动压栈的寄存器外,如果ISR中需要使用通用寄存器(如AX, BX等),谁负责保存和恢复它们?
- 答案: 程序员 (编写ISR的人) 负责保存和恢复这些通用寄存器。
- 保存通用寄存器时使用
PUSH指令,恢复时使用什么指令?顺序有要求吗?- 答案: 恢复时使用
POP指令。有顺序要求:POP的顺序必须与PUSH的顺序相反,以确保每个寄存器恢复到其原始值。
- 答案: 恢复时使用
【中级理解】
- 为什么说栈的FILO (First-In, Last-Out) 原理使其天然适合处理中断(以及嵌套调用)?
- 答案:
- 保存和恢复现场: 当中断发生(或子程序被调用)时,需要保存当前的执行状态(如返回地址、标志位、可能还有其他寄存器),以便中断(或子程序)处理完毕后能准确恢复。
PUSH操作将这些信息按顺序存入栈顶。 - 处理嵌套: 如果在执行一个ISR(或子程序A)的过程中,又发生了另一个中断(或调用了另一个子程序B),那么新的返回地址和状态信息会被压入到旧的信息之上。
- 正确返回: 当最内层的中断(或子程序B)执行完毕后,其返回信息(最后压入的)会首先从栈顶被
POP出来,确保正确返回到调用它的地方(ISR或子程序A)。然后,当外层的ISR(或子程序A)执行完毕,轮到它返回时,它对应的返回信息(较早压入的)此时也位于栈顶,也能被正确POP出来。 - FILO确保了无论有多少层嵌套,返回的顺序总是与进入的顺序相反,保证了程序执行流的正确性和现场恢复的准确性,就像正确地解开一串嵌套的括号一样。
- 保存和恢复现场: 当中断发生(或子程序被调用)时,需要保存当前的执行状态(如返回地址、标志位、可能还有其他寄存器),以便中断(或子程序)处理完毕后能准确恢复。
- 答案:
- 如果在ISR的开头
PUSH AX,PUSH BX,那么在ISR的结尾,恢复这两个寄存器的正确顺序是什么?如果顺序反了会怎么样?- 答案:
- 正确顺序:
POP BX,然后POP AX。 (与PUSH顺序相反) - 如果顺序反了 (变成
POP AX,然后POP BX):- 当执行
POP AX时,原本应该恢复到BX的值(因为BX的内容在栈中更深,是后 PUSH 的,所以先 PUSH 的 AX 的值在栈中更深,BX 的值在 AX 的值之上)会被错误地弹入AX。 - 当执行
POP BX时,原本应该恢复到AX的值会被错误地弹入BX。 - 结果就是,
AX得到了原来BX的值,BX得到了原来AX的值,两个寄存器的内容在返回主程序时被交换了。这很可能导致主程序后续的逻辑错误,因为依赖于AX和BX原始值的计算或操作会得到非预期的结果。
- 当执行
- 正确顺序:
- 答案:
【高级应用/分析】
-
如果一个ISR在执行过程中,由于某种错误(比如栈操作不当,如 PUSH/POP 不匹配,或者直接修改了SP寄存器)导致堆栈指针SP指向了错误的位置,当执行
IRET指令时,可能会发生什么?-
答案:
IRET指令期望从当前SP指向的堆栈位置依次弹出 IP、CS 和 Flags。如果SP指向了错误的位置,将会发生以下情况:
- 错误的返回地址 (IP, CS):
IRET会将SP所指内存区域的6个字节(假设是16位模式)当作 IP_low, IP_high, CS_low, CS_high, Flags_low, Flags_high (实际顺序和大小可能因模式而异,但概念一致) 来加载。如果SP错误,这些字节的内容将是随机的或者不相关的数据。CPU会跳转到一个完全错误的、不可预期的地址,其行为与“IVT向量错误”类似,可能导致执行非法指令、访问越界、系统崩溃等。 - 错误的处理器状态 (Flags): 弹出的Flags值也是错误的,这将导致CPU的各种状态位(如零标志、进位标志、中断允许标志IF等)被设置成不可预料的状态,进一步扰乱后续程序的执行。例如,如果IF位被错误地清零,可能会导致后续中断无法响应。
- 堆栈不平衡: 即使没有立即崩溃,错误的SP也意味着堆栈的状态被破坏了。如果该ISR是从另一个程序或ISR中调用的,返回后调用者的堆栈也是混乱的,可能导致更深层次的错误。
- 总之,堆栈指针的正确性对
IRET的正常工作至关重要,SP错误几乎必然导致灾难性后果。
- 错误的返回地址 (IP, CS):
-
-
画一个简图描述:主程序执行 -> 发生中断 -> CPU自动压栈 -> ISR开始执行并PUSH了2个通用寄存器 -> ISR执行完毕POP了2个通用寄存器 -> 执行IRET -> 返回主程序。标出关键时刻栈顶内容的变化。
-
答案: (假设栈向下增长,SP指向栈顶元素)
初始状态 (主程序执行中): Stack: | ... (其他数据) ... | ^ SP 1. 发生中断,CPU自动压栈: Stack: | ... | Flags_orig | CS_orig | IP_orig | <-- (IP_orig是栈顶) ^ SP (SP已减小) 2. ISR开始执行,PUSH AX, PUSH BX: Stack: | ... | Flags_orig | CS_orig | IP_orig | AX_val | BX_val | <-- (BX_val是栈顶) ^ SP (SP进一步减小) (ISR核心代码执行...) 3. ISR执行完毕,POP BX, POP AX: Stack: | ... | Flags_orig | CS_orig | IP_orig | <-- (IP_orig是栈顶) ^ SP (SP恢复到PUSH AX,BX之前) (BX_val -> BX, AX_val -> AX) 4. 执行 IRET: Stack: | ... (其他数据) ... | ^ SP (SP恢复到中断发生前) (IP_orig -> IP, CS_orig -> CS, Flags_orig -> Flags) 5. 返回主程序继续执行。
-
分节4:多源中断处理 (通过8259 PIC) (6.4)
【初级回忆】
-
为什么8086系统通常需要8259 PIC芯片?
- 答案: 因为8086 CPU本身只有少数几个硬件中断输入引脚 (如INTR, NMI),无法直接处理来自多个外部设备的中断请求。8259 PIC可以扩展CPU处理硬件中断的能力,管理多个中断源。
-
什么是8259的“级联”?它有什么作用?
-
答案:
级联 (Cascading)
是指将一个8259 PIC (从片 Slave) 的中断输出连接到另一个8259 PIC (主片 Master) 的一个IRQ输入上。
- 作用: 进一步扩展系统可以管理的硬件中断源的数量。例如,一片8259可管理8个IRQ,两片级联通常可以管理多达15个IRQ。
-
-
8259A的IMR (中断屏蔽寄存器) 的作用是什么?
- 答案: 允许通过编程来屏蔽 (禁止) 或允许特定的IRQ请求。IMR的每一位对应一条IRQ线,设置某位可以禁止对应的外部中断。
-
ISR在结束前,通常需要向8259A发送什么命令?这个命令的作用是什么?
-
答案:
通常需要发送
EOI (End Of Interrupt - 中断结束)
命令。
- 作用: 通知8259A该中断已经处理完毕,使其可以响应其他同级或低优先级的中断请求。如果没有EOI,8259A可能会一直认为该中断仍在服务中,从而阻塞其他中断。
-
【中级理解】
-
解释主片8259A和从片8259A是如何协同工作以处理超过8个外部中断请求的。
-
答案:
- 连接方式: 主片8259A的INTR输出直接连接到CPU的INTR引脚。从片8259A的INTR输出连接到主片8259A的一个IRQ输入引脚(通常是IRQ2)。
- 中断请求传递:
- 当连接到主片的设备(如IRQ0-IRQ1, IRQ3-IRQ7)产生中断时,主片直接向CPU请求中断。
- 当连接到从片的设备(如IRQ8-IRQ15)产生中断时,从片会向主片发出中断请求(通过主片的IRQ2)。
- 主片处理来自从片的请求: 主片接收到来自从片IRQ2的请求后,如果该请求未被屏蔽且优先级足够,主片会向CPU的INTR引脚发送中断请求。
- CPU响应与中断类型号:
- 当CPU响应主片的中断请求并发回INTA#信号时,如果中断源是主片自身管理的IRQ,主片直接将对应的中断类型号放到数据总线上。
- 如果中断源是来自从片的(即主片的IRQ2被触发),主片会识别出这一点。在CPU的INTA#周期,主片会指示从片将其中断类型号放到数据总线上。这通常涉及到主片在初始化时被告知IRQ2连接了从片,并且从片也需要被正确初始化以响应主片的查询并提供自己的中断向量。
- EOI处理: 对于来自从片的中断,ISR在结束时通常需要先向从片发送EOI,然后再向主片发送EOI。
- 通过这种方式,主片充当了CPU和从片之间的中介,有效地将从片管理的多个中断源汇集到主片的一个输入上,从而扩展了系统的中断处理能力。
-
-
当一个外部设备拉低连接到8259A的某条IRQ线时,8259A在整个中断响应过程中扮演了哪些关键角色?(直到CPU获取中断类型号)
- 答案:
- 中断请求检测 (Interrupt Request Register - IRR): 8259A的IRR会记录哪条IRQ线被拉低,表示有中断请求。
- 中断屏蔽判断 (Interrupt Mask Register - IMR): 8259A检查IMR,确认该IRQ线对应的中断是否被屏蔽。如果被屏蔽,则忽略该请求。
- 优先级仲裁 (Priority Resolver): 如果未被屏蔽,8259A的优先级解析器会比较新来的IRQ请求与当前正在服务的中断(记录在In-Service Register - ISR)以及其他挂起的请求的优先级。
- 向CPU发送INTR信号: 如果新请求的优先级足够高(例如,高于当前服务的中断,或者没有中断在服务),8259A会通过其INT输出引脚向CPU的INTR引脚发送一个高电平信号,请求CPU中断。
- 等待CPU响应 (INTA#): 8259A等待CPU通过INTA#(中断应答)信号线做出响应。
- 设置ISR位: 当CPU响应后(通常在第一个INTA#脉冲),8259A会将对应IRQ的位在ISR(服务中寄存器)中置位,表示该中断正在被服务。同时,如果工作在自动EOI模式下,IRR中对应的位会被清除;如果不是,则IRR位保持直到收到EOI。
- 提供中断类型号: 在第二个INTA#脉冲期间,8259A负责将预编程的、与被响应的IRQ线对应的8位中断类型号(vector number)放到数据总线上,供CPU读取。
- 答案:
-
BIOS在系统启动时对8259A进行初始化,主要会设置哪些内容?为什么这些设置是必要的?
-
答案:
BIOS对8259A的初始化主要设置以下内容:
- 工作模式 (Initialization Command Words - ICWs):
- ICW1: 设置是否级联、是边沿触发还是电平触发、是否需要ICW4等。
- ICW2: 设置中断向量基址。即IRQ0对应的中断类型号是多少,后续IRQ的中断类型号依次递增。例如,设置主片IRQ0对应INT 08H,则IRQ1对应INT 09H,以此类推。从片也需要设置自己的中断向量基址。
- ICW3 (级联时需要): 对于主片,设置哪条IRQ线连接了从片。对于从片,设置它自己连接到主片的哪条IRQ线(用一个ID表示)。
- ICW4 (如果ICW1中指定需要): 设置是否为8086/88模式、是否自动EOI、缓冲模式、是否特殊全嵌套模式等。
- 中断屏蔽 (Operation Command Words - OCWs, e.g., OCW1 for IMR): 通常BIOS会初始化IMR,可能会先屏蔽所有中断,然后有选择地打开一些必要的中断(如时钟中断、键盘中断)。
- 为什么这些设置是必要的:
- 唯一标识中断源: CPU通过中断类型号来查找IVT中的ISR地址。必须为每个IRQ源分配一个唯一的中断类型号,否则CPU无法区分不同的中断。ICW2就是做这个映射的。
- 级联协调: 在级联系统中,主从片必须知道彼此的连接关系(通过ICW3),才能正确地传递中断请求和中断类型号。
- 适应系统环境: 8259A有多种工作模式(通过ICW1, ICW4设置),需要根据CPU类型(如8086)、中断触发方式和期望的中断处理行为(如是否自动EOI)进行配置,以确保其与系统其他部分正确协同工作。
- 控制中断流: 通过IMR,系统可以有选择地允许或禁止某些中断,这对于系统稳定运行和调试非常重要。
- 没有正确的初始化,8259A将无法正确地管理中断请求、进行优先级判断、向CPU提供正确的中断类型号,整个硬件中断系统将无法工作。
- 工作模式 (Initialization Command Words - ICWs):
-
-
如果一个ISR忘记向8259A发送EOI命令,可能会对后续的中断处理产生什么影响?(特别是同级或低优先级中断)
-
答案:
如果ISR忘记发送EOI命令,8259A的ISR(服务中寄存器)中对应于刚刚被服务的那个IRQ的位将不会被清除。这意味着8259A会认为这个中断仍然在被CPU处理。
- 对同级中断的影响: 在默认的优先级模式下(如全嵌套模式),如果一个中断正在被服务(其ISR位被置位),那么其他同优先级的请求将不会被再次提交给CPU,直到当前中断的ISR位被清除(通过EOI)。所以,忘记发EOI会导致后续的同级中断被阻塞。
- 对低优先级中断的影响: 同样,在服务一个较高优先级的中断时,低优先级的中断请求会被挂起。如果高优先级中断的ISR忘记发送EOI,其ISR位一直保持置位,那么所有低于该优先级的中断请求将持续被阻塞,无法得到响应。
- 对更高优先级中断的影响(通常不受影响): 更高优先级的中断请求通常仍然可以打断当前(忘记发EOI的)ISR(如果CPU的IF位允许,并且8259A配置为允许嵌套),因为8259A的优先级机制允许这样做。
- 特殊情况(如特殊全嵌套模式或旋转优先级模式): 影响可能略有不同,但核心问题是ISR位未被清除,导致8259A的内部状态与实际中断处理状态不一致。
- 总结: 忘记发送EOI会导致8259A无法正确地更新其内部状态,从而可能阻塞同级和所有低优先级的中断请求,严重影响系统的中断响应能力和实时性。在某些情况下,如果该中断源持续发出请求,甚至可能导致系统表现得像死锁一样,因为CPU再也收不到来自该8259A的某些中断信号了。
-
【高级应用/分析】
-
假设主8259A的IMR端口是21H。如果要屏蔽IRQ3和IRQ5,同时允许其他中断(IRQ0,1,2,4,6,7),应该向21H端口写入什么十六进制值?(假设IRQ0对应bit0,IRQ7对应bit7,置1屏蔽)。
-
答案:
-
IMR是一个8位寄存器,bit0对应IRQ0,bit1对应IRQ1,...,bit7对应IRQ7。
-
置1表示屏蔽该IRQ,置0表示允许该IRQ。
-
要屏蔽IRQ3和IRQ5,意味着bit3和bit5要为1。
-
要允许IRQ0, IRQ1, IRQ2, IRQ4, IRQ6, IRQ7,意味着bit0, bit1, bit2, bit4, bit6, bit7要为0。
-
所以IMR的二进制值应该是 (bit7 ... bit0):
0101000(这里应该是8位,我们补全) ->
00101000- Bit 7 (IRQ7): 0 (允许)
- Bit 6 (IRQ6): 0 (允许)
- Bit 5 (IRQ5): 1 (屏蔽)
- Bit 4 (IRQ4): 0 (允许)
- Bit 3 (IRQ3): 1 (屏蔽)
- Bit 2 (IRQ2): 0 (允许)
- Bit 1 (IRQ1): 0 (允许)
- Bit 0 (IRQ0): 0 (允许)
-
二进制值:
00101000b -
转换为十六进制:
0010是2,1000是8。所以是28h。 -
因此,应该向21H端口写入
28h。
MOV AL, 28H OUT 21H, AL -
-
-
在一个级联的8259A系统中,如果一个中断请求来自从片的IRQ10,那么ISR在发送EOI时,通常需要对主片和从片都进行操作。请简述为什么以及大致的操作顺序。
- 答案:
- 为什么需要对两者都操作:
- 从片: 中断请求直接起源于从片所连接的设备 (IRQ10)。从片的ISR(服务中寄存器)中对应IRQ10的位被置位了。必须向从片发送EOI,以清除从片内部的这个ISR位,使其能够响应后续来自其管辖范围内的其他中断。
- 主片: 从片的中断请求是通过主片的一条IRQ线(如IRQ2)传递给主片的。因此,主片的ISR中对应于这条级联线(IRQ2)的位也被置位了。也必须向主片发送EOI,以清除主片内部这个级联线的ISR位,使主片能够响应后续来自其他IRQ线(包括再次来自从片的请求)的中断。
- 大致的操作顺序:
- 先向从片发送EOI: ISR执行完毕后,首先向从片8259A的命令端口 (如A0H) 发送EOI命令 (通常是写入20H)。这会清除从片内部对应IRQ10的ISR位。
- 再向主片发送EOI: 然后,再向主片8259A的命令端口 (如20H) 发送EOI命令 (通常也是写入20H)。这会清除主片内部对应级联线 (如IRQ2) 的ISR位。
- 顺序的重要性: 通常建议先对从片发EOI,再对主片发EOI。如果顺序颠倒,在某些情况下(尤其是在电平触发模式或某些特定优先级配置下),主片清除了级联线的ISR位后,如果从片此时仍有未处理完的中断请求(因为还没收到EOI),它可能会立即再次通过级联线向主片请求中断,可能导致不期望的行为。先清除从片的ISR位能确保从片内部状态先更新。
- 为什么需要对两者都操作:
- 答案:
-
如果两个外部设备共享同一条IRQ线(虽然不推荐,但假设发生了),当这条IRQ线被激活时,8259A和CPU会如何响应?系统如何区分是哪个设备产生的中断?(提示:可能需要在ISR中进一步查询设备状态)
- 答案:
- 8259A和CPU的响应:
- 当共享的IRQ线被任一设备激活(拉低)时,8259A会像处理单个设备的中断一样进行响应:检查屏蔽位、进行优先级判断,如果条件满足,就向CPU发送INTR信号。
- CPU响应后,8259A会将与该共享IRQ线对应的中断类型号提供给CPU。
- CPU根据中断类型号跳转到对应的ISR。
- 从8259A和CPU的层面来看,它们无法直接区分是共享线路上的哪个设备触发了中断,它们只知道该IRQ线有请求。
- 系统如何区分是哪个设备产生的中断:
- ISR的责任: 区分任务落在了为该共享IRQ编写的ISR身上。
- 查询设备状态寄存器: ISR必须轮询 (poll) 或检查连接到该共享IRQ线上的每一个可能设备的状态寄存器。每个设备通常都有一个或多个状态寄存器,其中会有特定的位指示该设备是否请求了中断,或者是否有数据准备好/操作完成等。
- 识别中断源: ISR会依次读取每个共享设备的状态寄存器,找到那个表明自己是中断源的设备。
- 服务中断: 一旦确定了是哪个设备产生了中断,ISR就执行针对该设备的特定处理代码。
- 处理多个潜在源: 如果多个共享设备可能同时请求中断(虽然概率较低,但可能发生),ISR需要设计成能够处理这种情况,例如,依次服务所有发出请求的设备,或者根据预设的次级优先级来服务。
- 清除中断请求: ISR在服务完设备后,除了向8259A发送EOI,还必须对产生中断的设备执行相应的操作来清除其内部的中断请求状态(例如,读取数据寄存器、向命令寄存器写入特定值等),否则该设备可能会持续拉低IRQ线,导致中断不断发生。
- 缺点: 共享IRQ线会增加ISR的复杂性,降低中断处理效率(因为需要轮询),并可能引入潜在的竞争条件和难以调试的问题。因此,在硬件设计中应尽可能避免IRQ共享,除非设备本身支持良好的共享机制(如PCI设备的中断共享)。
- 8259A和CPU的响应:
- 答案:
模块五:TSR (Terminate and Stay Resident) 程序
【初级回忆】
- TSR程序的基本思想是什么?它执行完毕后代码还在内存中吗?
- 答案: TSR程序的基本思想是程序在终止其主执行流程后,其部分代码仍然驻留在内存中,并且通常通过挂接(截获)某个系统中断,在特定中断发生时被激活执行。是的,它执行完毕后(通过特定DOS调用终止),部分代码仍在内存中。
- TSR中的例程通常是如何被调用的?
- 答案: 通常是通过中断 (interrupts) 调用的。TSR程序会修改中断向量表,将某个中断的处理指向TSR内部的例程。
- 终止并驻留内存的DOS功能调用是
INT 21H的哪个功能号 (AH的值)?DX寄存器此时的作用是什么?- 答案: 功能号是
AH=31H。DX寄存器此时的作用是指定程序需要驻留在内存中的大小 (以段落 Paragraphs为单位,1段落=16字节)。
- 答案: 功能号是
【中级理解】
-
请简述实现一个TSR程序(例如挂接时钟中断INT 8)的主要步骤。
- 答案:
- 保存原始中断向量: 在修改IVT之前,必须先读取并保存目标中断号(如INT 8)的原始ISR入口地址 (CS和IP)。这通常通过DOS功能调用
INT 21H, AH=35h或直接读取IVT内存实现。 - 设置新的中断向量: 将自定义的ISR的入口地址写入IVT中目标中断号对应的位置,替换掉原始向量。这通常通过DOS功能调用
INT 21H, AH=25h或直接写入IVT内存实现。 - 编写自定义ISR:
- 保存现场: 在ISR开头,使用
PUSH指令保存所有会被ISR修改的CPU寄存器(特别是通用寄存器和段寄存器)。 - 执行TSR核心任务: 实现TSR程序的核心逻辑(例如,计数、更新屏幕、检查特定条件等)。
- 发送EOI: 如果挂接的是硬件中断(如时钟中断INT 8,它来自IRQ0),必须向8259 PIC发送中断结束命令 (EOI)。
- 调用原始ISR (可选但推荐): 为了不破坏系统原有功能,通常需要在自定义ISR中通过
JMP FAR PTR [原始CS:IP]或类似方式调用之前保存的原始ISR。 - 恢复现场: 在ISR结束前,使用
POP指令按相反顺序恢复所有之前保存的寄存器。 - 返回: 使用
IRET指令从中断返回。
- 保存现场: 在ISR开头,使用
- 终止并驻留: 程序主体完成上述设置后,调用DOS功能
INT 21H, AH=31H,并在DX中指定需要驻留的内存大小(从程序起始到ISR结束以及所需数据区的大小,以段落为单位),使程序终止但其指定部分保留在内存中。
- 保存原始中断向量: 在修改IVT之前,必须先读取并保存目标中断号(如INT 8)的原始ISR入口地址 (CS和IP)。这通常通过DOS功能调用
- 答案:
-
在TSR的自定义ISR中,为什么通常建议调用原始的中断服务程序?如果不调用会怎么样?
- 答案:
- 为什么建议调用: 许多系统中断(如时钟中断、键盘中断)都执行着对操作系统或硬件至关重要的底层任务。例如,时钟中断可能负责更新系统时间、进行任务调度、处理定时事件等。键盘中断负责读取按键、管理键盘缓冲区等。如果自定义ISR完全取代了原始ISR而不去调用它,这些重要的系统功能就会丢失或被破坏。
- 如果不调用会怎么样:
- 系统功能异常: 如果挂接的是时钟中断而不调用原ISR,系统时间可能停止更新,依赖于定时器的任务可能无法执行。
- 设备失灵: 如果挂接的是键盘中断而不调用原ISR,键盘输入可能完全失效,或者操作系统无法正确处理按键。
- 系统不稳定甚至崩溃: 某些中断处理是维持系统稳定运行所必需的。剥夺这些中断的原始处理逻辑可能导致不可预测的错误,甚至系统崩溃。
- 调用原始ISR可以确保在执行完自定义的TSR任务后,系统原有的功能仍能继续正常执行,TSR程序就像是在原有功能链条中增加了一个环节,而不是完全替换掉它。
- 答案:
-
在设置新的中断向量之前,为什么必须先保存原始的中断向量?
-
答案:
主要有两个原因:
- 能够调用原始ISR: 如上所述,很多时候我们的TSR需要在完成自己的任务后,再让系统执行原来的中断处理逻辑,以保证系统功能的完整性。如果不先保存原始向量,就无法知道原始ISR的入口地址,也就无法调用它了。
- 程序卸载时恢复IVT: 一个行为良好的TSR程序在未来可能需要被“卸载”(尽管DOS时代卸载TSR比较复杂,但理论上应该考虑)。卸载时,需要将IVT中被修改的向量恢复成原始值,否则IVT中会留下一个指向不再存在的TSR代码的“野指针”,当中断发生时会导致系统崩溃。保存了原始向量,才能在卸载时正确恢复。
-
【高级应用/分析】
-
如果一个TSR程序在驻留时,
DX寄存器中设置的驻留大小小于其实际代码和数据所需的空间,可能会发生什么问题?如果设置得过大呢?- 答案:
- 驻留大小小于实际所需 (
DX过小):- 代码被覆盖: 当TSR通过
INT 21H, AH=31H终止并驻留时,DOS会根据DX指定的大小来保留内存。如果DX过小,意味着TSR程序中位于驻留区域之外的代码或数据部分没有被DOS保护起来。 - 后续程序加载时可能覆盖TSR: 当后续其他程序被加载到内存时,DOS的内存管理器可能会认为这部分“未被驻留”的TSR代码区域是空闲的,从而将新程序加载到这里,覆盖掉TSR的关键代码(尤其是ISR部分)或数据。
- TSR失效或崩溃: 当被挂接的中断发生,CPU跳转到TSR的ISR时,如果ISR代码已被覆盖,就会执行到垃圾指令,导致程序错误、系统崩溃或不可预知的行为。TSR实际上就失效了。
- 代码被覆盖: 当TSR通过
- 驻留大小远大于实际所需 (
DX过大):- 浪费内存: DOS会根据
DX的值保留一大块内存给这个TSR程序。如果DX设置得远超TSR实际需要的代码和数据大小,那么多余的被保留内存就无法被其他程序使用,造成了内存资源的浪费。 - 减少可用内存: 在内存资源紧张的DOS环境下,这会减少后续程序可用的内存总量,可能导致其他较大的程序无法加载或运行。
- 对TSR本身无直接危害: 设置过大通常不会直接导致TSR程序本身运行出错(只要实际需要的都在这个大区域内),但对整个系统的内存使用效率不利。
- 准确计算驻留大小很重要: 通常需要计算从程序起始PSP(程序段前缀,256字节)之后到TSR驻留代码(包括ISR和所有ISR用到的数据、子程序)末尾的总字节数,然后将其转换为段落数(除以16并向上取整)赋给
DX。
- 浪费内存: DOS会根据
- 驻留大小小于实际所需 (
- 答案:
-
设计一个简单的TSR逻辑:挂接键盘中断 (INT 9H),当用户按下特定组合键 (例如 Ctrl+Alt+S) 时,在屏幕右上角显示一个特定的字符。描述关键的ISR逻辑判断和操作。(不需要完整代码,侧重思路)
-
答案:
-
挂接INT 9H (键盘硬件中断):
- 保存原始INT 9H向量。
- 将IVT中的INT 9H向量指向自定义的
MyKeyboardISR。
-
MyKeyboardISR逻辑:-
保存现场:
PUSH所有会用到的寄存器 (AX, BX, CX, DX, ES, DS等)。 -
读取键盘扫描码: 通过
IN AL, 60H从键盘端口读取按键的扫描码。 -
获取Shift/Ctrl/Alt状态:
通常需要读取BIOS数据区(如
0040:0017h或
0040:0018h)来获取Ctrl, Alt, Shift等键的状态。
- 例如,检查
0040:0017h的特定位来判断Ctrl (bit 2) 和 Alt (bit 3) 是否被按下。
- 例如,检查
-
判断组合键:
- 检查扫描码是否是 'S' 键的扫描码 (例如,通常是
1Fh)。 - 同时检查Ctrl和Alt键是否都处于按下状态。
- 检查扫描码是否是 'S' 键的扫描码 (例如,通常是
-
如果组合键被按下 (Ctrl+Alt+S):
- 准备写屏:
MOV AX, 0B800HMOV ES, AX- 计算屏幕右上角偏移地址 (例如,第0行,第79列)。偏移
(0 * 80 + 79) * 2 = 158。 - 如果要遵循幻灯片奇数ASCII偶数属性,则属性在158,ASCII在159。
- 写入特定字符和属性:
MOV BYTE PTR ES:, desired_attribute(例如,红底黄字)MOV BYTE PTR ES:, '!'(例如,显示感叹号)
- (可选) 阻止按键传递给系统: 为了防止系统处理这个 'S' 键(如果组合键的目的是纯粹触发TSR功能),可能需要做一些特殊处理,比如不调用原始INT 9H,或者在调用前修改某些传递给原始ISR的参数(这比较复杂)。但通常对于这种热键,我们可能还是希望调用原始ISR,只是额外执行我们的操作。
- 准备写屏:
-
调用原始INT 9H ISR:
PUSHF; 模拟中断压栈Flags (如果原始ISR期望这样)CALL DWORD PTR [OriginalInt9Vector]; 调用保存的原始INT 9H ISR地址- (注意:直接
JMP也可以,但CALL后可以做一些清理工作再IRET,不过对于硬件中断ISR,通常是直接JMP或者在最后IRET前确保堆栈平衡后由自定义ISR的IRET返回)。更简单的方式是在自定义ISR的末尾,如果不需要做额外清理,直接JMP FAR PTR [OriginalInt9Vector],这样原始ISR的IRET会负责返回。如果我们的ISR要自己IRET,那么调用原始ISR后,原始ISR的IRET会返回到我们的ISR中,我们的ISR再IRET。
-
发送EOI: 向8259A主片发送EOI (
OUT 20H, ALAL=20H)。 -
恢复现场:
POP所有寄存器。 -
返回:
IRET。
-
-
TSR驻留: 主程序调用
INT 21H, AH=31H。
-
-
-
多个TSR程序都想挂接同一个系统中断(比如时钟中断),它们之间可能会如何相互影响?有没有办法让它们“和平共处”?(提示:链式调用)
-
答案:
-
相互影响:
- 覆盖问题: 如果多个TSR程序都试图直接修改IVT中同一个中断向量,那么后安装的TSR会覆盖掉先安装的TSR设置的向量。这意味着只有最后一个安装的TSR的ISR会被调用,之前的TSR都会失效。当中断发生时,CPU只会跳转到最后一个写入IVT的ISR地址。
- 功能丢失: 如果某个TSR没有正确地调用它之前被挂接的ISR(可能是另一个TSR的ISR,或者是原始系统ISR),那么被“跳过”的ISR的功能就会丢失。
-
和平共处的方法 (链式调用 - Interrupt Chaining):
- 保存前一个向量: 当一个TSR(我们称之为TSR_B)要挂接一个已经被其他程序(可能是原始系统ISR,也可能是另一个TSR_A)占用的中断向量时,TSR_B首先读取并保存IVT中当前指向的ISR地址(即TSR_A的ISR或原始ISR的地址)。
- 设置自己的向量: 然后TSR_B将自己的ISR_B的地址写入IVT。
- 在自己的ISR中调用前一个ISR: TSR_B的ISR_B在执行完自己的任务后(或者在执行任务前,取决于逻辑需要),必须通过之前保存的地址去调用TSR_A的ISR(或原始ISR)。
- 形成调用链: 这样,当中断发生时,CPU首先跳转到ISR_B。ISR_B执行后,会调用ISR_A。如果ISR_A也是一个TSR的ISR并且它也保存并调用了它之前的向量,那么这个调用链可以一直持续下去,直到调用到最原始的系统ISR。
- EOI和IRET: 通常由链中最后一个实际处理硬件中断的ISR(或者每个ISR如果它们都认为自己是直接响应硬件的)负责发送EOI。返回时,
IRET会逐层返回。
- 通过这种“击鼓传花”式的链式调用,每个TSR都有机会执行自己的代码,同时又不会剥夺其他TSR或系统原有功能执行的机会,从而实现“和平共处”。这种机制是DOS环境下TSR程序能够协同工作的关键。
-
-
这些答案希望能帮助你更好地理解和记忆这些知识点!如果还有不清楚的地方,尽管问!
习题—— 选择填空
题目
Thinking...
Deconstructing User Needs
I've started by analyzing the user's request for a detailed explanation of the IVT, acknowledging the 'abstract' nature of the provided information. My focus is on going beyond a simple recall and building a step-by-step, analogy-driven breakdown to foster deep understanding, rather than superficial knowledge. This will inform the response's structure.
Refining Structure & Analogy
I've reviewed the provided text and am now focusing on structuring the explanation. I'll start with the 'why' and build up using clear definitions and analogies. The phone book analogy seems promising. I'll incorporate textual visual aids, followed by detailed examples.
Perfecting Example Breakdown
I've now detailed the core concepts, constructing the analogies and outlining the structure: Why, What, Where, How. I'm generating concrete examples, including their calculation, with visual aids. This emphasizes the non-abstract nature by using simple examples to drive home user understanding.
好的,我们来出一些选择题和填空题,尽量覆盖 CS253_L910.pdf 复习材料中的核心考点。
选择题
模块一:直接屏幕访问
- 在标准的彩色文本模式下,屏幕显示内存的起始段地址是:
A.0A000h
B.0B000h
C.0B800h
D.0C000h - 根据幻灯片中的描述(奇数地址存ASCII,偶数地址存属性),屏幕上一个字符的ASCII码和其属性字节总共占用多少内存?
A. 1字节
B. 2字节
C. 4字节
D. 取决于字符 - 字符属性字节中,用于定义背景色的位是:
A. 低4位 (bits 0-3)
B. 高4位 (bits 4-7)
C. 低2位 (bits 0-1)
D. 高2位 (bits 6-7) - 直接访问屏幕内存的主要优点是:
A. 可移植性好
B. 编程简单
C. 速度快
D. 硬件无关 - 在TSR中更新屏幕特定位置字符的汇编示例中,
mov ax, 0b800h; mov es, ax这两句指令的目的是:
A. 将字符ASCII码存入AX
B. 设置数据段DS指向显存
C. 设置附加段ES指向显存
D. 计算屏幕偏移地址
模块二:触发器与三态设备 (I/O端口)
- 输入端口的核心组件及其作用是:
A. D型触发器,锁存数据
B. 三态缓冲器,共享数据总线
C. 地址译码器,选择端口
D. 逻辑与门,产生控制信号 - 当CPU执行
IN AL, DX指令时,以下哪个描述是正确的?
A. 数据从AL写入DX指定的端口
B. 数据从DX指定的端口读入AL
C. DX寄存器的内容被写入AL
D. AL寄存器的内容被写入DX指定的端口 - 输出端口使用D型触发器的主要目的是:
A. 允许多个设备共享输出总线
B. 对CPU输出的数据进行译码
C. 放大CPU输出的信号
D. 锁存并稳定保持CPU输出的数据 - 在输入端口的工作原理中,哪个组件负责识别CPU指定的端口地址并输出选通信号?
A. 三态缓冲器
B. CPU的数据总线
C. 地址译码器
D. CPU的控制单元
模块三:延时程序设计
- 在ISR中直接使用长时间的软件延时循环可能导致的主要问题不包括:
A. 中断嵌套
B. 堆栈溢出
C. 系统响应迟缓
D. CPU功耗降低 - 课件中推荐的在ISR中实现延时的更安全方法是:
A. 使用多层嵌套的LOOP指令
B. 关闭所有其他中断
C. 计数特定中断发生的次数
D. 调用BIOS提供的延时功能 - 软件延时循环的延时时间与CPU时钟频率的关系是:
A. 时钟频率越高,延时越长
B. 时钟频率越高,延时越短
C. 延时时间与时钟频率无关
D. 关系不确定
模块四:中断系统
- 由CPU内部执行指令时发生的错误(如除零)引起的中断属于:
A. 硬件中断
B. 软件中断
C. 异常中断 (内部中断)
D. NMI中断 - 当CPU响应一个硬件中断时,以下哪个寄存器不会被硬件自动压入堆栈?
A. Flags 寄存器
B. CS 寄存器
C. IP 寄存器
D. AX 寄存器 - 中断向量表 (IVT) 位于内存的哪个区域?
A. 内存的最高1KB
B. 内存的最低1KB
C. BIOS ROM区域
D. 扩展内存区域 - IVT中的每个中断向量占用多少字节,用于存储什么?
A. 2字节,存储ISR的IP
B. 2字节,存储ISR的CS
C. 4字节,存储ISR的CS和IP
D. 4字节,存储中断类型号和ISR的IP - 如果一个中断类型号是
0AH(十进制为10),那么它在IVT中的入口地址的偏移量是:
A.0AH
B.14H(十进制20)
C.28H(十进制40)
D.04H - 在ISR中,如果使用了通用寄存器,谁负责在ISR开始时保存它们,在结束时恢复它们?
A. CPU硬件自动完成
B. 操作系统自动完成
C. 程序员必须手动编写代码完成
D. 8259 PIC自动完成 - 8259 PIC芯片在中断系统中的主要作用是:
A. 直接执行中断服务程序
B. 扩展CPU处理硬件中断的能力并进行优先级管理
C. 存储中断向量表
D. 产生NMI信号 - 在PC中,两片8259 PIC级联时,从片通常连接到主片的哪个IRQ输入?
A. IRQ0
B. IRQ1
C. IRQ2
D. IRQ7 - 8259 PIC的IMR (中断屏蔽寄存器) 的作用是:
A. 设置中断优先级
B. 存储中断类型号
C. 屏蔽或允许特定的IRQ请求
D. 发送中断结束命令 - ISR在执行完其主要任务并通过
IRET返回之前,必须向8259 PIC发送什么命令?
A. INTA (中断应答)
B. EOI (中断结束)
C. IMR (设置屏蔽位)
D. ICW (初始化命令)
模块五:TSR (Terminate and Stay Resident) 程序
- TSR程序的基本特征是:
A. 执行后完全从内存中清除
B. 执行后部分代码驻留内存,并通过中断被调用
C. 只能通过硬件信号调用
D. 主要用于执行一次性任务后立即退出 - 实现TSR程序挂接系统中断(如INT 8)的第一步通常是:
A. 直接将自定义ISR地址写入IVT
B. 向8259 PIC发送EOI命令
C. 保存目标中断的原始中断向量
D. 调用INT 21H, AH=31H - 在TSR的自定义ISR中,调用原始被挂接中断的ISR的主要目的是:
A. 减少TSR程序的代码量
B. 确保系统原有功能的完整性
C. 提高TSR的执行速度
D. 方便TSR程序的调试 - DOS功能调用
INT 21H, AH=31H用于实现TSR的哪个功能?
A. 保存原始中断向量
B. 设置新的中断向量
C. 终止程序并将指定大小的内存驻留
D. 从中断服务程序返回 - 在使用
INT 21H, AH=31H进行TSR驻留时,DX寄存器指定的是:
A. TSR程序的入口点偏移地址
B. 需要驻留的内存大小(以字节为单位)
C. 需要驻留的内存大小(以段落为单位)
D. TSR程序的返回码
填空题
模块一:直接屏幕访问
- 文本屏幕被映射到特定的内存地址,这种机制称为 __________ 映射。
- 在屏幕内存布局中,每个字符占用2字节,其中一个字节存储字符的ASCII码,另一个字节存储字符的 __________。
- 根据幻灯片,如果一个字符的属性字节的低4位是
1100b(二进制),并且I R G B分别代表亮度、红、绿、蓝,那么其前景颜色是 __________。 - 直接写屏的汇编示例中,
mov bx, [offset]指令中[offset]通常是根据字符的 __________ 和 __________ 计算得到的。
模块二:触发器与三态设备 (I/O端口)
- 输入端口使用三态缓冲器允许多个设备共享数据总线,只有被 __________ 的设备才将数据放到总线上。
- 输出端口使用D型触发器(锁存器)来 __________ CPU输出到外部设备的数据。
- 当CPU执行
OUT port, acc指令时,数据从 __________ 流向 __________。 - 在输出端口的硬件实现中,地址译码器的输出信号与CPU的I/O __________ 信号进行逻辑与操作,以产生锁存数据的信号。
模块三:延时程序设计
- 在ISR中避免使用长延时的原因是,如果ISR执行时间过长,可能会导致第二次中断调用在第一次 __________ 时发生。
- 软件延时循环通过执行大量的 __________ 来消耗CPU时间。
- 延时时间与CPU时钟频率成 __________ 比关系。
模块四:中断系统
- 中断是打断CPU正常指令序列去处理 __________ 事件的机制。
- 8086 CPU的
NMI引脚用于接收 __________ 中断。 - 当CPU响应硬件中断时,会自动将标志寄存器、________ 和 ________ 压入堆栈。
- 中断向量表 (IVT) 包含 __________ 个中断向量。
- 每个中断向量存储对应ISR的入口地址,由2字节的 __________ 和2字节的 __________ 组成。
- 8259 PIC的 __________ 寄存器 (IMR) 用于禁止或允许特定的IRQ请求。
- ISR在以
IRET指令结束前,必须向8259 PIC发送 __________ 命令。 - PC中常使用两片8259 PIC级联,主片连接CPU的INTR引脚,从片则连接到主片的一个 __________ 输入(通常是IRQ2)。
- 8259 PIC在初始化时,需要通过ICW (初始化命令字) 设置其工作模式和IRQ到 __________ 的映射。
模块五:TSR (Terminate and Stay Resident) 程序
- TSR程序通过截获或 __________ 某个中断向量来实现其在后台的调用。
- 在修改IVT中的中断向量之前,必须先保存 __________ 的中断向量地址。
- TSR的自定义ISR在执行完自己的任务后,通常会通过
JMP FAR PTR指令调用 __________。 - 使用
INT 21H, AH=31H系统调用时,DX寄存器指定驻留内存大小的单位是 __________。 - 如果TSR程序挂接了时钟中断 (INT 8),其自定义ISR在结束前需要向8259 PIC发送 __________ 命令。
答案
模块一:直接屏幕访问
- C.
0B800h- 解析: 这是彩色文本模式下标准显存的起始段地址。
- B. 2字节
- 解析: 根据幻灯片,一个字符的ASCII码(奇数地址)和其属性字节(偶数地址)各占1字节,总共2字节。
- B. 高4位 (bits 4-7)
- 解析: 属性字节中,高4位 (bits 7-4) 定义背景色 (F R G B)。
- C. 速度快
- 解析: 直接内存访问绕过了操作系统或BIOS的调用,因此速度非常快。缺点是硬件相关。
- C. 设置附加段ES指向显存
- 解析:
mov ax, 0b800h将显存段地址载入AX,mov es, ax将AX的内容(即显存段地址)赋给ES段寄存器。后续可以通过ES:[offset]来访问显存。虽然DS也可以,但ES常用于串操作或指定目的操作数段。
- 解析:
模块二:触发器与三态设备 (I/O端口)
- B. 三态缓冲器,共享数据总线
- 解析: 输入端口使用三态缓冲器,使得多个设备可以共享数据总线,只有被选中的设备才输出数据。
- B. 数据从DX指定的端口读入AL
- 解析:
IN AL, DX指令的格式是IN 累加器, 端口地址。DX通常用于存放大于FFh的端口地址。数据从端口流向累加器。
- 解析:
- D. 锁存并稳定保持CPU输出的数据
- 解析: D型触发器(锁存器)用于在CPU发出写命令后,捕获并保持数据总线上的数据,为外部设备提供稳定的信号。
- C. 地址译码器
- 解析: 地址译码器负责监测地址总线,当CPU指定的端口地址与某个端口匹配时,输出选通信号以选中该端口。
模块三:延时程序设计
- D. CPU功耗降低
- 解析: 长时间循环延时会使CPU持续工作,反而可能增加功耗。A, B, C都是长延时可能导致的风险。
- C. 计数特定中断发生的次数
- 解析: 这是课件中明确提出的更安全的在ISR中实现延时的方法,避免了长时间占用CPU。
- B. 时钟频率越高,延时越短
- 解析: 相同的指令序列(相同的机器周期数)在更高时钟频率下,每个时钟周期的时间变短,因此总执行时间(延时)变短。
模块四:中断系统
- C. 异常中断 (内部中断)
- 解析: 除零错误是CPU在执行指令过程中内部产生的错误,属于异常中断。
- D. AX 寄存器
- 解析: CPU硬件在响应中断时会自动压栈Flags、CS和IP。通用寄存器如AX需要程序员手动保存。
- B. 内存的最低1KB
- 解析: IVT位于物理地址
00000h到003FFh。
- 解析: IVT位于物理地址
- C. 4字节,存储ISR的CS和IP
- 解析: 每个中断向量占4字节,前2字节是IP(偏移地址),后2字节是CS(段基址)。
- C.
28H(十进制40)- 解析: 偏移量 = 中断类型号 × 4。
0AH(十进制10) × 4 = 40 (十进制) =28H。
- 解析: 偏移量 = 中断类型号 × 4。
- C. 程序员必须手动编写代码完成
- 解析: CPU只自动保存Flags, CS, IP。ISR中若要使用其他通用寄存器,程序员必须负责PUSH和POP。
- B. 扩展CPU处理硬件中断的能力并进行优先级管理
- 解析: 8259 PIC的主要功能是管理多个外部中断源,进行优先级仲裁,并将中断请求传递给CPU。
- C. IRQ2
- 解析: 这是PC/AT体系结构中经典的级联方式,从片的INT输出连接到主片的IRQ2输入。
- C. 屏蔽或允许特定的IRQ请求
- 解析: IMR的每一位对应一条IRQ线,通过设置这些位可以禁止或允许相应的中断。
- B. EOI (中断结束)
- 解析: ISR在返回前必须向8259 PIC发送EOI命令,以清除中断服务状态,允许PIC响应其他中断。
模块五:TSR (Terminate and Stay Resident) 程序
- B. 执行后部分代码驻留内存,并通过中断被调用
- 解析: 这是TSR程序的核心特征。
- C. 保存目标中断的原始中断向量
- 解析: 在修改IVT以挂接自己的ISR之前,必须先保存原始的ISR地址,以便后续调用或在卸载时恢复。
- B. 确保系统原有功能的完整性
- 解析: 许多系统中断执行着重要的底层任务,TSR的ISR通常需要调用原始ISR来保证这些功能不被破坏。
- C. 终止程序并将指定大小的内存驻留
- 解析:
INT 21H, AH=31H是DOS提供的终止并驻留功能。
- 解析:
- C. 需要驻留的内存大小(以段落为单位)
- 解析:
DX寄存器指定驻留大小,单位是段落(1段落 = 16字节)。
- 解析:
填空题 - 答案与解析
模块一:直接屏幕访问
-
文本屏幕被映射到特定的内存地址,这种机制称为
内存
映射。
- 解析: Memory-mapped I/O 是一种常见的硬件访问方式。
-
在屏幕内存布局中,每个字符占用2字节,其中一个字节存储字符的ASCII码,另一个字节存储字符的
属性字节 (Attribute Byte)
。
- 解析: 属性字节定义字符的颜色、闪烁等。
-
根据幻灯片,如果一个字符的属性字节的低4位是
1100b(二进制),并且I R G B分别代表亮度、红、绿、蓝,那么其前景颜色是
亮红色 (Bright Red)
。
- 解析:
1100b-> I=1 (亮), R=1 (红), G=0 (绿), B=0 (蓝)。所以是亮红色。
- 解析:
-
直接写屏的汇编示例中,
mov bx, [offset]指令中
[offset]通常是根据字符的
行号
和
列号
计算得到的。
- 解析: 偏移地址 = (行号 × 每行字符数 + 列号) × 2。
模块二:触发器与三态设备 (I/O端口)
-
输入端口使用三态缓冲器允许多个设备共享数据总线,只有被
选中 (enabled/selected)
的设备才将数据放到总线上。
- 解析: 通过地址译码和控制信号使能正确的三态缓冲器。
-
输出端口使用D型触发器(锁存器)来
锁存并保持 (latch and hold)
CPU输出到外部设备的数据。
- 解析: 为外部设备提供稳定的数据信号。
-
当CPU执行
OUT port, acc指令时,数据从
累加器 (accumulator / acc)
流向
端口 (port)
。
- 解析: 课件中对此指令的描述有误,这是正确的流向。
-
在输出端口的硬件实现中,地址译码器的输出信号与CPU的I/O
写 (write)
信号进行逻辑与操作,以产生锁存数据的信号。
- 解析: 写操作时才需要锁存数据到输出端口。
模块三:延时程序设计
-
在ISR中避免使用长延时的原因是,如果ISR执行时间过长,可能会导致第二次中断调用在第一次
完成 (is occurring / has finished)
时发生。
- 解析: 指的是中断嵌套或中断丢失的风险。
-
软件延时循环通过执行大量的
空操作 (no-operation / NOP-like instructions) / 循环指令 (loop instructions)
来消耗CPU时间。
- 解析:
LOOP指令本身就是一种循环,循环体可以是空操作。
- 解析:
-
延时时间与CPU时钟频率成
反 (inversely)
比关系。
- 解析: 频率越高,周期越短,相同指令数执行时间越短。
模块四:中断系统
-
中断是打断CPU正常指令序列去处理
紧急 (urgent / specific)
事件的机制。
- 解析: 中断用于响应需要立即关注的事件。
-
8086 CPU的
NMI引脚用于接收
不可屏蔽 (Non-Maskable)
中断。
- 解析: NMI通常用于处理非常紧急的事件,如硬件故障。
-
当CPU响应硬件中断时,会自动将标志寄存器、
代码段寄存器 (CS)
和
指令指针 (IP)
压入堆栈。
- 解析: 这是硬件自动完成的现场保存。
-
中断向量表 (IVT) 包含
256
个中断向量。
- 解析: 对应0-255号中断。
-
每个中断向量存储对应ISR的入口地址,由2字节的
IP (指令指针/偏移地址)
和2字节的
CS (代码段寄存器/段基址)
组成。
- 解析: 顺序通常是IP在前,CS在后。
-
8259 PIC的
中断屏蔽寄存器 (Interrupt Mask Register)
(IMR) 用于禁止或允许特定的IRQ请求。
- 解析: 通过编程IMR可以控制哪些中断源可以被响应。
-
ISR在以
IRET指令结束前,必须向8259 PIC发送
EOI (End Of Interrupt / 中断结束)
命令。
- 解析: 通知PIC该中断已处理完毕。
-
PC中常使用两片8259 PIC级联,主片连接CPU的INTR引脚,从片则连接到主片的一个
IRQ (Interrupt Request / 中断请求)
输入(通常是IRQ2)。
- 解析: 这是扩展中断源的常用方法。
-
8259 PIC在初始化时,需要通过ICW (初始化命令字) 设置其工作模式和IRQ到
中断类型号 (Interrupt Type Number / Vector Number)
的映射。
- 解析: 确保每个IRQ对应一个唯一的CPU中断向量。
模块五:TSR (Terminate and Stay Resident) 程序
-
TSR程序通过截获或
挂接 (hooking)
某个中断向量来实现其在后台的调用。
- 解析: 修改IVT使中断指向TSR的ISR。
-
在修改IVT中的中断向量之前,必须先保存
原始 (original / existing)
的中断向量地址。
- 解析: 为了能调用原始ISR或在卸载时恢复。
-
TSR的自定义ISR在执行完自己的任务后,通常会通过
JMP FAR PTR指令调用
原始中断服务例程 (original ISR)
。
- 解析: 保证系统原有功能的执行。
-
使用
INT 21H, AH=31H系统调用时,
DX寄存器指定驻留内存大小的单位是
段落 (Paragraphs)
。
- 解析: 1段落 = 16字节。
-
如果TSR程序挂接了时钟中断 (INT 8),其自定义ISR在结束前需要向8259 PIC发送
EOI (End Of Interrupt / 中断结束)
命令。
- 解析: 因为时钟中断是硬件中断,由8259 PIC管理。
希望这些答案和解析能帮助你更好地复习!
浙公网安备 33010602011771号