内中断
产生
中断源
- 除法错误
- 单步执行
- 执行 into 指令
-
中断类型码
中断类型码表示几号中断源
除法错误:0
- 单步执行:1
- into 指令:4
-
向量表
根据 8 位中断类型码,在中断向量表中查找中断处理程序的入口地址
8086CPU 最多支持256个中断源
中断向量表存放在内存地址0000:0000~0000:03FF处,一个表项包含中断处理程序的段地址和偏移地址,因此占用4个字节
高地址存放段地址,低地址存放偏移地址
中断源从0号开始
过程
获取中断类型
Npushf标志寄存器入栈TF=0``IF=0(TF=1就会产生单步中断,因此处理中断的时候要将TF=0避免中断程序执行时陷入无限中断中)push CSpush IP- 获取中断处理程序内存地址(
[N×4+2]:[N×4]) - 执行中断处理程序
iret 指令
用于恢复中断处理程序前的状态,等价于pop IPpop CSpopf
除法中断处理
``` assume cs:code
code segment start:mov ax,1000h mov bl,1 div bl
mov ax,4c00hint 21h
code ends
end start
上面的程序会产生除法溢出,因为除数是 8 位的,因此结果保存在`ax`里,其中商`1000h`保存在`al`里,出现溢出,余数`0`保存在`ah`里,无溢出<a name="iXZuI"></a>### 处理 0 号中断程序
assume cs:code
code segment
start:mov ax,cs mov ds,ax mov si,offset do0
mov ax,0mov es,axmov di,200hmov cx,offset do0end - offset do0 ; 获取 do0 程序的字节数cldrep movsb ; 将 do0 程序送入内存地址 0000:0200 处; 设置中断向量表连接到 0000:0200mov ax,0mov es,axmov word ptr es:[0*4],200hmov word ptr es:[0*4+2],0mov ax,4c00hint 21h; 以下都是 do0 程序do0:jmp short do0startdb 'overflow!'
do0start:mov ax,cs mov ds,ax mov si,202h ; jmp 占两个字节, 202h 链接到字符串的地址
mov ax,0b800hmov es,axmov di,12*160+36*2 ; es:di 指向显存空间的中间位置; 把字符串一个个取出来放到显存空间mov cx,9s:mov al,[si]mov es:[di],alinc siadd di,2loop smov ax,4c00hint 21h
do0end:nop
code ends
end start
上述程序解析:1. 处理程序`do0`1. 将`do0`送入`0:200`1. 将中断向量表`0`号中断源的处理程序地址改为`0:200`1. 将字符串`overflow`输入到显存上<a name="KBD5z"></a>## 单步中断执行完一条指令后,寄存器`TF`被设置成`1`产生单步中断(`1`号中断源),并将寄存器的状态显示在屏幕上,等待下一条指令<br /><a name="JBNOq"></a>## 特殊情况设置`ss`和`sp`的时候不产生单步中断
mov ax,2000 mov ss,ax mov sp,10 mov bx,1000
<br />因此设置`ss`和`sp`应该是连续的指令<a name="oDfUK"></a># int 指令<a name="dcsh6"></a>## 求平方中断程序
assume cs:code
code segment start:mov ax,3456 ; 0D80H int 7ch ; 产生中断, 去获取 ax 的平方, 并且低位存在 ax 里, 高位存在 dx 里 add ax,ax adc dx,dx ; 最后结果 016C8000H mov ax,4c00h int 21h code ends end start
```;中断安装程序assume cs:codecode segmentstart:mov ax,csmov ds,axmov si,offset sqr; 安装程序mov ax,0mov es,axmov di,200hmov cx,offset sqrend - offset sqrcldrep movsb; 修改向量表mov ax,0mov es,axmov word ptr es:[7ch*4],200hmov word ptr es:[7ch*4+2],0mov ax,4c00hint 21hsqr:mul axiretsqrend:nopcode endsend start
转换大写
assume cs:codedata segmentdb "conversation",0data endscode segmentstart:mov ax,datamov ds,axmov si,0int 7chmov ax,4c00hint 21hcode endsend start
;中断安装程序assume cs:codecode segmentstart:mov ax,csmov ds,axmov si,offset capital; 安装程序mov ax,0mov es,axmov di,200hmov cx,offset capitalend - offset capitalcldrep movsb; 修改向量表mov ax,0mov es,axmov word ptr es:[7ch*4],200hmov word ptr es:[7ch*4+2],0mov ax,4c00hint 21hcapital:push sipush cschange:mov cl,[si]mov ch,0jcxz okand byte ptr [si],11011111Binc siloop changeok:pop sipop cxiretcapitalend:nopcode endsend start
模拟 loop 指令

assume cs:codecode segmentstart:mov ax,0b800hmov es,axmov di,160*12mov bx,offset s - offset se ; 注意此时的偏移差是反向偏移mov cx,80s:mov byte ptr es:[di],'!' ; 把 ! 送入显存空间add di,2 ; 地址加一个字int 7ch ; 跳转到中断程序, 此时入栈的 IP 为 se 的偏移地址se:nopmov ax,4c00hint 21hcode endsend start
;中断安装程序assume cs:codecode segmentstart:mov ax,csmov ds,axmov si,offset lp; 安装程序mov ax,0mov es,axmov di,200hmov cx,offset lpend - offset lpcldrep movsb; 修改向量表mov ax,0mov es,axmov word ptr es:[7ch*4],200hmov word ptr es:[7ch*4+2],0mov ax,4c00hint 21hlp:push bpmov bp,sp ; 获取栈顶偏移地址dec cx ; cx 先 -1jcxz lpret ; 再判断 cx 是否为 0, 为 0 则直接跳出中断程序add [bp+2],bx ; 不为 0 则将 se 的偏移地址 + 之前的偏移差获得 s 的偏移地址; [bp] 是原 bp [bp+2] 是 se 的 IP, [bp+4] 是 se 的 CSlpret:pop bpiret ; 如果前面的 add 执行了, 则 cs:ip 就指向 s, 实现循环lpend:nopcode endsend start
BIOS 中的中断程序
int 10h是包含多个与屏幕输出相关子程序的中断程序
assume cs:codecode segmentmov ah,2 ; 2 号子程序, 设置光标位置mov bh,0 ; 第 0 页mov dh,5 ; 第 5 行mov dl,12 ; 第 12 列int 10hmov ah,9 ; 9 号子程序, 在光标位置显示字符mov al,'a' ; 显示的字符mov bl,11001010B ; BL(闪烁) RGB(背景) I(高亮) RGB(前景)mov bh,0 ; 第 0 页mov cx,3 ; 字符重复 3 次int 10hmov ax,4c00hint 21hcode endsend
DOS 中的中断程序
;mov ax,4c00hmov ah,4ch ; 第 4c 号子程序mov al,0 ; 返回值int 21h
光标处显示字符串
assume cs:codedata segmentdb 'Welcome to masm','$'data endscode segmentstart:mov ah,2mov bh,0mov dh,5mov dl,12int 10h ; 这里设置光标mov ax,datamov ds,axmov dx,0 ; ds:dx 指向字符串mov ah,9 ; 21h 的第 9 号子程序int 21hmov ax,4c00hint 21hcode endsend start
外中断
外设的输入到达后,相关芯片将向CPU发出相应的中断信息,CPU执行完当前指令后就会检测到发送过来的中断信息
可屏蔽中断
IF=1则CPU会在执行完当前指令后响应中断,IF=0则不会响应,因此内中断过程中需要将IF设置成0
sti ; 设置 IF=1
cli ; 设置 IF=0
几乎所有的外设中断都是可屏蔽的
键盘输入
按下一个键,开关接通,芯片产生一个扫描码,说明了该键在键盘上的位置,称为通码
松开一个键,也会产生扫描码,称为断码
通码和断码都是一个字节
通码最高位为0断码最高位为1,即通码+80h=断码
主板上相关接口端口地址60h
中断执行过程
- 读出
60h端口的扫描码 - 字符按键,将对应的
ASCII码送入BIOS的键盘缓冲区,控制按键(ctrl)或切换按键(CapsLk),转成状态字节,写入内存中存储状态字节的单元(0040:17)BIOS键盘缓冲区可存储15个键盘输入 一个键盘输入使用一个字存放,高位扫描码,低位字符码(ASCII码)
| 位置 | 按键 | 值 |
|---|---|---|
| 0 | 右 shift |
1按下 |
| 1 | 左 shift |
|
| 2 | ctrl |
|
| 3 | alt |
|
| 4 | ScrollLock |
1指示灯亮 |
| 5 | NumLock |
1键盘输入数字 |
| 6 | CapsLock |
1大写 |
| 7 | Insert |
1删除状态 |
int9 中断程序
assume cs:code,ss:stackstack segmentdb 128 dup (0)stack endsdata segmentdw 0,0 ; 这里要保存系统自带 int9 程序的内存地址data endscode segmentstart: mov ax,stackmov ss,axmov sp,128mov ax,datamov ds,axmov ax,0mov es,ax; 将自带的 int9 程序内存地址保存起来push es:[9*4]pop ds:[0]push es:[9*4+2]pop ds:[2]; 中断向量表中设置新的 int9 程序mov word ptr es:[9*4],offset int9mov es:[9*4+2],csmov ax,0b800hmov es,axmov ah,'a's: mov es:[160*12+40*2],ahcall delayinc ahcmp ah,'z'jna s ; 如果不等于'z'就继续mov ax,0mov es,ax; 中断向量表恢复成原来的; 不恢复别的程序无法使用键盘; 这里也可以使用下面这种写法; mov ax,ds:[0]; mov es:[9*4],axpush ds:[0]pop es:[9*4]push ds:[2]pop es:[9*4+2]mov ax,4c00hint 21hdelay:push axpush dxmov dx,10h ; 循环 10/0000h次mov ax,0s1: sub ax,1sbb dx,0cmp ax,0jne s1cmp dx,0 ; 必须 dx 和 ax 都为 0 才退出jne s1pop dxpop axretint9:push axpush bxpush esin al,60h ; 读取60h端口的扫描码; 模拟调用原来的 int9 程序; 不能直接使用 int9 因为程序入口已经改成新的 int9 程序了pushf ; 这里要用两次 pushf 因为 pushf 操作一个字节, 而 pop bx 操作两个字节pushfpop bxand bx,11111100B ; 设置 TF=0 IF=0push bxpopfcall dword ptr ds:[0]; 这里其实可以不需要设置 TF=0 IF=0; 因为能进入新的 int9 程序, 说明当前处于中断中, TF/IF 已经被设置成 0 了cmp al,1 ; esc 扫描码 01jne int9retmov ax,0b800hmov es,axinc byte ptr es:[160*12+40*2+1] ; 属性+1,改变颜色int9ret:pop espop bxpop axiretcode endsend start
不可屏蔽中断
中断类型码为2,因此CS=0AH``IP=8H
