汇编语言实验

本文最后更新于:2023年9月20日 下午

loop双层循环


H655pWu.md.png

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

code segment

start: mov dx,0
mov cx,10

s: push cx

b: add dx,cx

loop b

pop cx
loop s


mov ax,4c00h
int 21h


code ends
end start


  • 两层循环解决

实验4 [bx]和loop的使用


1.编程,向内存0:200-0:23F依次传送数据0-63(3FH)。

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
assume cs:code         ;八条指令的

code segment

start: mov bx,0h
mov ds,bx

mov cx,64

s: mov [bx+200h],bx
inc bx
loop s

mov ax,4c00h
int 21h
code ends
end start




assume cs:code ;九条指令的,书上现在还规定指令数

code segment

start: mov ax,0020H
mov ds,ax
mov bx,0
mov cx,64

s: mov [bx],bl
inc bx
loop s


mov ax,4c00h
int 21h
code ends
end start



实验5 编写、调试具有多个段的程序


1.程 序如下,编写code段中的代码,将a段和b段中的数据依次相加,将结果保存到c段中。

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
assume cs:code
a segment
db 1,2,3,4,5,6,7,8
a ends
b segment
db 1,2,3,4,5,6,7,8
b ends
c segment
db 0,0,0,0,0,0,0,0
c ends
code segment
start:
?
code ends
end start


补充后的代码如下:

assume cs:code
a segment
db 1,2,3,4,5,6,7,8
a ends
b segment
db 1,2,3,4,5,6,7,8
b ends
c segment
db 0,0,0,0,0,0,0,0
c ends
code segment
start: mov ax,a
mov ds,ax
mov cx,8
mov si,0

s: mov dx,0
add dl,[si]
add dl,[16+si]
mov [32+si],dl
inc si
loop s

mov ax,4c00h
int 21h
code ends
end start

2.程序 如下,编写code段代码,用push指令将a段中的前8个字型数据,逆序存储到b段中。

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
assume cs:code 
a segment
dw 1,2,3,4,5,6,7,8,9,0ah,0bh,0ch,0dh,0eh,0fh,0ffh
a ends
b segment
dw 0,0,0,0,0,0,0,0
b ends
code segment
start:
?
code ends
end start


补充后的代码如下:

assume cs:code
a segment
dw 1,2,3,4,5,6,7,8,9,0ah,0bh,0ch,0dh,0eh,0fh,0ffh
a ends

b segment
dw 0,0,0,0,0,0,0,0
b ends
code segment

start: mov ax,a
mov ds,ax

mov ax,b
mov ss,ax
mov sp,16

mov bx,0
mov cx,8
s: push [bx]
add bx,2
loop s


mov ax,4c00h
int 21h
code ends
end start

实验6 实践课程中的程序


将下面的程序补充完整,实现将 datasg 段中的每个单词的前 4 个字母改为大写字母。【要求:仅能修改 codesg segment 的代码】

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
assume cs:codesg, ss:stacksg, ds:datasg

stacksg segment
dw 0,0,0,0,0,0,0,0
stacksg ends

datasg segment
db '1. display '
db '2. brows '
db '3. replace '
db '4. modify '
datasg ends

codesg segment

start:
mov ax,4c00h
int 21h

codesg ends
end start



修改后的代码:

assume cs:codesg, ss:stacksg, ds:datasg

stacksg segment
dw 0,0,0,0,0,0,0,0
stacksg ends

datasg segment
db '1. display '
db '2. brows '
db '3. replace '
db '4. modify '
datasg ends

codesg segment
start: mov ax,stacksg
mov ss,ax
mov sp,16

mov ax,datasg
mov ds,ax
mov bx,0

mov cx,4

s0: push cx
mov si,0
mov cx,4

s1: mov al,[bx+3+si]
and al,11011111b
mov [bx+3+si],al

inc si
loop s1

add bx,16
pop cx
loop s0



mov ax,4c00h
int 21h
codesg ends

end start

实验7 寻址方式在结构化数据访问中的应用


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

data segment
db '1975','1976','1977','1978','1979','1980','1981','1982','1983'
db '1984','1985','1986','1987','1988','1989','1990','1991','1992'
db '1993','1994','1995'
;以上是表示21年的21个字符串
dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514
dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
;以上是表示21年公司总收的21个dword型数据
dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
dw 11542,14430,15257,17800
;以上是表示21年公司雇员人数的21个word型数据
data ends


table segment
db 21 dup ('year summ ne ?? ')
table ends


codesg segment
start: mov ax,data
mov es,ax

mov ax,table
mov ds,ax

mov bx,0
mov si,0 ;初始化年份和收入的具体位置
mov di,0 ;初始化雇员数和人均收入的具体位置
mov cx,21

s: mov ax,es:[si] ;年份和收入的低16bit,占两个字节
mov [bx+0h],ax
mov ax,es:[si+54h] ;加54h是因为21*4=84=54h
mov [bx+5h],ax
add si,2

mov ax,es:[si] ;年份和收入的高16bit,占两个字节
mov [bx+2h],ax
mov ax,es:[si+54h]
mov [bx+7h],ax
add si,2

mov ax,es:[di+0a8h] ;雇员数,占两个字节,加0a8h是因为84*2=168=a8h
mov [bx+0ah],ax
add di,2

mov ax,[bx+5h]
mov dx,[bx+7h]
div word ptr [bx+0ah] ;除法算人均收入
mov [bx+0dh],ax

add bx,16
loop s

mov ax,4c00h
int 21h
codesg ends


end start

实验9 根据材料编程


1.将你的姓名和学号在屏幕上中间显示出来。字符属性要求如下: 学号尾号为 1,3,5,7,9 的为黑底绿色;学号尾号为 0,2,4,6,8 的为黑底红色。

  • BL(闪烁) R G B(背景) I (高亮) R G B (前景)

  • 80x25彩色字符模式显示缓冲区(以下简称为显示缓冲区)的结构:

    • 内存地址空间中,B8000H~BFFFFH 共32KB的空间,为80X25 彩色字符模式的显示缓冲区。向这个地址空间写入数据,写入的内容将立即出现在显示器上。
    • 在 80x25 彩色字符模式下,显示器可以显示 25 行,每行80个字符,每个字符可以有256 种属性(背景色、前景色、闪、高亮等合信息)。
    • 这样,一个字符在显示缓冲区中就要占两个字节,分别存放字符的 ASCII码和属性。
    • 80x25模式下,一屏的内容在显示缓冲区中共占4000个字节。
    • 显示缓冲区分为 8页,每页 4KB(4000B),显示器可以显示任意一页的内容。一般情况下,显示第0页的内容。也就是说通常情况下,B8000H~B8F9FH 中的4000个字节的内容将出现在显示器上。
  • 在一页显示缓冲区中:

    • 偏移000~09F对应显示器上的第1行(80个字符占160个字节);
    • 偏移0A0~13F对应显示器上的第2行;
    • 偏移140~1DF对应显示器上的第3行;
    • 依此类推,可知,偏移FOO~F9F 对应显示器上的第25 行。
  • 在一行中,一个字符占两个字节的存储空间(一个字),低位字节存储字符的 ASCII码,高位字节存储字符的属性。一行共有 80个字符,占160个字节。即在一行中:

    • 00~01单元对应显示器上的第 1列:
    • 02~03单元对应显示器上的第2列
    • 04~05单元对应显示器上的第3 列
    • 依此类推,可知,9E~9F 单元对应显示器上的第 80 列
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
assume cs:code

data segment
db 'ID: 2021100001, Name: Lu Shuaichao'
data ends

code segment

start:
mov ax,data
mov ds,ax

mov bx,0
mov ax,0B800H
mov es,ax ;es 控制打印到屏幕上

mov bp,070eh

;bp 控制打印在屏幕上具体的位置。显示器中间的位置为第 12 行(一共25行嘛),对应的偏移为06e0h(11*160)。
;要打印的内容共 34 个字符,每个字符占两字节,每行占 160个字节,所以要从与行首位置偏移为 46(002eh)的位置写入,这个位置刚好为中间的位置。
;06e0h + 002eh 最终可得出应从偏移为 070eh 的位置开始写入。

mov dl,00000100b ;dl 寄存器存放颜色属性

mov cx,34 ;一次打印循环次数

s2: mov al,[bx]
mov es:[bp],al
mov es:[bp+1],dl ;每次向显存中写入一个字符(两个字节)

inc bx ;下一个字符
add bp,2
loop s2

s0: mov ax,4c00h
int 21h

code ends
end start

实验10 编写子程序


1.显示字符串

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
assume cs:code
data segment
db 'Welcome to masm!',0
data ends

code segment

start: mov dh,8 ;指定行号
mov dl,3 ;指定列号
mov cl,2 ;指定颜色
mov ax,data
mov ds,ax
mov si,0
call show_str

mov ax,4c00h
int 21h

show_str: push ax
push bx
push dx
push si

mov ax,0B800H
mov es,ax ;设置es为显示区段地址

mov ax,00A0H ;每行80个字符,一共占160个字节
mul dh ;设置字符显示在哪行
mov dh,0
add ax,dx
add ax,dx ;加两次是因为一个字符占两个字节,
;比如屏幕上的一个字母a,它就占两个字节,一个字节是ascii码,一个字节是属性
mov bx,ax ;bx是显示区的偏移地址

mov al,cl ;颜色属性存入al
mov si,0

a: mov cl,ds:[si] ;ds:[si]是当前指向的字符
mov ch,0
jcxz ok ;读到0就退出循环

mov es:[bx],cl ;第一个是字母
mov es:[bx+1],al ;第二个是属性

inc si
add bx,2
jmp short a


ok: pop si
pop dx
pop bx
pop ax
ret


code ends
end start


2.数值显示(加上判断溢出后的代码没看懂)

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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
assume cs:code,ds:data,ss:stack
data segment
db 10 dup(0)
data ends

stack segment
dw 16 dup(0) ;32字节
stack ends

code segment
start:
mov bx,data ;设置ds段地址
mov ds,bx
mov bx,stack ;设置栈顶
mov ss,bx
mov sp,20H

mov ax,12666 ;要显示的数据
mov si,0 ;ds:si指向字符串首地址
call dtoc ;将数据转为十进制字符

mov dh,8 ;在屏幕第几行开始显示
mov dl,3 ;在屏幕第几列开始显示
mov cl,2 ;显示的字符的颜色
call show_str

mov ax,4c00H ;程序返回
int 21H


dtoc: ;功能:将给定的word型数据转为十进制字符形式,存入data段,首地址ds:si
;参数:ax 指定的word数据
;返回:ds:si指向data段字符串首地址

push bx; ;将子程序用到的寄存器压入栈
push cx;
push dx;
push si;

mov bx,000aH ;bl = 除数,bh = 一共除了几次
mov dx,0 ;即将进行除法,dx是高16位,低16位在ax中
pushyushu:
mov cx,0
mov cl,bl ;cx = 除数
call divdw ;调用不会溢出的除法函数,结果的商的高16位,在dx中,
;低16位在ax中,余数在cx中,余数一定<10

push cx ;cx=余数,这个余数在显示的时候要倒序显示,因此先压入栈
inc bh ;记录将余数压入栈的次数
mov cx,ax ;cx = ax = 结果的商的低16位
add cx,dx ;dx是结果的商的高16位,ax和dx一定都是非负数
jcxz popyushu ;若cx=0,则说明除法计算已经完毕,跳转下一步执行
jmp short pushyushu

popyushu:
mov ch,0
mov cl,bh ;ch=0,所以cx = 将余数压入栈的次数,也就是接下来的循环次数
s1:
pop ax; ;从栈中pop出一个余数
add ax,30H ;从数字转为对应的数字字符
mov ds:[si],al ;用al就够了
inc si
loop s1

pop si ;子程序结束,将寄存器的值pop出来
pop dx;
pop cx;
pop bx;

ret

divdw: ;功能:计算word型被除数与byte型除数的除法
;参数: ax=被除数低16位,dx=被除数高16位,cx = 除数
;返回: ax=商的低16位,dx=商的高16位,cx = 余数

;计算公式: X/N = int( H/N ) * 65536 + [rem( H/N) * 65536 + L]/N
;其中X为被除数,N为除数,H为被除数的高16位,L为被除数的低16位,
;int()表示结果的商,rem()表示结果的余数。

push bx ;bx是额外用到的寄存器,要压入栈

mov bx,ax ;bx=L
mov ax,dx ;ax=H
mov dx,0 ;dx=0
div cx ;计算H/N,结果的商即int(H/N)保存在ax,余数即rem(H/N)保存在dx

;接下来要计算int(H/N)*65536,思考一下,65536就是0001 0000 H,
;因此计算结果就是,高16位=int(H/N),低16位为0000H。

push ax ;将int(H/N)*65536结果的高16位,即int(H/N),压入栈
mov ax,0
push ax ;将int(H/N)*65536结果的低16位,即0000H,压入栈

;接下来要计算 rem(H/N)*65536 ,同理可得,
;计算结果为 高16位= rem(H/N)*65536 ,即此时dx的值,
;低16位为 0000H。

mov ax,bx ;ax = bx = L ,即 [rem(H/N)*65536 + L]的低16位
div cx ;计算 [rem( H/N) * 65536 + L]/N ,结果的商保存在ax,余数保存在dx

;接下来要将两项求和。 左边项的高、低16位都在栈中,
;其中高16位就是最终结果的高16位,低16位是0000H。
;右边项的商为16位,在ax中,也就是最终结果的低16位,
;余数在dx中,也就是最终结果的余数。

mov cx,dx ;cx = 最终结果的余数
pop bx ;cx = int(H/N)*65536结果的低16位,即0000H。
pop dx ;bx = int(H/N)*65536结果的高16位,即最终结果的高16位

pop bx ;还原bx的值

ret


show_str:
;功能:将data段中首地址为ds:si的字符,以指定颜色显示在屏幕指定位置
;参数:dh 行号, dl 列号 ,cl 颜色
;返回:无

push dx ;将子程序用到的寄存器压入栈
push si
push ax
push bx

mov ax,0B800H ;设置es为显示区段地址
mov es,ax

mov ax,00A0H ;设置首字符显示的地址
mul dh
mov dh,0
add ax,dx
add ax,dx
mov bx,ax ;bx是显示区的偏移地址

mov al,cl ;用al存储属性字节
mov ch,0
mov si,0

s: ;循环读取字符并显示
mov cl,ds:[si]
jcxz ok ;若读到0,就退出循环
mov es:[bx],cl
inc bx
mov es:[bx],al
inc bx
inc si
jmp short s

ok: ;将寄存器的值pop出来
pop bx
pop ax
pop si
pop dx

ret ;返回


code ends
end start




实验12 编写0号中断的处理程序


1.编写 0 号中断的处理程序并将其安装在 0:200 处,使得在除法溢出发生时,屏幕中间自动显示字符串“divide overflow!”,然后返回到 DOS。安装完中断处理程序后,编写针对该中断处理程序的测试程序,并运行测试程序,检查是否正确触发中断处理程序。

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

code segment
start: mov ax,cs
mov ds,ax
mov si,offset do0 ;设置ds:si指向源地址

mov ax,0
mov es,ax
mov di,200h ;设置es:di指向目的地址

mov cx,offset do0end-offset do0 ;cx为传输长度
cld ;传输方向为正
rep movsb ;用movsb指令,将do0的代码送入0:200处

mov ax,0
mov es,ax
mov word ptr es:[0],200h
mov word ptr es:[2],0h ;设置中断向量

mov ax,4c00h
int 21h

do0: jmp short do0start ;EB10 占两个字节,所以下面是202h,不是200h
db "divide overflow!"

do0start: mov ax,cs
mov ds,ax
mov si,202h ;设置ds:si指向字符串

mov ax,0b800h
mov es,ax
mov di,12*160+33*2 ;设置es:di指向显存空间的中间位置

mov cx,16 ;cx为字符串长度

s: mov al,ds:[si]

mov es:[di],al
inc di
mov byte ptr es:[di],2H
inc di

inc si
loop s

mov ax,4c00h
int 21h

do0end:nop


code ends
end start


测试程序:
assume cs:code

code segment

start: mov ax,1000h
mov bx,0
mov bh,1h
div bh



mov ax,4c00h
int 21h


code ends
end start



实验13 编写0号中断的处理程序


编写并安装 int 7ch 中断例程,功能为以“年/月/日 时:分:秒”的格式,在屏幕中间显示当前的日期、时间。中断例程安装在 0:200 处。

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

code segment
start: mov ax,cs
mov ds,ax
mov si,offset a ;设置ds:si指向源地址

mov ax,0
mov es,ax
mov di,200h ;设置es:di指向目的地址

mov cx,offset aend-offset a ;cx为传输长度
cld ;传输方向为正
rep movsb ;用movsb指令,将a的代码送入0:200处

mov ax,0
mov es,ax
mov word ptr es:[7ch*4],200h
mov word ptr es:[7ch*4+2],0 ;设置中断向量

mov ax,4c00h
int 21h


a: jmp short astart
db "9/8/7 4:2:0"

astart: mov ax,cs
mov ds,ax
mov si,202h ;ds:si指向数据段

mov ax,0b800h
mov es,ax
mov bx,160*12+30*2
mov di,0 ;es:di指向显示缓冲区

mov cx,11

s: push cx
mov cl,ds:[si]
cmp cl,48
jb no
cmp cl,57
ja no ;ASCII码值小于48或大于57则转移至no,即不是数字就直接显示

call yes ;是数字就转移到yes处执行
pop cx
loop s

mov ax,4c00h
int 21h

no: mov byte ptr es:[bx+di],cl
add di,2
inc si

pop cx
sub cx,1

jmp far ptr s ;段间转移至s处


yes: sub cl,48 ;字符转换为整数
mov al,cl
out 70h,al
in al,71h

mov ah,al
mov cl,4
shr ah,cl
and al,00001111b

add ah,30h
add al,30h


mov byte ptr es:[bx+di],ah
mov byte ptr es:[bx+di+2],al
add di,4
inc si

ret



aend: nop

code ends
end start


测试程序:
assume cs:code
code segment
start:
int 7ch
mov ax,4c00h
int 21h
code ends
end start

;问题:刚开始没注意到 db 中,即数据段中的数字是 ascii 码。
;解决办法:sub cl,48 ;将字符转换为整数


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


安装一个新的 int 9 中断例程,功能:在 DOS 下,按下“A”键后,如果松开,则显示满屏幕的“A”(字符显示属性为蓝底高亮红色),其他键照常处理。

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

;(有点8懂)

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 ;cx为传输长度
cld ;设置传输方向为正
rep movsb

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

cli ;设置IF=0,不响应可屏蔽中断
mov word ptr es:[9*4],204h
mov word ptr es:[9*4+2],0 ;将新的int 9中断例程安装在0:204处
sti ;设置IF=1,响应可屏蔽中断

mov ax,4c00h
int 21h

int9: push ax
push bx
push cx
push es

in al,60h ;从端口60h读出键盘的输入

pushf
call dword ptr cs:[200h] ;调用BIOS的int 9中断例程

cmp al,9eh
jne int9ret

mov ah,'A'
mov al,1ch
mov bx,0b800h
mov es,bx
mov bx,0
mov cx,2000
s: mov es:[bx],ah
mov es:[bx+1],al
add bx,2
loop s

int9ret:pop es
pop cx
pop bx
pop ax
iret ;前面没call就险先写iret

int9end:nop

code ends
end start


实验16 编写包含多个功能子程序的中断例程


  • 安装一个新的 int 7ch 中断例程,为显示输出提供如下功能子程序。
    • (1)清屏;
    • (2)设置前景色;
    • (3)设置背景色;
    • (4)向上滚动一行。
  • 入口参数说明如下:
    • (1) 用 ah 寄存器传递功能号:0 表示清屏;1 表示设置前景色;2 表示设置背景色;3 表示向上滚动一行;
    • (2) 对于 1、2 号功能,用 al 传递颜色值,(al)∈ {0,1,2,3,4,5,6,7}。
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137

assume cs:code

code segment
start: mov ax,cs
mov ds,ax
mov si,offset int7c ;设置ds:si指向源地址

mov ax,0
mov es,ax
mov di,200h ;设置es:di指向目的地址

mov cx,offset int7cend-offset int7c ;cx为传输长度
cld ;传输方向为正
rep movsb ;用movsb指令,将a的代码送入0:200处

cli
mov ax,0
mov es,ax
mov word ptr es:[07ch*4],200h
mov word ptr es:[07ch*4+2],0 ;设置中断向量
sti

mov ax,4c00h
int 21h

int7c: jmp short setscreen

; 这里要注意,在安装程序中的sub1是一串数据,在中断例程安装在0:200h中时 如果仍然按照第十六章那样设置,是无法成功调用子程序的,
; 因为主程序运行完毕以后,主程序所占的内存可能会被其他内容覆盖,所以我们要把子程序安装在中断例程之后的内存中
; 当然这样会存在一个问题,如果中断例程并没有安装在0:200处,则table中的值也要相应改动
; 至于为什么子程序中的sub1s,sub2s以及loop指令还能正常工作,是因为这是基于位移的跳转
; 即在主程序中,相应的loop/jmp指令已经编译为回退/前进xx字节的指令,因此不会受位置移动的影响

table dw sub1-int7c+200h,sub2-int7c+200h,sub3-int7c+200h,sub4-int7c+200h

setscreen: push bx

cmp ah,3 ; 判断功能号是否大于3
ja sret
mov bl,ah
mov bh,0
add bx,bx ; 根据ah提供的功能号找到对应子程序在table中的偏移

push cs
pop ds
call word ptr [bx+200h+2] ; 调用

sret: pop bx
iret

sub1: push bx ;1号子程序,清屏
push cx
push es
mov bx,0b800h
mov es,bx
mov bx,0
mov cx,2000
sub1s: mov byte ptr es:[bx],' '
add bx,2
loop sub1s
pop es
pop cx
pop bx
ret

sub2: push bx ;2号子程序,设置前景色
push cx
push es
mov bx,0b800h
mov es,bx
mov bx,1
mov cx,2000
sub2s: and byte ptr es:[bx],11111000b
or es:[bx],al
add bx,2
loop sub2s
pop es
pop cx
pop bx
ret

sub3: push bx ;3号子程序,设置背景色
push cx
push es
mov cl,4
shl al,cl
mov bx,0b800h
mov es,bx
mov bx,1
mov cx,2000
sub3s: and byte ptr es:[bx],10001111b
or es:[bx],al
add bx,2
loop sub3s
pop es
pop cx
pop bx
ret

sub4: push cx ;4号子程序,向上滚动一行
push si
push di
push es
push ds

mov si,0b800h
mov es,si
mov ds,si
mov si,160
mov di,0
cld
mov cx,24
sub4s: push cx
mov cx,160
rep movsb
pop cx
loop sub4s

mov cx,80
mov si,0
sub4s1: mov byte ptr [160*24+si],' '
add si,2
loop sub4s1

pop ds
pop es
pop di
pop si
pop cx
ret

int7cend: nop

code ends
end start


汇编语言实验
http://viper2383.github.io/2023/05/27/汇编语言实验/
作者
w1per3
发布于
2023年5月27日
许可协议