汇编语言学习笔记-4

0x00 内中断

​ 中断意思就是:CPU不再接着(刚执行完的指令)向下执行,而是转去处理特殊信息(中断信息)

​ 那什么时候CPU会马上处理中断信息呢?

image-20230224130151427

​ CPU根据中断信息可以找到要执行的处理程序,因此中断信息和处理程序的入口地址间需建立联系。中断类型码用于定位中断处理程序。

​ 通过中断向量表,可以使用中断类型码(8位),来确定中断处理程序的段地址与偏移地址。中断向量表保存在内存里,里面存放着256个中断处理程序的入口地址。中断向量表在0000:0000~0000:03FF中,共1024个字节。

中断过程(硬件自动执行,程序员无法干预):用中断类型码可在中断向量表中找到中断处理程序的入口(即中断向量),并用它设置CS和IP,使CPU执行中断过程(需要先保存原来CS与IP的值,以便执行完中断后回到原程序点)。具体如下图所示:

image-20230224132509027

​ 由于CPU随时都可能进行中断处理,因此中断处理程序必须一直存储在内存中,且中断处理程序的入口地址(也就是中断向量)必须存储在对应的中断向量表项中。

中断处理程序

​ 其书写方法与子程序类似,具体如下图:

image-20230224133037377

注:iret从栈中依次弹出IP、CS、标志寄存器,汇编语法描述为:pop IP; pop CS; popf

Q&A

​ 已知:

image-20230224135512555

image-20230224135527549

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
assume cs:code, ds:data
data segment
db "overflow!"
data ends

code segment
do0:
mov ax, data
mov ds, ax
mov si, 0
mov ax, 0b800H ; 显示overflow字符串
mov es, ax
mov di, 12*160+36*2 ; es:di指向显存空间的中间位置
mov cx, 9
s:
mov al, ds:[si]
mov es:[di], al
inc si
add di, 2
loop s

mov ax, 4c00H ; 溢出后直接结束了,不返回
int 21H
start:
mov ax, 0H
mov ds, ax
mov si, offset do0
mov ax, 0000H
mov es, ax
mov di, 0200H ; 将do0的代码送入到0000:0200
mov cx, offset start; 也可以写成mov cx, offset start - offset do0
sub cx, offset do0
cld
rep movsb

mov ax, 1000H
mov bh, 1
div bh

mov ax, 4c00H
int 21H
code ends
end start

​ 上述代码有两个问题:

  1. 此程序执行完成后,如果再触发div溢出错误,由于data段数据以被回收,因此会显示随机字符串。
  2. 并没有改变中断向量表第一项,也就是入口地址为0000:0200

​ 修改后的程序为:

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
assume cs:code

code segment
do0:
jmp short do0start
db "overflow!"
do0start:
mov ax, 0000H
mov ds, ax
mov si, 0202H ; 0:200H 有指令jmp short do0start(2字节长)
mov ax, 0b800H ; 显示overflow字符串
mov es, ax
mov di, 24*160+36*2 ; es:di指向显存空间的中间位置

mov cx, 9
s:
mov al, ds:[si]
mov es:[di], al
inc si
add di, 2
loop s

mov ax, 4c00H ; 溢出后直接结束了,不返回
int 21H
start:
mov ax, 0H
mov ds, ax
mov si, offset do0
mov ax, 0000H
mov es, ax
mov di, 0200H ; 将do0的代码送入到0000:0200
mov cx, offset start - offset do0
cld
rep movsb
mov ax, 0
mov es, ax
mov word ptr es:[0], 0200H
mov word ptr es:[2], 0000H

int 0 ; 进入中断处理程序

mov ax, 4c00H
int 21H
code ends
end start

单步中断

​ CPU执行完一条指令之后,如果检测到标志寄存器的TF(Trace Flag)位为1,则产生单步中断,引发中断过程

​ 单步中断的中断类型码为1,中断过程如下:

image-20230224181726078

​ 有一个问题:Debug的T指令在执行一条指令后,就显示各个寄存器的状态,如何实现的?

(1)T命令执行指令时先设置将TF=1,则CPU工作于单步中断方式,使CPU执行完一条程序指令后就引发单步中断。

(2)执行对应中断处理程序。寄存器的内容被显示在屏幕上,并且等待输入命令。

补充

​ 有些情况下,CPU 在执行完当前指令后,即便是发生中断,也不会响应。例如,在执行完向ss寄存器传送数据的指令后,即便是发生中断,CPU 也不会响应。为啥咧?因为ss:sp联合指向栈顶,对它们的设置应连续完成。若设置ss的指令后CPU响应中断,中断过程将CS和IP值入栈,此时ss改变而sp未改变,ss:sp指向的不是正确的栈顶,将引起错误。

​ 那么debug的单步中断是如何避免这个问题的呢?(那么之前实验,出现的单步运行时,mov ss,ax之后一条指令没有被显示,这个原因我们就知道啦。)

image-20230224183337133

0x01 int指令

​ 中断信息可以来自于CPU的内部或者外部,int指令引发的中断是内中断。int n的执行过程如下:

image-20230224190034447

Q&A

image-20230224191123559

​ 安装程序如下(安装7cH中断):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
assume cs:code
code segment
cube:
mul ax
iret
start:
mov ax, cs ; ds:[si]->es:[di]
mov ds, ax
mov si, offset cube
mov ax, 0000H
mov es, ax
mov di, 0200H
cld
mov cx, offset start - offset cube
rep movsb

mov ax, 0 ; 设置中断表
mov es, ax
mov word ptr es:[7cH*4], 0200H
mov word ptr es:[7cH*4+2], 0000H

code ends
end start

​ 应用程序如下:

1
2
3
4
5
6
7
8
9
10
11
assume cs:code
code segment
start: ; 计算2*3456^2
mov ax, 3456
int 7cH
add ax, ax
adc dx, dx
mov ax, 4c00H
int 21H
code ends
end start

注:intiret类似于callret

image-20230224194301948

​ 安装程序:

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
assume cs:code
code segment
upper:
push si
push bx
s0:
mov bl, ds:[si]
cmp bl, 0H
je upper_end
and bl, 11011111B
mov ds:[si], bl
inc si
loop s0
upper_end:
pop bx
pop si
iret
start:
mov ax, cs ; ds:[si]->es:[di]
mov ds, ax
mov si, offset upper
mov ax, 0000H
mov es, ax
mov di, 0200H
mov cx, offset start - offset upper
cld
rep movsb

mov ax, 0000H
mov es, ax
mov word ptr es:[7cH*4], 0200H
mov word ptr es:[7cH*4+2], 0000H

mov ax, 4c00H
int 21H
code ends
end start

​ 应用程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
assume cs:code, ds:data
data segment
db 'aaabbb', 0
data ends
code segment
start:
mov ax, data
mov ds, ax
mov si, 0
int 7cH

mov ax, 4c00H
int 21H
code ends
end start

image-20230225111952160

image-20230225112055338

​ 应用程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
assume cs:code
code segment
start:
mov ax, 0b800H
mov es, ax
mov si, 160*12
mov bx, offset s_end - offset s
mov cx, 80
s:
mov byte ptr es:[si], '!'
add si, 2
int 7cH
s_end:
mov ax, 4c00H
int 21H
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
assume cs:code
code segment
install:
push bp
mov bp, sp
cmp cx, 0
jna install_end
dec cx
sub [bp+2], bx
install_end:
pop bp
iret
start:
mov ax, 0000H
mov es, ax
mov di, 0200H
mov ax, cs
mov ds, ax
mov si, offset install
mov cx, offset start - offset install
cld ; 控制正向还是反向
rep movsb

mov ax, 0000H
mov es, ax
mov word ptr es:[7cH*4], 0200H
mov word ptr es:[7cH*4+2], 0000H

mov ax, 4c00H
int 21H
code ends
end start

image-20230225130219592

​ 最大位移为FFFFH。

image-20230225130535962

​ 安装程序为:

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
assume cs:code
code segment
install:
push bp
mov bp, sp
add ss:[bp+2], bx
pop bp
iret
start:
mov ax, cs
mov ds, ax
mov si, offset install
mov ax, 0000H
mov es, ax
mov di, 0200H
mov cx, offset start - offset install
cld
rep movsb

mov ax, 0000H
mov es, ax
mov es:[7cH*4], 0200H
mov es:[7cH*4+2], 0000H

mov ax, 4c00H
int 21H
code ends
end start

0x02 BIOS与DOS提供的中断例程

​ BIOS(Basic Input Output System)在主板ROM中,里面存放着这些内容:

(1)硬件系统的检测与初始化程序。

(2)外部中断与内部中断的中断例程。

(3)对硬件设备进行I/O操作的中断例程。

​ 操作系统DOS也提供了中断例程,从操作系统的角度来看,DOS的中断例程就是操作系统向程序员提供的编程资源

​ 程序员用int调用BIOS和DOS的中断例程

DOS与BIOS的中断例程是如何安装到内存中去的?

(1) 开机后CPU初始化(CS)=0FFFFH,(IP)=0,从FFFF:0处开始执行第一条指令,该指令为一条转跳指令。

(2)CPU转去执行BIOS中的硬件系统检测初始化程序(将BIOS的中断例程的入口地址登记在中断向量表中)。

(3) 硬件系统检测和初始化完成后,调用int 19h进行操作系统的引导。从此将计算机交由操作系统DOS控制。

(4)DOS 启动后,除完成其它工作外,还将它所提供的中断例程装入内存,并建立相应的中断向量

0x03 BIOS中断例程应用

int 10h是BIOS提供的中断例程,包含了多个与屏幕输出相关的子程序。当中断例程包含多个子程序时,用传递的参数来决定执行哪个子程序(用ah来传递内部子程序的编号)

int 10h举例(设置光标位置

1
2
3
4
5
6
7
ah -> 子程序编号
bh -> 页参数
dh -> 行参数
dl -> 列参数
al -> 字符
bl -> 颜色属性
cx -> 字符重复个数

image-20230225134605037

注:在80*25字符模式下,行号取值范围为0-24,列号取值范围为0-79

而bh页号的含义为:

image-20230225134255932

image-20230225140935439

注:BL颜色属性的格式为:

image-20230225141411017

例子:在屏幕第5行12列显示3个红底高亮闪烁绿色的’a’

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
assume cs:code
code segment
start:
; 设置光标到:第0页-第5行-第12列
mov ah, 2
mov bh, 0
mov dh, 5
mov dl, 12
int 10h

mov ah, 9
mov al, 'a'
mov bl, 11001010B
mov bh, 0
mov cx, 3
int 10h

mov ax, 4c00H
int 21H
code ends
end start

0x04 DOS中断例程应用

int 21h中断例程是DOS提供的中断例程。

​ 例如程序返回功能:

image-20230225142304937

​ DOS的int 21h也可以在在光标位置显示字符串,格式如下:

1
2
3
ds:dx     ; 指向要显示的字符串,以'$'作为结束符
mov ah, 9 ; 功能号9,表示在光标位置处显示字符串
int 21H

​ 例子,在屏幕的第5行12列显示字符串”Welcome to masm!”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
assume cs:code, ds:data
data segment
db 'Welcome to masm!$'
data ends
code segment
start:
; 设置光标到:第0页-第12行-第5列
mov ah, 2
mov bh, 0
mov dh, 5
mov dl, 12
int 10h

mov ax, data
mov ds, ax
mov dx, 0
mov ax, 0900H
int 21h

mov ax, 4c00H
int 21H
code ends
end start

0x05 端口

​ CPU可以直接读写3个地方的数据:CPU 内部的寄存器;内存单元;端口。

端口读写不能用movpushpop等内存读写指令。

​ 端口的读写指令有:in表示从端口读取数据,out表示向端口写入数据。

​ 举例说明:in al, 60H,表示从60h号端口读入一个字节,执行流程如下:

(1)CPU通过地址总线将地址信息60h发出;

(2)CPU通过控制总线发出端口读命令,选中端口所在的芯片,并通知它,将要从中读取数据;

(3)端口所在的芯片将60h端口中的数据通过数据总线送入CPU。

注:in和out指令与端口进行数据读写时,只能使用固定的寄存器ax或者al

​ 端口读写举例:

1
2
3
4
5
6
7
; 0-255端口
in al, 20h ;从20h端口读入一个字节
out 20h, al;向20h端口写入一个字节
; 256-65535端口
mov dx, 3f8H
in al, dx ;从3f8H端口读入一个字节
out dx, al ;向3f8H端口写入一个字节

0x06 CMOS RAM芯片

PC机中有一个CMOS RAM芯片,其有如下特征:

(1)包含一个时钟和一个有128字节的RAM存储器。

(2)该芯片靠电池供电。所以,关机后其内部的实时钟仍可正常工作, RAM 中的信息不丢失。

(3)128 个字节的 RAM 中,内部时钟占用 0-0dh单元来保存时间信息,其余大部分单元用于保存系统配置信息,供系统启动时BIOS程序读取。

(4)此芯片有两个端口:70h地址端口与71h数据端口。70h存放要访问的CMOS RAM单元的地址;71h存放从选定的CMOS RAM单元中读取的数据,或要写入到其中的数据。

​ 例如,要读取CMOS RAM的2号单元:

1
2
3
mov al, 2
out 70h, al
in al, 71h

​ 再比如,写入CMOS RAM的2号单元0。

1
2
3
4
mov al, 2
out 70h, al
mov al, 0
out 70h, al

​ CMOS RAM中有128字节的RAM,其中0-0Dh放着当前时间,具体为:

image-20230304214041855

​ 给出一个题目,如下:

image-20230304214303899

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
assume cs:code
code segment
start:
; 取出月份信息
mov al, 8
out 70h, al
in al, 71h
mov ah, al
mov cl, 4
shr ah, cl
and al, 00001111B

; 将月份信息转为ascii码
add ah, 30H
add al, 30H

; 打印到屏幕上
mov bx, 0b800H
mov es, bx
mov byte ptr es:[160*12+40*2], ah
mov byte ptr es:[160*12+40*2+2], al

mov ax, 4c00H
int 21H
code ends
end start

0x07 shl与shr指令

​ 逻辑移位指令。例如shl为左移指令:将一个寄存器或内存单元中的数据向左移位;将移出的一位写入CF中;最低位补0。

​ 当移动位数大于1时,移动位数要放入cl中。

0x08 外中断

​ CPU能够执行指令进行运算,并对外部I/O设备进行控制。

PC的主板上有接口芯片,接口芯片内部有很多寄存器,CPU将这些寄存器当作端口来访问。外设与CPU靠接口芯片(端口)来进行联系,如下所示:

image-20230304220741003

​ CPU 在执行完当前指令后,可以检测到外设发送过来的中断信息,引发中断过程,处理外设的输入。

中断类型

​ 可屏蔽中断与不可屏蔽中断。可屏蔽中断是CPU可以不立即响应的(可以屏蔽的)外中断。

​ 标志寄存器的IF位决定CPU是否响应可屏蔽中断:

​ (1)如果IF=1,则CPU在执行完当前指令后响应中断,引发中断过程;

​ (2)如果IF=0,则不响应此可屏蔽中断。

回顾内中断的中断过程:

(1)取中断类型码n

(2)标志寄存器入栈,IF=0TF=0IF=0代表禁止其他的可屏蔽中断。sti用于设置IF=1,cli用于设置IF=0

(3)CS、IP入栈。

(4)(IP)=(n*4)(CS)=(n*4+2),由此转去执行中断处理程序。

​ 内中断的中断类型码是在CPU内部产生的。可屏蔽外中断的信息来自于CPU外部,中断类型码是通过数据总线送入CPU的;

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

不可屏蔽中断的中断过程为:

(1)标志寄存器入栈,IF=0TF=0;CS、IP入栈。

(2)(IP)=(8)(CS)=(0AH)

几乎所有由外设引发的外中断都是可屏蔽中断。

键盘输入的处理过程

(1)键盘输入。(2)引发9号中断。(3)执行int 9中断例程。

​ 键盘中有芯片扫描键盘上每个键的开关状态:按下一个键时,该芯片产生一个扫描码(通码,对应于按下键的位置),通码被送入主板上的相关接口芯片内端口地址为60H的寄存器。松开按下的键时,产生扫描码(断码,对应于松开键的位置),断码被送入60H端口中。

​ 其中,断码与通码之间的关系为:断码=通码+80h。具体流程为:

image-20230304224728897

补充:BIOS键盘缓冲区是系统启动后,BIOS用于存放int 9中断例程所接收的键盘输入的内存区,可以存储15个键盘输入,一个键盘输入用一个字存放,高位存放扫描码,低位存放字符码(ascii)。

​ 如果要按下若是控制键(如 Ctrl )和切换键(如 CapsLock),则会改变键盘状态字节,其具体描述如下:

image-20230304225531643

编写int 9中断例程

题目:编程在屏幕中间依次显示a-z ,并可以让人看清。在显示的过程中,按下Esc键后,改变显示的颜色。

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
assume cs:code
code segment
; 循环100000h次,能让人看清
empty_cycle:
push ax
push bx

mov ax, 0
mov bx, 10h
s1:
sub ax, 1
sbb bx, 0
cmp ax, 0
jne s1
cmp dx, 0
jne s1

pop bx
pop ax
ret

start:
mov bx, 0b800H
mov es, bx
mov cx, 26
mov al, 'a'
s0:
mov es:[160*12+40*2], al
inc al
call empty_cycle
loop s0

mov ax, 4c00H
int 21H
code ends
end start

上述代码实现了编程在屏幕中间依次显示a-z ,并可以让人看清,那怎么改变颜色呢?

补充:intcall的比较。

image-20230304231137294

image-20230304231157491

​ 那么如果要用call来模仿int的话,就是:

image-20230304231356735

1
2
3
4
5
6
7
pushf ; (1)标志寄存器入栈
pushf ; (2)IF=0,TF=0
pop ax
and ah, 11111100B
push ax
popf
call dword ptr ds:[0] ; (3)

​ 题目思路如下:

image-20230304230901362

1
2
3
4
5
6
7
8
9
10
11
12
13
in al, 60h ; (1)从60h端口读取键盘输入
;(2)由于int 9被重写了,所以不能直接用Int 9来进行步骤2,那么假设旧的int 9入口放在了ds:[0]中,那么就要调用它
pushf
pushf
pop ax
and ah, 11111100B
push ax
popf
call dword ptr ds:[0]
; (3)如何改变颜色呢?
; 字符显示在b800:160*12+40*2;其字符属性的控制字节:b800:160*12+40*2+1。
; inc b800:[160*12+40*2+1] 就可以改变颜色
; 注意:要在该程序返回前,将中断向量表中的int 9中断例程的入口地址恢复为原来的地址。否则程序返回后,别的程序将无法使用键盘。

​ 最终程序为:(不用持久化安装int9例程

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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
assume cs:code, ss:stack, ds:data
stack segment
db 128 dup(0)
stack ends

data segment
dw 2 dup(0)
data ends

code segment
;--------------------------- 新的 int 9 例程 ----------------------------
int9:
push ax
push bx
push es

in al, 60h ; 获取低位,al内容是ascii码

; 对int进行模拟
pushf
pushf
pop bx
and bh, 11111100B
push bx
popf
call dword ptr ds:[0]

cmp al, 1h ; Esc的扫描码为1
jne int9_end

; Esc改变字符颜色
mov bx, 0b800H
mov es, bx
inc byte ptr es:[160*12+40*2+1]
int9_end:
pop es
pop bx
pop ax
iret
;--------------------------- 新的 int 9 例程 ---------------------------

;--------------------------- 空循环 ------------------------------------
; 循环100000h次,能让人看清
empty_cycle:
push ax
push bx

mov ax, 0
mov bx, 10h
s1:
sub ax, 1
sbb bx, 0
cmp ax, 0
jne s1
cmp dx, 0
jne s1

pop bx
pop ax
ret
;--------------------------- 空循环 ------------------------------------
;--------------------------- 安装新的int 9 例程 ------------------------
int9_install:

; 保存旧的int 9地址到 ds:[2], ds:[0]
mov ax, 0000H
mov es, ax
push es:[9*4] ; 旧的int 9偏移地址
pop ds:[0]
push es:[9*4+2] ; 新的int 9段地址
pop ds:[2]


; 设置新的int9的入口地址
; 0000:[9*4] 中放入 offset int9,0000:[9*4+2] 中放入 cs。
mov ax, 0000H
mov es, ax
mov es:[9*4], offset int9
mov es:[9*4+2], cs

jmp start_1
;--------------------------- 安装新的int 9 例程 ------------------------
;--------------------------- 主程序 -----------------------------------
start_0:
; 初始化堆栈
mov ax, stack
mov ss, ax
mov sp, 128

; 初始化data段
mov ax, data
mov ds, ax

; 安装新的int 9例程
jmp int9_install
start_1:
; 屏幕上依次显示a-z
mov ax, 0b800H
mov es, ax
mov cx, 26
mov al, 'a'
s0:
mov es:[160*12+40*2], al
inc al
call empty_cycle
loop s0

; 将旧的int 9例程地址放回原位
mov ax, 0000H
mov es, ax
push ds:[0]
pop es:[9*4]
push ds:[2]
pop es:[9*4+2]

; 程序结束
mov ax, 4c00H
int 21H
;--------------------------- 主程序 -----------------------------------
code ends
end start_0

题目:安装一个新的int 9中断例程,在DOS下,按Esc键后改变当前屏幕的显示颜色,其他的键照常处理。

注:如何改变屏幕颜色?改变从B800开始的4000个字节中的所有奇地址单元中的内容,当前屏幕的显示颜色即发生改变。

​ 以下是int9_new_install安装程序:

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
assume cs:code, ss:stack

stack segment
db 128 dup(0)
stack ends

code segment
;------------------ int9_new ----------------------------
int9_new:
push ax
push bx
push cx
push es
; 取当前键盘输入的元素
in al, 60H

; 放入标志寄存器
pushf
pushf
pop bx
and bx, 11111100B
push bx
popf
mov ax, 0000H
mov es, ax
call dword ptr es:[0200H]

; 更改屏幕颜色
cmp al, 3BH
jne int9_new_end
mov ax, 0B800H
mov es, ax
mov cx, 2000
mov bx, 0
s0:
inc byte ptr es:[bx+1]
add bx, 2
loop s0
int9_new_end:
pop es
pop cx
pop bx
pop ax
iret
;------------------ int9_new ----------------------------

;------------------ main --------------------------------
start:
; 初始化堆栈
mov ax, stack
mov ss, ax
mov sp, 128

; 保存int9_old的地址
mov ax, 0000H
mov es, ax
push es:[9*4]
pop es:[0200H]
push es:[9*4+2]
pop es:[0200H+2]

; 安装int9_new
; ds:[si]->es:[di]
mov ax, cs
mov ds, ax
mov si, offset int9_new
mov ax, 0000H
mov es, ax
mov di, 0210H
mov cx, offset start - offset int9_new
cld
rep movsb

; 改变入口地址
mov ax, 0000H
mov es, ax
cli
mov word ptr es:[9H*4], 0210H
mov word ptr es:[9H*4+2], 0000H
sti

mov ax, 4c00H
int 21H
;------------------ main --------------------------------
code ends

end start

​ 以下是call调用程序:

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
assume cs:code, ss:stack
stack segment
db 128 dup(0)
stack ends

code segment
;------------------ empty_cycle ---------------------
empty_cycle:
push ax
push bx

mov ax, 0
mov bx, 0FFFFH
s0:
sub ax, 1
sbb bx, 0
cmp ax, 0
jne s0
cmp bx, 0
jne s0

pop bx
pop ax
ret
;------------------ empty_cycle ---------------------
;------------------ main ----------------------------
start:
call empty_cycle

mov ax, 4c00H
int 21H
;------------------ main ----------------------------
code ends

end start

​ 以下是int9_new_uninstall卸载程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
assume cs:code, ss:stack
stack segment
db 128 dup(0)
stack ends

code segment
;------------------ main ----------------------------
start:
; 将旧的 int9 地址还原
mov ax, 0000H
mov es, ax
push es:[0200H]
pop es:[9*4]
push es:[0200H+2]
pop es:[9*4+2]

mov ax, 4c00H
int 21H
;------------------ main ----------------------------
code ends

end start

调了好久都没调好(不知道为啥,cli无法屏蔽外部中断),什么勾八程序。

​ CPU对外设输入的处理方法:

​ (1)外设的输入送入端口;

​ (2)向CPU 发出外中断(可屏蔽中断)信息;

​ (3)CPU检测到可屏蔽中断信息,如果IF=1,CPU在执行完当前指令后响应中断,执行相应的中断例程;

​ (4)可在中断例程中实现对外设输入的处理。

留言

2023-02-24

© 2024 wd-z711

⬆︎TOP