概述
可先看看:https://blog.csdn.net/qq_37232329/article/details/79920832
在开始外中断之前先来说说端口,这里的端口不是网络上的端口。网络上的端口是逻辑上存在的,即因为计算机在通信时必须要为各个进程建立基于各自协议的连接(当然有些是无连接的比如UDP),所以需要端口防止"撞车"。就好比IP地址是各个小区名字,端口就是门牌号码,A小区中的1号用户要和B小区的2号用户通信,不仅要知道B小区的名字还要知道用户的门牌号码是一个道理。但是这里所说的端口指的是硬件上的概念。
在计算机中,和CPU通过总线相连接的芯片除了各种存储器还有各种接口卡的接口芯片,主板上的接口芯片(CPU通过他们来对部分外设访问),其他芯片(存储系统信息和相关I/O处理)。在这些芯片中都有可以由CPU读写的寄存器。它们在物理上可能处在不同芯片上,但是由于都与总线相连并且都是CPU通过控制总线来传输命令,从CPU角度来说这些寄存器都是端口,并且这些端口在物理空间中都有地址。CPU在逻辑上对他们进行了统一编址建立了一个统一的端口地址空间。CPU可以直接读取CPU内部寄存器,端口和内存单元的内容
在8086CPU中最多可以定位64KB个端口也就是2^16个。所以CPU对外设是不直接控制的,而是通过接口芯片上的寄存器也即端口来发送读写命令间接控制。
-----------------------------------------------------跛了脚的分割线---------------------------------------------------------
所以说了那么多,外中断到底怎么回事儿?
1.CPU面临的主要是两种中断,一种是内中断,一种是外中断。内中断由CPU内部产生,而外中断则是相关外设的输入信息到达相关芯片后由芯片向CPU发送相应的中断信息以此来处理外设输入。
2.外中断又可以分为两类,一种是可屏蔽中断,一种是不可屏蔽中断。这可屏蔽与不可屏蔽与IF标志位关系重大,如果IF == 0那么一旦产生可屏蔽中断CPU不会接受,否则CPU变会响应中断,执行中断所对应的中断处理程序,使用cli和sti分别可以清除和置位IF位可以理解成CLear If即CLI,SeT If即STI。
3.外中断的实现过程和内中断基本相同,回忆一下内中断怎么实现的:1.获取中断类型码N----->2.pushf----->3.IF=0 TF=0----->4.push IP;push CS----->mov cs, [N * 4] mov ip, [N * 4 + 2]。
可屏蔽外中断和内中断唯一实现方式不同的是在第一步,因为可屏蔽外中断是通过数据总线输入的即外设用接口卡通过总线与CPU相连,而内中断在CPU内部产生。这也是为什么第三步IF要清除,因为当发生内中断时候,如果突然给你来了下可屏蔽外中断如果IF为1,那不就是处理内中断处理程序半当中又要跳到可屏蔽外中断处理程序去,这就导致了错误。
不可屏蔽外中断是CPU必须响应的,其中断类型码也是固定为2,所以在中断过程中是不需要读取中断类型码直接是第三点中的2,3,4步就是不可屏蔽外中断的过程。
注:外中断基本都是可屏蔽外中断,除非是紧急情况通过不可屏蔽外中断来处理
PC键盘的处理过程:
当按下键盘时接口卡产生一个扫描码,此扫描码送到接口芯片的寄存器中被称为通码。松开按键同样产生一个扫描码被称为断码。断码 = 通码 + 80h
键盘扫描码: https://blog.csdn.net/qq_37232329/article/details/79926440
强插一个例子:
assume cs:code
stack segment
db 128 dup (0)
stack ends
;这段代码无需多解释,无任何新知识,若有疑问复习前章
code segment
start:
mov ax, stack
mov ss, ax
mov sp, 80h
mov ax, 0b800h
mov es, ax
mov ah, 'a'
s:
mov es:[160 * 12 + 40 * 2], ah
call delay
inc ah
cmp ah, 'z'
jna s
mov ax, 4c00h
int 21h
delay: ;延时过程
push ax
push dx
mov dx, 0010h ;作者的机器肯定比我好...他高位1000而我一开始弄1000时,DosBox直接卡死了
mov ax, 0
s1:
sub ax, 1
sbb dx, 0
cmp ax, 0
jne s1
cmp dx, 0
jne s1
pop dx
pop ax
ret
code ends
end start
这部分代码作者主要是为了为后面做铺垫,运用到的只是前面所学的知识,即单纯只是轮流显示a-z字符还有延时的这个过程
Int9号中断:
Int9中断时BIOS提供的中断,用来进行基本的键盘输入处理,主要工作:
1.读出60h端口的扫描码
2.如果是字符键扫描码则将该扫描码和它所对应的ASCII码送入内存中的BIOS键盘缓冲区,如果是控制键则将其转换为状态字节后写入内存中存储状态字节的单元
3.对键盘系统进行相关控制
BIOS键盘缓冲区可以容纳15个键盘输入,是用来存放int9中断例程所接受的键盘输入的内存区,一个字单元代表一个键盘输入,高位放置扫描码,低位放置ASCII码
下面,重新写一下Int9号中断例程,上代码:
assume cs:code
stack segment
db 128 dup (0)
stack ends
data segment
dw 0, 0 ;这是用来存储原本int9号中断例程的入口位置
data ends
code segment
start:
mov ax, stack
mov ss, ax
mov sp, 80h
mov ax, data
mov ds, ax
mov ax, 0
mov es, ax
;将原来的int 9号中断入口地址暂时存放data段保存
push es:[9 * 4]
pop ds:[0]
push es:[9 * 4 + 2]
pop ds:[2]
;将新的int9中断例程入口地址放入原本的int9号中断所在向量表位置
;这里使用cli和sti的原因是为了防止在设置新的中断例程入口的过程中防止
;键盘敲击或其他原因导致的外中断使CPU去其他地方执行程序导致错误
cli ;IF = 0屏蔽外中断
mov word ptr es:[9 * 4], offset int9
mov word ptr es:[9 * 4 + 2], cs
sti ;恢复外中断
;s段的准备工作
mov ax, 0b800h
mov es, ax
mov ah, 'a'
s: ;显示a-z
mov es:[160 * 12 + 40 * 2], ah
call delay
inc ah
cmp ah, 'z'
jna s
mov ax, 0
mov es, ax
;把原本的int9号中断例程入口放回中断向量表
push ds:[0]
cli ;原因同上面的cli
pop es:[9 * 4]
push ds:[2]
pop es:[9 * 4 + 2]
sti ;原因同上面的sti
mov ax, 4c00h
int 21h
delay:
push ax
push dx
mov dx, 0010h
mov ax, 0
s1:
sub ax, 1
sbb dx, 0
cmp ax, 0
jne s1
cmp dx, 0
jne s1
pop dx
pop ax
ret
int9:
push ax
push bx
push es
in al, 60h ;从60h端口读入键盘扫描码到al
;模拟int指令
pushf ;标志寄存器入栈
;由于进入中断历程后IF和TF自动会被设定为0所以不需要这些代码也可以
; pushf ;这里标志寄存器再入栈是为了改变IF和TF位
; pop bx ;将标志位pop到bx寄存器中
; and bh, 11111100b ;由于IF和TF标志位在8,9位所以把其清除
; push bx 把修改过后的标志位push到堆栈里
; popf ;然后把堆栈中已经IF TF置零的16位标志位pop回标志寄存器中
call dword ptr ds:[0] ;调用原本的int 9号中断例程,即相当于mov cs, ds:[0] mov ds:[0 + 2]
cmp al, 1 ;比较读取的键盘扫描码是否为Esc
jne int9ret ;不是则不发生颜色变化,直接跳回
mov ax, 0b800h
mov es, ax
inc byte ptr es:[160 * 12 + 40 * 2 + 1]
int9ret:
pop es
pop bx
pop ax
iret ;pop ip; pop cs; popf
code ends
end start
总结一下,大部分注释已经说得很清晰了(很讨厌这些注释),大体过程就是,把原本int9中断例程的入口地址放入data段保存,然后把新的int9中断例程代码入口地址放入中断向量表(即原来int9的位置)。新的int9从键盘输入中读取字符看是不是Esc的扫描码,即in al, 60h 和cmp al, 1。(注:1是Esc的扫描码)如果符合那么久模拟int指令来调用原本的int9中断例程(模拟中把TF IF设为0其实不要也可以因为中断例程本来就会设置TF IF为0)。
这个例子是按F1可以让背景色和前景色改变
assume cs:code
stack segment
db 128 dup (0)
stack ends
code segment
start:
mov ax, stack
mov ss, ax
mov sp, 128
push cs
pop ds
;安装中断例程
mov ax, 0
mov es, ax
mov si, offset int9
mov di, 204h
mov cx, offset int9end - offset int9
cld
rep movsb
;把原本的int9号中断例程入口地址放到0:200处即新中断例程的最开始四个字节
push es:[9 * 4]
pop es:[200h]
push es:[9 * 4 + 2]
pop es:[202h]
;把新中断例程的入口地址放入int9中断例程在中断向量表中的位置上
cli
mov word ptr es:[9 * 4], 204h
mov word ptr es:[9 * 4 + 2], 0
sti
mov ax, 4c00h
int 21h
int9:
push ax
push bx
push cx
push es
in al, 60h
;模拟int指令执行旧int9中断
pushf
call dword ptr cs:[200h]
cmp al, 3bh
jne int9ret
mov ax, 0b800h
mov es, ax
mov bx, 1
mov cx, 2000
s:
inc byte ptr es:[bx]
add bx, 2
loop s
int9ret:
pop es
pop cx
pop bx
pop ax
iret
int9end:
nop
code ends
end start
最后是一个习题,要求是按A没情况,松开A后会有满屏幕的A。就是把上面那段代码复制下来后改一改就行了。
我把两道题目结合起来改了一下,上代码:
assume cs:code
stack segment
db 128 dup (0)
stack ends
code segment
start:
mov ax, stack
mov ss, ax
mov sp, 80h
mov ax, 0
mov es, ax
push cs
pop ds
mov di, 204h
mov si, offset int9
mov cx, offset int9end - offset int9
cld
rep movsb
push es:[9 * 4]
push es:[9 * 4 + 2]
pop es:[202h]
pop es:[200h]
cli
mov word ptr es:[9 * 4], 204h
mov word ptr es:[9 * 4 + 2], 0
sti
mov ax, 4c00h
int 21h
int9:
push ax
push bx
push cx
push es
in al, 60h
pushf
call dword ptr cs:[200h]
;这段变了
cmp al, 1eh
je int9ret
cmp al, 1
je s0
cmp al, 9eh
jne int9ret
mov ax, 0b800h
mov es, ax
mov bx, 0
mov si, 1
mov cx, 2000
;控制字符
s:
mov byte ptr es:[bx], 'K'
mov byte ptr es:[bx + 2], 'i'
mov byte ptr es:[bx + 4], 'o'
mov byte ptr es:[bx + 6], 'p'
mov byte ptr es:[bx + 8], 'l'
mov byte ptr es:[bx + 10], 'e'
mov byte ptr es:[bx + 12], 'R'
add bx, 14
loop s
jmp int9ret
;控制颜色变化
s0:
mov ax, 0b800h
mov es, ax
mov bx, 1
mov cx, 2000
s1:
inc byte ptr es:[bx]
add bx, 2
loop s1
int9ret:
pop es
pop cx
pop bx
pop ax
iret
int9end:
nop
code ends
end start
(完)
接下去可看直接定址表:https://blog.csdn.net/qq_37232329/article/details/79939184
最后
以上就是老实书本为你收集整理的8086汇编内中断总结之外中断复习总结的全部内容,希望文章能够帮你解决8086汇编内中断总结之外中断复习总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复