我是靠谱客的博主 老实书本,最近开发中收集的这篇文章主要介绍8086汇编内中断总结之外中断复习总结,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

    可先看看: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汇编内中断总结之外中断复习总结所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(48)

评论列表共有 0 条评论

立即
投稿
返回
顶部