0%

《汇编语言》(第四版) 实验 15

《汇编语言(第四版)》 . 王爽著 . 清华大学出版社 . 2019

实验 15 安装新的 int9 中断例程

外中断

外中断源有两类:

1)可屏蔽中断
CPU 可以不响应,是否响应取决于标志寄存器 IF 位的设置,当 CPU 检测到可屏蔽中断信息时,若 IF=1,则 CPU 在执行完当前指令后响应中断,引发中断过程;若 IF=0,则不响应可屏蔽中断

在中断过程中将 IF 置 0 可以禁止其他的可屏蔽中断,若需要处理则可将 IF 置 1

1
2
sti               ;设置 IF=1
cli ;设置 IF=0

几乎所有由外设引发的外中断都是可屏蔽中断,也是我们主要讨论的对象

可屏蔽中断基本上与内中断的中断过程相同,只是其中断类型码是通过数据总线送入 CPU 的

2)不可屏蔽中断
CPU 必须响应的外中断,检测到不可屏蔽中断信息时,在执行完当前指令后立即响应,引发中断过程,对于 8086CPU,其中断类型码固定为 2

通过对键盘输入的处理来理解 PC 机是如何处理外设输入的

理解以下过程
键盘产生扫描码(通码与断码:断码 = 通码 + 80h)- 扫描码送入处理键盘输入的端口 60h - 引发九号中断 - 执行int 9 中断例程

编写 int 9 中断例程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
assume cs:code,ss:stack

stack segment
db 128 dup (0)
stack ends

;用 ds:0 ds:2 保存原 int 9 中断例程的入口地址
data segment
dw 0,0
data ends

code segment
start: mov ax,stack ;设置栈空间
mov ss,ax
mov sp,128

mov ax,data
mov ds,ax

mov ax,0 ;设置 es 为中断向量表的段地址
mov es,ax

push es:[9*4]
pop ds:[0]
push es:[9*4+2]
pop ds:[2] ;将原来的 int 9 中断例程的入口地址保存

mov word ptr es:[9*4],offset int9
mov es:[9*4+2],cs ;在中断向量表中设置新的 int 9 中断例程的入口地址

mov ax,0b800h ;显示缓冲区的段地址
mov es,ax
mov ah,'a'
s: mov es:[160*12+40*2],ah ;显示 'a'~'z'
call delay ;每显示一个字母后,延时一段时间
inc ah
cmp ah,'z'
jna s

mov ax,0
mov es,ax

push ds:[0]
pop es:[9*4]
push ds:[2]
pop es:[9*4+2] ;在程序返回前恢复原来的地址(原 int 9 中断例程的入口地址)

mov ax,4c00h ;程序返回
int 21h

delay: push ax ;延时函数
push dx
mov dx,100h ;用两个 16 位寄存器来存放 32 位的循环次数
mov ax,0 ;循环 100 0000 h 次(dx:ax)
s1: sub ax,1
sbb dx,0
cmp ax,0
jne s1
cmp dx,0
jne s1
pop dx
pop ax
ret

;------以下为新的 int 9 中断例程--------------------

;int 9 中断例程
;1) 从 60h 端口读出键盘的输入
;2) 调用 BIOS 的 int 9 中断例程,处理其他硬件细节
;3) 判断是否为 Esc 的扫描码,如果是,改变显示的颜色后返回;如果不是则直接返回

int9: push ax
push bx
push es

in al,60h ;键盘端口地址

;模拟 int 指令,调用原 int 9 中断例程
pushf ;标志寄存器入栈

pushf ;置 IF 和 TF 为 0,分别为标志寄存器的第 9 位与第 8 位
pop bx ;通过 pushf 与 popf 的配合修改标志寄存器
and bh,11111100b
push bx
popf

call dword ptr ds:[0] ;对 int 指令进行模拟,调用原来的 int 9 中断例程

cmp al,1 ;ESC 的扫描码
jne int9ret

mov ax,0b800h
mov es,ax
inc byte ptr es:[160*12+40*2+1] ;将属性值加 1,改变颜色

int9ret: pop es
pop bx
pop ax
iret

code ends
end start
测试程序运行

检测点 15.1

(1)

pushf
call dword ptr ds:[0]

无需再设置标志寄存器的 IF 与 TF 位

(2)

找到修改中断向量表的程序段

先用 cli 指令将 IF 置 0,禁止其他的可屏蔽中断,在程序段执行后再用 sti 将 IF 置 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
;在中断向量表中设置新的 int 9 中断例程的入口地址
cli
mov word ptr es:[9*4],offset int9
mov es:[9*4+2],cs
sti

...

;在程序返回前恢复原来的地址
;即原 int 9 中断例程的入口地址
cli
push ds:[0]
pop es:[9*4]
push ds:[2]
pop es:[9*4+2]
sti

安装新的 int 9 中断例程

注意与前面程序的区别,安装程序返回后地址将丢失,新 int 9 中断例程与原 int 9 中断例程的入口地址皆不可保存在安装程序中,于是我们将原 int 9 中断例程的入口地址放在 0000:0200 处,将新 int 9 中断例程放在 0000:0204

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
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 ;设置 ds:si 指向源地址
mov di,204h ;设置 es:di 指向目的地址
mov cx,offset int9end-offset int9 ;设置 cx 为传输长度
cld ;设置传输方向为正

rep movsb

push es:[9*4] ;保存原有 int 9 的中断处理程序的入口地址
pop es:[200h]
push es:[9*4+2]
pop es:[202h]

cli
mov word ptr es:[9*4],204h ;设置中断向量表
mov word ptr es:[9*4+2],0
sti

;此程序无需将中断向量表恢复成原 int 9 中断例程的入口地址

mov ax,4c00h
int 21h

int9: push ax
push bx
push cx
push es

in al,60h ;键盘端口地址

pushf ;调用原 int9 中断处理程序
call dword ptr cs:[200h] ;当此中断例程执行时 (CS)=0

cmp al,3bh ;F1 的扫描码为 3bh
jne int9ret

mov ax,0b800h
mov es,ax
mov bx,1
mov cx,2000 ;改变 4000 个字节中所有奇地址单元中的内容
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
测试程序运行

实验任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
assume cs:code

stack segment
db 128 dup (0)
stack ends

code segment
start: mov ax,stack
mov ss,ax
mov sp,128

;新 int 9 中断例程传送到内存
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

;保存原 int 9 中断例程的入口地址
push es:[9*4]
pop es:[200h]
push es:[9*4+2]
pop es:[202h]

;设置中断向量表表项为新 int 9 中断例程存放的内存地址
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,9eh ;松开 A 键的扫描码(断码)
jne int9ret

mov ax,0b800h
mov es,ax
mov bx,0
mov cx,2000
s: mov byte ptr es:[bx],'A' ;满屏的 A 字符
add bx,2
loop s

int9ret: pop es
pop cx
pop bx
pop ax
iret

int9end: nop

code ends
end start
测试程序运行


汇编语言实验合集

汇编语言实验合集

实验 1 查看 CPU 和内存,用机器指令和汇编指令编程
实验 2 用机器指令和汇编指令编程
实验 3 编程、编译、连接、跟踪
实验 4 [bx] 和 loop 的使用
实验 5 编写、调试具有多个段的程序
实验 6 实践课程中的程序
实验 7 寻址方式在结构化数据访问中的应用
实验 8 分析一个奇怪的程序
实验 9 根据材料编程
实验 10 编写子程序
课程设计 1
实验 11 编写子程序
实验 12 编写 0 号中断的处理程序
实验 13 编写、应用中断例程
实验 14 访问 CMOS RAM
实验 15 安装新的 int9 中断例程
实验 16 编写包含多个功能子程序的中断例程
实验 17 编写包含多个功能子程序的中断例程
课程设计 2