65、深入探索MS - DOS编程中的中断处理与硬件控制

深入探索MS - DOS编程中的中断处理与硬件控制

1. 中断返回与控制

当执行IRET(中断返回)指令时,它会从栈中弹出IP、CS和标志寄存器的值,从而将控制权交还给中断发生时正在执行的程序。

2. 中断控制指令

CPU有一个名为中断标志(IF)的标志位,用于控制CPU对外部(硬件)中断的响应方式:
- 若中断标志置位(IF = 1),则表示中断已启用。
- 若中断标志清零(IF = 0),则表示中断被禁用。

2.1 STI指令

STI指令用于启用外部中断。例如,系统响应键盘输入时,会暂停当前正在执行的程序,调用INT 9将按键存储在缓冲区,然后返回当前程序。通常情况下,中断标志是启用的,否则系统定时器将无法正确计算时间和日期,输入的按键也会丢失。

2.2 CLI指令

CLI指令用于禁用外部中断,应谨慎使用,仅在执行关键操作且不能被中断时使用。例如,在更改SS和SP的值时,如果代码被中断,可能会导致SS寄存器指向新的栈段,而栈指针尚未更新。为确保安全,可按以下步骤操作:

cli                ; 禁用中断
mov   ax,mystack   ; 重置SS
mov   ss,ax
mov   sp,100h      ; 重置SP
sti                ; 重新启用中断

中断一次禁用的时间不应超过几毫秒,否则可能会丢失按键并减慢系统定时器。当CPU响应中断处理程序时,其他中断会立即被禁用,而MS - DOS和BIOS中断服务例程在开始执行时会重新启用中断。

3. 编写自定义中断处理程序

中断向量表存在的原因是,IBM - PC的设计者希望能够在不更换ROM芯片的情况下对BIOS例程进行修改和更正。通过中断向量表,可以将表中的地址替换为指向RAM中程序的地址。

中断向量表中的每个地址都指向一个称为中断处理程序或中断服务例程(ISR)的程序。应用程序可以用一个新的地址替换表中的地址,指向新的中断处理程序。例如,可以编写一个自定义的键盘中断处理程序。但由于涉及的工作量较大,更可能的做法是让中断处理程序直接调用默认的INT 9键盘程序从键盘端口读取按键。

3.1 INT 21h函数25h和35h
  • 函数35h(获取中断向量) :返回中断向量的段 - 偏移地址。调用该函数时,将所需的中断号放在AL中,MS - DOS会在ES:BX中返回32位向量。示例代码如下:
.data
int9Save LABEL WORD
DWORD ?      ; 在此存储旧的INT 9地址
.code
mov
ah,35h      ; 获取中断向量
mov
al,9        ; 针对INT 9
int
21h         ; 调用MS - DOS
mov
int9Save,BX ; 存储偏移量
mov
int9Save+2,ES  ; 存储段地址
  • 函数25h(设置中断向量) :允许用新的处理程序替换现有的中断处理程序。调用时,将中断号放在AL中,将自己的中断处理程序的段 - 偏移地址放在DS:DX中。示例代码如下:
mov
ax,SEG kybd_rtn
; 键盘处理程序
mov
ds,ax
; 段地址
mov
dx,OFFSET kybd_rtn
; 偏移地址
mov
ah,25h             
; 设置中断向量
mov
al,9h              
; 针对INT 9h
int
21h 
3.2 Ctrl - Break处理程序示例

当用户在MS - DOS程序等待输入时按下Ctrl - Break,控制权将传递给默认的INT 23h中断处理程序,该处理程序会终止当前运行的程序,可能导致程序处于不稳定状态。可以用自己的代码替换INT 23h处理程序,防止程序停止。示例程序如下:

TITLE Control - Break Handler             (Ctrlbrk.asm)
; 此程序安装自己的Ctrl - Break处理程序,防止用户使用Ctrl - Break(或Ctrl - C)停止程序。
; 程序输入并回显按键,直到按下Esc键。
INCLUDE Irvine16.inc
.data
breakMsg BYTE "BREAK",0
msg
BYTE "Ctrl - Break demonstration."
BYTE  0dh,0ah
BYTE "This program disables Ctrl - Break (Ctrl - C). Press any"
BYTE  0dh,0ah
BYTE "keys to continue, or press ESC to end the program."
BYTE  0dh,0ah,0
.code
main PROC
mov
ax,@data
mov
ds,ax
mov
dx,OFFSET msg
; 显示问候消息
call
Writestring
install_handler:
push
ds          
; 保存DS
mov
ax,@code    
; 将DS初始化为代码段
mov
ds,ax
mov
ah,25h      
; 设置中断向量
mov
al,23h      
; 针对中断23h
mov
dx,OFFSET break_handler
int
21h
pop
ds          
; 恢复DS
L1:
mov
ah,1        
; 等待按键,回显
int
21h
cmp
al,1Bh      
; 是否按下ESC?
jnz
L1          
; 否:继续
exit
main ENDP
; 以下过程在按下Ctrl - Break时执行,必须保留所有寄存器。
break_handler PROC
push
ax
push
dx
mov
dx,OFFSET breakMsg
call
WriteString
pop
dx
pop
ax
iret
break_handler ENDP
END main

主程序初始化INT 23h的中断向量,INT 21h函数25h的输入参数如下:
| 参数 | 值 | 说明 |
| ---- | ---- | ---- |
| AH | 25h | 设置中断向量功能号 |
| AL | 23h | 要处理的中断向量 |
| DS:DX | 新Ctrl - Break处理程序的段/偏移地址 |

程序的主循环输入并回显按键,直到按下Esc键。当按下Ctrl - Break时,break_handler过程会显示一条消息并立即返回调用程序。在break_handler结束时执行IRET,控制权将返回主程序,继续执行被中断的MS - DOS函数。在中断处理程序中必须保留所有寄存器,且无需恢复INT 23h向量,因为MS - DOS在程序结束时会自动恢复。

4. 终止并驻留程序(TSR)

终止并驻留程序(TSR)安装在内存中,直到被特殊的移除实用软件移除或计算机重启。TSR在被某些事件(如按键)激活之前处于休眠状态。

早期的TSR存在兼容性问题,多个程序替换相同的中断向量时,旧程序会使向量指向自己的程序,且不提供指向使用相同向量的其他程序的前向链。后来,TSR作者会保存要替换的中断的现有向量,并在自己的程序处理完中断后前向链到原始中断处理程序。但这意味着最后安装的TSR在处理中断时具有最高优先级,用户有时需要注意以特定顺序加载TSR程序。

4.1 键盘示例

假设编写一个中断服务例程,检查键盘输入的每个字符并将其存储在位置10B2:0020。安装ISR的步骤如下:
1. 从中断向量表中获取当前的INT 9向量并保存。
2. 用ISR的地址替换表中的条目。

当按下键盘键时,键盘控制器将一个字节传输到计算机的键盘端口,触发硬件中断。8259 PIC将中断号传递给CPU,CPU跳转到中断向量表中的INT 9地址,即我们的ISR地址。我们的程序有机会检查键盘字节,处理完后跳转到原始的BIOS键盘处理程序。

mermaid流程图如下:

graph LR
    A[按下键盘键] --> B[键盘控制器传输字节]
    B --> C[触发硬件中断]
    C --> D[8259 PIC传递中断号]
    D --> E[CPU跳转到INT 9地址(ISR)]
    E --> F[ISR检查键盘字节]
    F --> G[ISR跳转到原始BIOS键盘处理程序]
    G --> H[BIOS INT 9h例程处理]
    H --> I[IRET返回控制权]
5. 应用:No_Reset程序

No_Reset程序是一种简单的内存驻留程序,用于防止系统通过Ctrl - Alt - Delete键重启。安装该程序后,只有按下Ctrl + Alt + Right shift + Del组合键才能重启系统。

5.1 MS - DOS键盘状态字节

程序需要检查MS - DOS键盘状态字节,该字节存储在地址0040:0017h,用于判断Ctrl、Alt、Del和RightShift键是否被按下。另一个位于0040:0018的键盘状态字节重复了前面的标志,只是位3表示Ctrl - NumLock是否激活。

5.2 安装程序

程序的安装代码位于末尾,因为它不会驻留在内存中。驻留部分从标签int9_handler开始,留在内存中,并由INT 9h向量指向。以下是程序代码:

TITLE Reset - Disabling program              (No_Reset.asm)
; 此程序通过拦截INT 9键盘硬件中断,禁用通常的DOS重置命令(Ctrl - Alt - Del)。
; 它检查MS - DOS键盘标志中的移位状态位,将任何Ctrl - Alt - Del更改为Alt - Del。
; 计算机只能通过输入Ctrl + Alt + Right shift + Del重启。
; 汇编、链接,并通过在Microsoft LINK命令行中包含/T命令将其转换为COM程序。
; 在运行此程序之前,以纯MS - DOS模式启动。
.model tiny
.386
.code
rt_shift   EQU 01h
; 右Shift键:位0
ctrl_key   EQU 04h
; CTRL键:位2
alt_key    EQU 08h
; ALT键:位3
del_key    EQU 53h
; DEL键的扫描码
kybd_port  EQU 60h
; 键盘输入端口
ORG   100h       
; 这是一个COM程序
start:
jmp   setup      
; 跳转到TSR安装
;   内存驻留代码从这里开始
int9_handler PROC FAR
sti               
; 启用硬件中断
pushf
; 保存寄存器和标志
push
es
push
ax
push
di
;   将ES:DI指向DOS键盘标志字节:
L1:
mov
ax,40h             
; DOS数据段位于40h
mov
es,ax
mov
di,17h             
; 键盘标志的位置
mov
ah,es:[di]         
; 将键盘标志复制到AH
;   测试CTRL和ALT键:
L2:
test
ah,ctrl_key        
; CTRL键是否按下?
jz
L5                 
; 否:退出
test
ah,alt_key         
; ALT键是否按下?
jz
L5                 
; 否:退出
;   测试DEL和右Shift键:
L3:
in
al,kybd_port       
; 读取键盘端口
cmp
al,del_key         
; 是否按下DEL键?
jne
L5                 
; 否:退出
test
ah,rt_shift        
; 右Shift键是否按下?
jnz
L5                 
; 是:允许系统重置
L4:
and
ah,NOT ctrl_key    
; 否:关闭CTRL位
mov
es:[di],ah         
; 存储键盘标志
L5:
pop
di                 
; 恢复寄存器和标志
pop
ax
pop
es
popf
jmp
cs:[old_interrupt9]
; 跳转到INT 9例程
old_interrupt9 DWORD ?
int9_handler ENDP
end_ISR label BYTE
; --------------- (TSR程序结束) ------------------
;   保存原始INT 9向量的副本,并将我们程序的地址设置为新向量。
;   终止此程序,将int9_handler过程留在内存中。
setup:
mov
ax,3509h       
; 获取INT 9向量
int
21h
mov
word ptr old_interrupt9,bx
; 保存INT 9向量
mov
word ptr old_interrupt9+2,es
mov
ax,2509h          
; 设置INT 9向量
mov
dx,offset int9_handler
int
21h
mov
ax,3100h
; 终止并驻留
mov
dx,OFFSET end_ISR  
; 指向驻留代码的末尾
shr
dx,4
; 除以16
inc
dx
; 向上舍入到下一个段落
int
21h               
; 执行MS - DOS功能
END start

安装程序时,在标签setup处,调用INT 21h Function 35h获取当前的INT 9h向量并存储在old_interrupt9中,以便程序能够前向链到现有的键盘处理程序。同时,调用INT 21h Function 25h将中断向量9h设置为程序驻留部分的地址。最后,调用INT 21h Function 31h退出到MS - DOS,将驻留程序留在内存中。

内存驻留的中断处理程序从int9_handler开始,每次按下键盘键时都会执行。处理程序获得控制权后会重新启用中断,因为8259 PIC会自动禁用中断。其处理流程如下:
1. 定位存储在地址0040:0017的键盘标志字节并复制到AH。
2. 检查Ctrl和Alt键是否都被按下,若未按下则退出。
3. 若Ctrl和Alt键都被按下,从键盘端口读取字符并与Del键比较,若未按下Del键则退出。
4. 检查右Shift键是否按下,若按下则允许系统重置;否则,清除键盘标志字节中的Ctrl键位,禁用用户重启计算机的尝试。
5. 最后跳转到现有的BIOS INT 9h例程处理正常按键。

6. 硬件控制使用I/O端口

x86系统提供两种类型的硬件输入 - 输出:内存映射和基于端口的。
- 内存映射I/O :程序可以将数据写入特定的内存地址,数据会传输到输出设备;同样,也可以从预定义的内存地址读取输入设备的数据。例如,文本视频显示就是一个内存映射设备,将字符放入视频段时,它们会立即显示在屏幕上。
- 基于端口的I/O :需要使用IN和OUT指令将数据读写到特定编号的位置(称为端口)。端口是CPU与其他设备(如键盘、扬声器、调制解调器和声卡)之间的连接或通道。

深入探索MS - DOS编程中的中断处理与硬件控制(续)

7. 关于中断处理的常见问题解答

为了更好地理解中断处理相关知识,下面对一些常见问题进行解答:
1. 默认的关键错误处理程序执行什么操作? :文档未提及相关内容。
2. 中断向量表的每个条目中包含什么? :每个条目指向一个称为中断处理程序或中断服务例程(ISR)的程序。
3. INT 10h的中断向量存储在哪个地址? :文档未提及相关内容。
4. 哪个控制器芯片生成硬件中断? :8259 PIC生成硬件中断。
5. 哪个指令禁用硬件中断? :CLI指令禁用硬件中断。
6. 哪个指令启用硬件中断? :STI指令启用硬件中断。
7. 哪个IRQ级别具有最高优先级,0还是15? :文档未提及相关内容。
8. 基于对IRQ级别的了解,如果一个程序正在创建磁盘文件,按下键盘上的键,你认为键何时会被放入键盘缓冲区——在文件创建之前还是之后? :文档未提及相关内容。
9. 当按下键盘上的键时,执行哪个硬件中断? :执行INT 9硬件中断。
10. 当中断处理程序完成时,CPU如何恢复到中断触发前的执行位置? :通过执行IRET(中断返回)指令,从栈中弹出IP、CS和标志寄存器的值,从而恢复到中断前的执行位置。
11. 哪些MS - DOS函数用于获取和设置中断向量? :INT 21h函数35h用于获取中断向量,INT 21h函数25h用于设置中断向量。
12. 解释中断处理程序和内存驻留程序的区别。 :中断处理程序是响应特定中断而执行的程序,用于处理中断事件;内存驻留程序是安装在内存中并一直驻留,直到被移除或计算机重启,通常在特定事件触发时被激活。
13. 描述一个TSR程序。 :终止并驻留程序(TSR)安装在内存中,在被某些事件(如按键)激活之前处于休眠状态,直到被特殊的移除实用软件移除或计算机重启。
14. 如何从内存中移除TSR程序? :可以使用特殊的移除实用软件移除,或者重启计算机。
15. 如果一个内存驻留程序替换了一个中断向量,它如何仍然利用该中断现有处理程序中的某些功能? :可以保存要替换的中断的现有向量,并在自己的程序处理完中断后前向链到原始中断处理程序。
16. 哪个MS - DOS函数终止程序并使程序的一部分驻留在内存中? :INT 21h函数31h用于终止程序并使程序的一部分驻留在内存中。
17. 在No_reset程序中,哪个键组合会实际重启计算机? :按下Ctrl + Alt + Right shift + Del组合键会重启计算机。

8. 中断处理与硬件控制的总结

通过前面的介绍,我们对MS - DOS编程中的中断处理和硬件控制有了较为深入的了解。下面以表格形式总结相关关键信息:
| 主题 | 关键内容 |
| ---- | ---- |
| 中断返回 | 执行IRET指令,从栈中弹出IP、CS和标志寄存器的值,将控制权交还给中断发生时正在执行的程序。 |
| 中断控制指令 | STI指令启用外部中断,CLI指令禁用外部中断,中断禁用时间不宜过长。 |
| 自定义中断处理程序 | 可使用INT 21h函数25h和35h设置和获取中断向量,编写自定义中断处理程序。 |
| Ctrl - Break处理程序 | 可替换默认的INT 23h处理程序,防止程序因按下Ctrl - Break而停止。 |
| 终止并驻留程序(TSR) | 安装在内存中,在特定事件激活前休眠,早期存在兼容性问题,可通过前向链解决。 |
| 键盘示例 | 编写中断服务例程检查键盘输入字符,安装时需获取并保存INT 9向量,处理完后跳转到原始BIOS处理程序。 |
| No_Reset程序 | 防止系统通过Ctrl - Alt - Delete重启,需检查键盘状态字节,安装时设置INT 9向量并驻留程序。 |
| 硬件控制使用I/O端口 | x86系统提供内存映射和基于端口的两种硬件输入 - 输出方式。 |

9. 操作步骤总结

为了方便大家回顾和操作,下面总结一些关键操作的步骤:

9.1 编写自定义中断处理程序并设置中断向量
  1. 使用INT 21h函数35h获取现有中断向量:
.data
int9Save LABEL WORD
DWORD ?      ; 在此存储旧的INT 9地址
.code
mov
ah,35h      ; 获取中断向量
mov
al,9        ; 针对INT 9
int
21h         ; 调用MS - DOS
mov
int9Save,BX ; 存储偏移量
mov
int9Save+2,ES  ; 存储段地址
  1. 编写自定义中断处理程序(如kybd_rtn)。
  2. 使用INT 21h函数25h设置新的中断向量:
mov
ax,SEG kybd_rtn
; 键盘处理程序
mov
ds,ax
; 段地址
mov
dx,OFFSET kybd_rtn
; 偏移地址
mov
ah,25h             
; 设置中断向量
mov
al,9h              
; 针对INT 9h
int
21h 
9.2 安装Ctrl - Break处理程序
  1. 编写Ctrl - Break处理程序(如break_handler)。
  2. 在主程序中设置INT 23h中断向量:
main PROC
mov
ax,@data
mov
ds,ax
mov
dx,OFFSET msg
; 显示问候消息
call
Writestring
install_handler:
push
ds          
; 保存DS
mov
ax,@code    
; 将DS初始化为代码段
mov
ds,ax
mov
ah,25h      
; 设置中断向量
mov
al,23h      
; 针对中断23h
mov
dx,OFFSET break_handler
int
21h
pop
ds          
; 恢复DS
L1:
mov
ah,1        
; 等待按键,回显
int
21h
cmp
al,1Bh      
; 是否按下ESC?
jnz
L1          
; 否:继续
exit
main ENDP
9.3 安装键盘中断服务例程(TSR)
  1. 获取当前INT 9向量并保存:
setup:
mov
ax,3509h       
; 获取INT 9向量
int
21h
mov
word ptr old_interrupt9,bx
; 保存INT 9向量
mov
word ptr old_interrupt9+2,es
  1. 编写中断服务例程(如int9_handler)。
  2. 设置新的INT 9向量:
mov
ax,2509h          
; 设置INT 9向量
mov
dx,offset int9_handler
int
21h
  1. 终止并驻留程序:
mov
ax,3100h
; 终止并驻留
mov
dx,OFFSET end_ISR  
; 指向驻留代码的末尾
shr
dx,4
; 除以16
inc
dx
; 向上舍入到下一个段落
int
21h               
; 执行MS - DOS功能
10. 总结与展望

通过本文的介绍,我们了解了MS - DOS编程中中断处理和硬件控制的相关知识,包括中断返回、中断控制指令、自定义中断处理程序、Ctrl - Break处理程序、终止并驻留程序以及硬件控制使用I/O端口等内容。这些知识对于深入理解计算机系统的工作原理和进行底层编程具有重要意义。

在实际应用中,中断处理和硬件控制可以用于实现各种功能,如键盘监控、系统保护等。但同时也需要注意兼容性问题和程序的稳定性,避免出现系统崩溃等情况。

未来,随着计算机技术的不断发展,虽然MS - DOS已经逐渐被更先进的操作系统所取代,但其中的中断处理和硬件控制原理仍然具有一定的参考价值。对于对计算机底层技术感兴趣的开发者来说,深入研究这些知识可以帮助他们更好地理解现代计算机系统的工作机制。

mermaid流程图展示整体操作流程:

graph LR
    A[开始] --> B[了解中断处理与硬件控制知识]
    B --> C[编写自定义中断处理程序]
    C --> D[设置中断向量]
    D --> E[安装Ctrl - Break处理程序]
    E --> F[安装TSR程序]
    F --> G[使用I/O端口进行硬件控制]
    G --> H[测试与优化程序]
    H --> I[结束]

总之,掌握MS - DOS编程中的中断处理和硬件控制知识,不仅可以帮助我们解决实际问题,还能为我们进一步探索计算机底层技术打下坚实的基础。希望本文能对大家有所帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值