汇编语言中的外中断和内中断差不多
1、取中断类型码n
2、标致寄存器入栈,IF=0,TF=0
2、CS、IP入栈
3、(IP)=(n*4),(CS)=(n*4+2)
但是外中断和内中断的中断类型码的来源不同,所以第一步肯定是不一样的,其他相同
下面来讲一下键盘事件下的处理方式
只要有键盘事件发生,就会触发int 9,这个int 9是硬件扫描过程,其实就是CPU对按键的IO口进行扫描的过程
这里问题就出现了,因为键盘事件发生必定会引发int 9,所以如果要对按按键后进行处理的话,需要将中断程序写在int 9中,但是如果需要扫描键盘的按键,就必须要调用BIOS提供的int 9
解决这种冲突的办法就是先将BIOS提供的int 9的IP和CS储存起来,用模拟int指令的方式来调用原来的int 9,当然新建的int 9需要将其入口的IP和CS放到向量表中
任务:显示a~z,按下ESC结束
代码:
assume cs:code
data segment ;储存原int9的IP和CS
dw 0, 0
data ends
code segment
start:
mov ax, 0 ;将原int9的IP和CS放到data空间中
mov ds, ax
mov ax, data
mov es, ax
push ds:[9*4]
pop es:[0]
push ds:[9*4 + 2]
pop es:[2]
mov ax, 0 ;将新的int9中断的IP,CS写入中断向量表中
mov ds, ax
mov word ptr ds:[9*4], offset do9h
mov word ptr ds:[9*4 + 2], cs
mov ax, 0B800H
mov ds, ax
mov al, 'a'
mov cl, 2
s:mov ds:[10*160 + 8*2], al
mov byte ptr ds:[10*160 + 8*2 + 1], cl
call delay ;调用延时的子程序,方便显示
cmp al, 'z'
je s0
inc al
jmp s
s0:mov al, 'a'
jmp s
send:mov ax, 0 ;将原int9中断的IP,CS恢复
mov ds, ax
mov ax, data
mov es, ax
push es:[0]
pop ds:[9*4]
push es:[2]
pop ds:[9*4 + 2]
mov ax, 4C00H
int 21H
do9h:push ax
push dx
push ds
in al, 60H ;从键盘端口读出键盘的输入
mov dl, al
pushf ;模仿int指令,进行原int9的调用
pushf ;将标致寄存器的IF和TF置零
pop ax
and ah, 11111100B
push ax
popf
mov ax, data;保存下一条指令的IP和CS
mov ds, ax
call dword ptr ds:[0]
cmp dl, 1 ;ESC的扫描码为1
jne ok
jmp send ;跳到循环外,不再执行循环指令
ok:pop ds
pop dx
pop ax
iret
delay:push dx
push ax
mov dx, 10H
mov ax, 0
s1:sub ax, 1 ;-1的反码是65535
sbb dx, 0 ;此时CF寄存器是1,则dx-1
cmp ax, 0 ;内循环
jne s1
cmp dx, 0 ;外循环
jne s1
pop ax
pop dx
ret
code ends
end start
运行结果:
按下ESC键后的显示结果:
为了避免在修改int 9的IP和CS时就发生外部中断,那么会让中断的地址混乱,所以可以改进一下程序
指令:cli:设置IF=0,屏蔽中断
sti:设置IF=1,不屏蔽中断
将这两条指令分别加在修改入口地址的前后,那么就可以避免上面说的那种情况了
例:
cli ;设置IF=0,屏蔽中断
mov ax, 0 ;将新的int9中断的IP,CS写入中断向量表中
mov ds, ax
mov word ptr ds:[9*4], offset do9h
mov word ptr ds:[9*4 + 2], cs
sti ;设置IF=1,不屏蔽中断
这样就避免了在修改IP,CS时发生外部中断
这里还有个问题,这里的jmp是有范围的,如果在程序中超出了jmp的范围,那么就不能完美的跳出程序了,为了解决这个问题,还需要模拟利用中断模拟jmp指令,让jmp指令的范围得到扩充
因为进入到外部中断时,会将CS和IP入栈,只要改变一下IP,在跳回程序后,就能跳到指定的位置
在中断中:
mov bp, sp ;模拟jmp指令,跳出循环
mov word ptr [bp + 8], offset send
这样就能跳出循环了
完整的代码:
assume cs:code
data segment ;储存原int9的IP和CS
dw 0, 0
data ends
code segment
start:
mov ax, 0 ;将原int9的IP和CS放到data空间中
mov ds, ax
mov ax, data
mov es, ax
push ds:[9*4]
pop es:[0]
push ds:[9*4 + 2]
pop es:[2]
mov ax, 0 ;将新的int9中断的IP,CS写入中断向量表中
mov ds, ax
mov word ptr ds:[9*4], offset do9h
mov word ptr ds:[9*4 + 2], cs
mov ax, 0B800H
mov ds, ax
mov al, 'a'
mov cl, 2
s:mov ds:[10*160 + 8*2], al
mov byte ptr ds:[10*160 + 8*2 + 1], cl
call delay ;调用延时的子程序,方便显示
cmp al, 'z'
je s0
inc al
jmp s
s0:mov al, 'a'
jmp s
send:mov ax, 0 ;将原int9中断的IP,CS恢复
mov ds, ax
mov ax, data
mov es, ax
push es:[0]
pop ds:[9*4]
push es:[2]
pop ds:[9*4 + 2]
mov ax, 4C00H
int 21H
do9h:push ax
push dx
push ds
in al, 60H ;从键盘端口读出键盘的输入
mov dl, al
pushf ;模仿int指令,进行原int9的调用
pushf ;将标致寄存器的IF和TF置零
pop ax
and ah, 11111100B
push ax
popf
mov ax, data;保存下一条指令的IP和CS
mov ds, ax
call dword ptr ds:[0]
cmp dl, 1 ;ESC的扫描码为1
jne ok
mov bp, sp ;模拟jmp指令,跳出循环
mov word ptr [bp + 6], offset send
ok:pop ds
pop dx
pop ax
iret
delay:push dx
push ax
mov dx, 10H
mov ax, 0
s1:sub ax, 1 ;-1的反码是65535
sbb dx, 0 ;此时CF寄存器是1,则dx-1
cmp ax, 0 ;内循环
jne s1
cmp dx, 0 ;外循环
jne s1
pop ax
pop dx
ret
code ends
end start
效果是一样的