《汇编语言(王爽)第三版》实验【未完待续】
实验一:查看CPU和内存,用机器指令和汇编指令编程
(3) 在内存fff00H-fffffH的某几个单元中可以看到rom的生产日期
这里是2017年5月19日
(4)向b810写数据
在这里写入的数据都会显示在屏幕上
检测点3.1
(1)在Debug中,用“d 0:0 1f”查看内存,结果如下
实验二:用机器指令和汇编指令编程
(1)用debug,将下面的程序写入内存,逐条执行,根据指令执行后的实际运行情况填空
实验四:[bx]和loop的使用
1.编程,向内存0:200-0:23F依次传送数据0~63(3FH)。
;P121 第五章实验四 (1)
assume cs:code
code segment
main:
mov ax,0020h
mov ds,ax ;指定段地址0200h
mov bx,0h
mov cx,003fh
mov ax,0h
s:
mov [bx],al
inc bx
inc al
loop s
mov ax,4c00h
int 21h
code ends
end main
2.编程,向内存0:200-0:23F依次传送数据0~63(3FH),只能使用9条指令。
;P121 第五章实验四 (2)
assume cs:code
code segment
main:
mov ax,0020h
mov ds,ax ;指定段地址0200h
mov bx,0h
mov cx,003fh
s:mov [bx],bl
inc bl
loop s
mov ax,4c00h
int 21h
code ends
end main
3.补全下面的程序,其功能是将“mov ax,4c00”之前的指令复制到内存0:200处。
assume cs:code
code segment
main:
mov ax,cs
mov ds,ax
mov ax,0020h
mov es,ax ;es=0020
mov bx,0
mov cx,17h
s: mov al,[bx]
mov es:[bx],al
inc bx
loop s
mov ax,4c00h
int 21h
code ends
end main
PS:指令的段地址是放在CS里面,所以第一个空是“cs”,由图可以看出这些代码一共长15h,但是cx要赋值17h
实验五 编写、调试具体多个段的程序
5)
;默认是一次记录到代码段中的
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
cc segment
db 0,0,0,0,0,0,0,0
cc ends
code segment
start: mov ax,a
mov ds,ax
mov ax,b
mov es,ax
mov bx,0
mov cx,8
s: mov al,ds:[bx]
add es:[bx],al
inc bx
loop s
mov ax,4c00h
int 21h
code ends
end start
6)
assume cs:code
a segment
dw 1,2,3,4,5,6,7,8,0ah,0bh,0ch,0dh,0eh,0fh,0ffh
a ends
b segment
db 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 cx,8
mov bx,0
s: push ds:[bx]
add bx,2
loop s
mov ax,4c00h
int 21h
code ends
end start
实验六:实践课程中的程序
(1)7.1
;程序7.1 P 139
assume cs:code,ds:data
data segment
db 'unIX'
db 'foRK'
data ends
code segment
start: mov al,'a'
mov bl,'b'
mov ax,4c00h
int 21h
code ends
end start
(2)7.4
;大小写转换 P143
; and or 的使用
assume cs:codesg,ds:datasg
datasg segment
db 'BaSiC'
db 'iNforMaTiOn'
datasg ends
codesg segment
start: mov ax,datasg
mov ds,ax
mov bx,0
mov cx,5
;第一个循环将第一段变成大写
s:mov al,[bx]
and al,11011111B
mov [bx],al
inc bx
loop s
;第二个循环将第二段变成小写
mov bx,5
mov cx,11
s0:mov al,[bx]
or al,00100000B
mov [bx],al
inc bx
loop s0
mov ax, 4c00h
int 21h
codesg ends
end start
问题8.1:【图片里的代码没有打完整,等我截完图才发现,懒得改了Orz】
;问题8.1 p 171
; 100001 / 100 = 1000 …… 1
; 186A1H / 64H = 3E8H …… 1
assume cs:code
data segment
dd 100001 ;186A1 64
dw 100 ;ax=86A1 dx=0001 6400 0000
dw 0
data ends
code segment
start: mov ax,data
mov ds,axq
mov ax,ds:[0]
mov dx,ds:[2]
;ax=86A1 dx=0001
div word ptr ds:[4]
mov ds:[6],ax
mov ax,4c00h
int 21h
code ends
end start
实验七:【总算是撸出来了好开心,这里要感谢一位师傅,在自闭的时候给了我冷静】
;实验七
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'
;年份:每一个一个4字节,4*21 【0-53H】
dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,19514
dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
;每一个一个4字节,4*21 【54H-0A7H】
dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
dw 11542,14430,15257,17800
;员工数:每一个两个字节,2*21 【0A8H-H】
data ends
table segment
db 21 dup('year summ ne ?? ')
table ends
codesg segment
start:
mov ax,data
mov ds,ax
mov ax,table
mov es,ax
;定义好了数据段
mov cx,21
mov bx,0
mov si,0
mov di,0
mov ax,0
mov ss,ax
mov sp,2003H
s:
push cx
mov ax,0[si]
mov es:0[bx],ax
mov ax,0[si+2]
mov es:2[bx],ax
;年份赋值:每一次赋一个字,执行两次,完成四个字节的赋值,si来遍历年份
mov ax,54H[si]
mov es:5[bx],ax
mov ax,54H[si+2]
mov es:7[bx],ax
;收入赋值:每一次赋一个字,执行两次,完成四个字节的赋值
mov ax,0A8H[di]
mov es:10[bx],ax
;雇员赋值:每一次赋一个字,执行一次,完成两个字节的赋值
mov ax,54H[si]
mov dx,54H[si+2] ;赋值被除数
mov cx,0A8H[di]
div word ptr cx
pop cx
mov es:13[bx],al
add bx,16 ;用bx控制行
add si,4 ;用si控制年份和收入
add di,2 ;用di控制雇员
loop s
mov ax,4c00H
int 21H
codesg ends
end start
实验八:
八没啥好说的,直接debug一下就可以了,值得一提的是jmp放的是偏移地址,而且 “ -u ” 的时候,要加 “ 0 ”才会显示start前的指令
实验九:显示绿色、红底绿色、白底蓝色的welcome to masm!
- 我先用python脚本写出了“welcome.....”的十六进制表示
u="welcome to masm!"
strs=""
# 绿字
# for i in u:
# strs=strs+str(hex(ord(i)))[2:]+"h,"+"2h"+","
# print(strs)
# print(len(strs.split(",")))
# 绿地红字
# for i in u:
# strs=strs+str(hex(ord(i)))[2:]+"h,"+"24h"+","
# print(strs)
# print(len(strs.split(",")))
# 3.白底蓝字
for i in u:
strs=strs+str(hex(ord(i)))[2:]+"h,"+"0F1h"+","
print(strs)
print(len(strs.split(",")))
- 然后再是写asm
;实验9
assume cs:code
data1 segment
;1.绿底字体
db 77h,2h,65h,2h,6ch,2h,63h,2h,6fh,2h,6dh,2h,65h,2h,20h,2h,74h,2h,6fh,2h,20h,2h,6dh,2h,61h,2h,73h,2h,6dh,2h,21h,2h
data1 ends
data2 segment
;2.绿底红字
db 77h,24h,65h,24h,6ch,24h,63h,24h,6fh,24h,6dh,24h,65h,24h,20h,24h,74h,24h,6fh,24h,20h,24h,6dh,24h,61h,24h,73h,24h,6dh,24h,21h,24h
data2 ends
data3 segment
;3.白底蓝字
db 77h,0F1h,65h,0F1h,6ch,0F1h,63h,0F1h,6fh,0F1h,6dh,0F1h,65h,0F1h,20h,0F1h,74h,0F1h,6fh,0F1h,20h,0F1h,6dh,0F1h,61h,0F1h,73h,0F1h,6dh,0F1h,21h,0F1h
data3 ends
code segment
start:
mov ax,0b800h
mov ds,ax
;1.绿底字体
mov ax,data1
mov es,ax
mov cx,32
mov bx,0
s1:
mov al,es:[bx]
mov ds:[bx+0a12h],al
inc bx
loop s1
;2.绿底红字
mov ax,data2
mov es,ax
mov cx,32
mov bx,0
s2:
mov al,es:[bx]
mov ds:[bx+0a32h],al
inc bx
loop s2
;3.白底蓝字
mov ax,data3
mov es,ax
mov cx,32
mov bx,0
s3:
mov al,es:[bx]
mov ds:[bx+0a52h],al
inc bx
loop s3
mov ax,4c00h
int 21h
code ends
end start
10.10 参数和结果传递的问题
这里我改了一下代码 mov [di].2,dx 的时候居然报错有点迷
;P201
assume cs:code
data segment
dw 1,2,3,4,5,6,7,8
dd 0,0,0,0,0,0,0,0
data ends
code segment
start:
mov ax,data
mov ds,ax
mov si,0
mov di,16
mov cx,8
s:
mov bx,[si]
call cube
mov [di],ax
mov [di+2],dx
add si,2
add di,4
loop s
mov ax,4c00h
int 21h
cube: mov ax,bx
mul bx
mul bx
ret
code ends
end start
10.11 批量数据的传递
assume cs:code
data segment
db 'conversation'
data ends
code segment
start:
mov ax,data
mov ds,ax ;ds赋值
mov si,0
mov cx,12 ;设置循环体
call capital ;调用函数
mov ax,4c00h
int 21h
capital:
and byte ptr [si],11011111b ;将字符转为大写
inc si
loop capital
ret
code ends
end start
实验十(1) 显示字符串
在这次实验中,我们将要编写3个子程序,通过它们来认识几个常见的问题和掌握解决这些问题的方法。同前面的所有实验一样,这个实验是必须要独立完成的,在后面的课程中,将要用到这个实验中编写的3个子程序。
1. 显示字符串
问题:显示字符串是现实工作中经常要用到的功能,应该编写一个通用的子程序来实现这个功能。我们应该提供灵活的调用接口,使调用者可以决定显示的位置(行、列)、内容和颜色。
提示:
(1) 子程序的入口参数是屏幕上的行号和列号,注意在子程序内部要将它们转化为显存中的地址,首先要分析一下屏幕上的行列位置和显存地址的对应关系:
(2) 注意保存子程序中用到的相关寄存器:
(3) 这个子程序的内部处理和显存的结构密切相关,但是向外提供了与显存结构无关的接口。通过调用这个子程序,进行字符串的显示时可以不必了解显存的结构,为编程提供了方便。在实验中,注意体会这种设计思想。
子程序描述
名称:show_str
功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串。
参数:(dh)=行号(取值范围0~24),(dl)=列号(取值范围0~79),
(cl)=颜色,ds:si指向字符串的首地址
返回:无
就用举例:在屏幕的8行3列,用绿色显示data段中的字符串。
这一份是网上的答案,我结合自己的理解添加了注释【感觉师傅写的很谨慎,考虑地很完善】
1 assume cs:code
2 data segment
3 db 'welcome to masm!',0
4 data ends
5
6 code segment
7 start:
8 ;dx存放的是行列信息
9 mov dh,8
10 mov dl,3
11 mov cl,2
12 mov ax,data
13 mov ds,ax
14 mov si,0
15
16 call show_str
17 mov ax,4c00h
18 int 21h
19
20 show_str:
21
22 ;
23 ;入栈储存数据tt
24 ;
25 push dx
26 push cx
27 push si
28
29 ;
30 ;第八行定位【460-4ff】
31 ; 【7*a0=460】
32 mov bl,dh ;行号赋值8
33 dec bl ;行号自减一
34
35 mov al,160 ;A0h【每一行的自增值】
36
37 mul bl ;将bl与al相乘结果放到ax中
38 mov bx,ax ;结果赋值
39 ;
40 ;第三列定位【04-05】
41 ;
42 add dl,dl ;列号自增
43 ;
44 ;行列号拼接形成偏移地址
45 ;
46 add bl,dl
47 ;
48 ;设定显示位置
49 ;
50 mov ax,0b800h
51 mov es,ax
52
53 mov al,cl ;颜色赋值【后面要用cx来结束循环所以这里要把cx的数据先保存起来】
54
55 mov di,0 ;数据初始化
56 s:
57 mov ch,0
58 mov cl,ds:[si]
59 jcxz ok
60 ;将数据赋给cx,当cx为0的时候(到字符串尾)就会自动退出
61
62 mov es:[bx+di],cl
63 mov es:[bx+di+1],al;颜色赋值【颜色设定在高位】
64
65 add di,2 ;es段每次跳两个字节
66 inc si ;ds段每次跳一个
67 loop s
68
69 ok:
70 ;
71 ;数据恢复
72 ;
73 pop dx
74 pop cx
75 pop si
76 ret
77 code ends
78 end start
然后这一份是我自己写的暴力版,当初完全没get到,cl=2 有什么内涵 Orz
1 ;实验10(1)
2 assume cs:code
3 data segment
4 db 'Welcome to masm!',0 ;17
5 data ends
6
7 code segment
8 start:
9 mov dh,8
10 mov dl,3
11 mov cl,2
12 mov ax,data
13 mov ds,ax
14
15 mov si,0
16 call show_str
17
18 mov ax,4c00h
19 int 21h
20
21
22 show_str:
23 mov si,0466h
24 mov cx,17
25
26 mov ax,0b800h
27 mov es,ax
28
29 mov di,0
30
31 s: mov al,ds:[di]
32 mov es:[si],al
33
34 mov ah,2
35 mov es:[si+1],ah
36
37 add si,2
38 add di,1
39 jcxz ok
40 loop s
41
42
43 ok: ret
44 code ends
45 end start
实验十(2) 解决除法溢出
问题
前面讲过,div指令可以做除法。当进行8位除法的时候,用al存储结果的商,ah存储结果的余数:进行16位除法的时候,用ax存储结果的商,dx存储结果的余数。可是,现在有一个问题,如果结果的商大于ah或ax所能存储的最大值,那么将如何?比如,下面的程序段:1 mov bh,1 2 mov ax,1000 3 div bh
进行的是8位除法,结果的商为1000,而1000在ah中放不下,又比如,下面的程序段:
1 mov ax,1000h 2 mov dx,1 3 mov bx,1 4 div bx
进行的是16位除法,结果的商为11000H,而11000H在ax中存放不下。我们在用div指令做除法的时候,很可能发生上面的情况:结果的商过大,超出了寄存器所能存储的范围。当CPU执行div等除法指令的时候。如果发生这样的情况,将引发CPU的一个内部错误。这个错误被称为:除法溢出。我们可以通过特殊的程序来处理这个错误,这里我们不讨论这个错误的处理,这是后面的课程中要涉及的内容。
好了,我们已经清楚了问题的所在:用div指令做除法的时候可能产生除法溢出。由于有这样的问题,在进行除法运算的时候要注意除数和被除数的值,比如1000000/10就不能用div指令来计算。那么怎么办呢?我们用下面的子程序divdw解决。
子程序描述
名称:divdw
功能:进行不会产生溢出的除法运算,被除数为dword型,除数为word型,结果为dword型。
参数:(ax)=dword型数据的低16位
(dx)=dword型数据的高16位
(cx)=除数
返回:(dx)=结果的高16位,(ax)=结果的低16位
(cx)=余数
应用举例:计算1000000/10(F4240H/0AH)
1 mov ax,4240h 2 mov dx,000fh 3 mov cx,0ah 4 call divdw
结果:
(dx)=0001h,(ax)=86a0h,(cx)=0
提示
给出一个公式:
X:被除数,范围:[0,FFFF FFFF]
N:除数,范围:[0,FFFF]
H:X高16位,范围:[0,FFFF]
L:X低16位,范围:[0,FFFF]
int():描述性运算符,取商,比如:rem(38/10)=8
rem():描述性运算符,取答数,比如:rem(38/10)=8
公式:X/N=int(H/N)*65536+[rem(H/N)*65536+L]/N
这个公式将可能产生溢出的除法运算:X/N,转变为多个不会产生溢出的除法运算。公式中,等号右边的所有除法运算都可以用div指令来做,肯定不会导致除法溢出。
这里我写到pop ax就与网上的答案产生分歧了,后来才知道没有好好理解王爽老师提供的公式的含义:
1 进行的是1000000/10
2
3 000f 4240h / 0ah = 1h*10000h+[5*10000h+4240h]/0ah
4 = 10000h+54240h/ah
5 = 10000h+86a0h
6 = 186a0h
7 其中:
8 f / a = 1……5
也就是说高位除的时候,它的商是结果的高位,它的余数是低位除的高位
1 ;实验十(2)
2
3 assume cs:code
4
5 stack segment
6 dw 8 dup(0)
7 stack ends
8
9 code segment
10 start:
11 mov ax,stack
12 mov ss,ax
13 mov sp,16
14
15 mov ax,4240h
16 mov dx,000fh
17 mov cx,0ah
18 ;[ dx ax ] / cx = [dx ax] …… cx
19 call divdw
20
21 mov ax,4c00h
22 int 21h
23
24 divdw: ;[x/n = int()/n*65536+mod()/n] 12/3 = (1*10 + 2)/3 = [int(12/10)*10 + mod(12/10)]/3 =int(12/10)*10/3 + mod(12/10)/3= 1 + 3
25 ;将高位与除数做运算
26 push ax ;将L存放起来
27
28 mov ax,dx ;将ax转化为H
29 mov dx,0 ;将余数位清零
30 div cx ;int(H/N)=ax,rem(H/N)=dx
31
32 mov bx,ax ;将int(H/N)的值存起来[这是已经得到的高位]
33
34 ;将低位与除数做运算
35 pop ax ;将L赋值给ax,rem(H/N)=dx
36 div cx ;
37
38 mov cx,dx ;cx放余数
39 mov dx,bx ;dx放商的高位,ax默认低位
40
41 ret
42 code ends
43 end start
实验十(3) 解决除法溢出
问题
编程,将data段中的数据以十进制的形式显示出来。1 data segment 2 dw 123,12666,1,8,3,38 3 data ends
这些数据在内存中都是二进制信息,标记了数值的大小。要把它们显示到屏幕上,成为我们能够读懂的信息,需要进行信息的转化。比如,数值12666,在机器中存储为二进制信息:0011000101111010B(317AH),计算机可以理解它。而我们要在显示器上读到可以理解的数值12666,我们看到的应该是一串字符:“12666”。由于 显卡遵循的是ASCII编码,为了让我们能在显示器上看到这串字符,它在机器中应以ASCII码的形式存储为:31H、32H、36H、36H、36H(字符“0”~“9”对应的ASCII码为30H~39H)。
通过上面的分析可以看到,在概念世界中,有一个抽象的数据12666,它表示了一个数值的大小。在现实世界中它可以有多种表示形式,可以在电子机器中以高低电平(二进制)的形式存储,也可以在纸上、黑板上、屏幕上以人类的语言“12666”来书写。现在,我们面临的问题就是,要将同一抽象的数据,从一种表示形式转化为另一种表示形式。
可见,要将数据用十进制形式显示到屏幕上,要进行两步工作:
(1) 将用二进制信息存储的数据转变为十进制形式的字符串:
(2) 显示十进制形式的字符串。
第二步我们在本次实验的第一个子程序中已经实现,在这里只要调用一下show_str即可。我们来讨论第一步,因为将二进制信息转变为十进制形式的字符串也是经常要用到的功能,我们应该为它编写一个通用的子程序。
子程序描述
名称:dtoc
功能:将word型数据转变为表示十进制数的字符串,字符串以0为结尾符。
参数:(ax)=word型数据
ds:si指向字符串的首地址
返回:无
应用举例:编程,将数据12666以十进制的形式在屏幕的8行3列,用绿色显示出来。在显示时我们调用本次实验中的第一个子程序show-str。
提示
下面我们对这个问题进行一下简单地分析。
(1) 要得到字符串“12666”,就是要得到一列表示该字符的ASCII码:31H、32H、36H、36H、36H。十进制数码字符对应的ASCII码=十进制数码值+30H要得到表示十进制数的字符串,先求十进制数每位的值。例:对于12666,先求得每位的值:1、2、6、6、6。再将这些数分别加上30H,便得到了表示12666的ASCII码串:31H、32H、36H、36H、36H。
(2) 那么,怎样得到每位的值呢?采用如图10.2所示的方法。
1 dtoc:
2 push ax
3 push bx
4 push si
5 mov bx,10
6 mov si,0
7
8 s1:
9 ;取出每一位并且将它转化为字符放到栈段里面
10 mov dx,0
11 div bx
12 add dx,30h
13 push dx
14
15 inc si ;记录一共push几个数字
16 mov cx,ax
17 Inc cx
18 loop s1
19
20 mov cx,si
21 mov si,0
22 s2:
23 pop ds:[si]
24 add si,1
25 loop s2
26
27 pop si
28 pop bx
29 pop ax
30 ret
后记:
一开始是打算这样写的,但是出现了报错,错误点是第17行。实际上当ax变成0的时候 loop,cx 默认自减一,然后cx就变成负的了,没有达到当ax变成零自动退出的目的,而且也没有三个push来保存数据,还是保有着C/C++当中的局部变量的思想Orz。这里数据恢复很重要啊
1 dtoc:
2 mov bx,10
3 mov si,0
4
5 s1:
6 ;取出每一位并且将它转化为字符放到栈段里面
7 mov dx,0
8 div bx
9 add dx,30h
10 push dx
11
12 inc si ;记录一共push几个数字
13 mov cx,ax
14 loop s1
15 push ax
16
17 mov cx,si
18 mov si,0
19 s2:
20 pop ds:[si]
21 add si,1
22 loop s2
23 ret
实验十一:
;实验十一
assume cs:codeseg
dataseg segment
db "Beginner's All-purpose Symboliv Instruction Code.",0
dataseg ends
codeseg segment
begin:
mov ax,dataseg
mov ds,ax ;数据段初始化
mov si,0 ;data:[0]
call letters
mov ax,4c00h
int 21h
letters:
s: mov cl,ds:[si] ;ds:[si] => 将data段的每一位赋值给cl,那么当读到最后一位的0就会退出循环
jcxz ok ;当cx为0的时候跳转到ok
mov bl,97 ;97 ascii 是一个“a”
cmp cl,bl ;执行cl-bl判断大小影响 cl > bl ==> cf,zf
jnb ba ;大于等于就转移到ba,检测cf是否等于0(NC)
inc si
loop s
ba: ;大于等于a跳到这
mov bl,122
cmp cl,bl
jna lz ;小于等于就转移检测cf/zf是否等于1(ZR)
inc si ;不然就找下一个
jmp letters
lz: ;小于等于z跳到这( a<= x <=z )
and cl,11011111b
mov ds:[si],cl
inc si
jmp letters
ok:
ret
codeseg ends
end begin
实验十二:
mov ax, cs
mov ds, ax
mov si, offset do0
设定ds段,用C语言来描述就是相当于有一个data段的指针,将其指向code段(asm代码中的第29行)
mov ax, 0
mov es, ax
mov di, 200h
设定es段,用C语言来描述就是相当于有一个指针,将其指向内存中的0:[200]
mov cx, offset do0end - offset do0
cld
rep movsb
复习一下:
MOVSB:字符串传送指令,这条指令按字节传送数据。通过SI和DI这两个寄存器控制字符串的源地址和目标地址,比如DS:SI这段地址的N个字节复制到ES:DI指向的地址,复制后DS:SI的内容保持不变。
REP:指令就是“重复”的意思,术语叫做“重复前缀指令”,因为既然是传递字符串,则不可能一个字(节)一个字(节)地传送,所以需要有一个寄存器来控制串长度。这个寄存器就是CX,指令每次执行前都会判断CX的值是否为0(为0结束重复,不为0,CX的值减1),以此来设定重复执行的次数。因此设置好CX的值之后就可以用REP MOVSB了。
CLD:则是清方向标志位,也就是使DF的值为0,在执行串操作时,使地址按递增的方式变化,这样便于调整相关段的的当前指针。这条指令与STD的执行结果相反,即置DF的值为1。
所以说这三条的意思是将ds的内容赋值到es中。
mov ax, cs
mov ds, ax
mov si, 202h
mov ax, 0b800h
mov es, ax
mov di, 12*160+36*2
这一段是设置显示结果的初始化(指定ds,es段),其中在 “mov si,202h”中为什么设定si是202h ?因为rep movsb之后0:200段已经放的是cs中的代码,所以可以直接用
mov cx,9
mov bl,2
s:mov al,ds:[si]
mov es:[di],al
mov es:[di+1],bl
inc si
add di,2
loop s
这一段是把ds段中的db内容赋值到显存区,其中“mov es:[di+1],bl”设定颜色是绿的
mov ax, 0
mov es, ax
mov word ptr es:[0], 200h
mov word ptr es:[2], 0
设置中断向量将入口地址0:200写入中断向量表0号表项中,0号表项的地址是0:0
mov ax, 1000
mov bl, 1
div bl
一段除法溢出,测试写的对不对
assume cs:codesg
codesg segment
start:
mov ax, cs
mov ds, ax
mov si, offset do0
mov ax, 0
mov es, ax
mov di, 200h
mov cx, offset do0end - offset do0
cld
rep movsb
mov ax, 0
mov es, ax
mov word ptr es:[0], 200h
mov word ptr es:[2], 0
mov ax, 1000
mov bl, 1
div bl
mov ax, 4c00h
int 21h
do0:
jmp short do0start
db "overflow!"
do0start:
mov ax, cs
mov ds, ax
mov si, 202h
mov ax, 0b800h
mov es, ax
mov di, 12*160+36*2
mov cx,9
mov bl,2
s:mov al,ds:[si]
mov es:[di],al
mov es:[di+1],bl
inc si
add di,2
loop s
mov ax,4c00h
int 21h
do0end:
nop
codesg ends
end start
十三章案例:
问题一:编写安装中断7ch的中断案例
功能:求一word型数据的平方
参数:(ax)=要计算的数据
返回值:dx,ax中存放结果的高16位和低16位
应用举例:求2*3456^2
assume cs:code
code segment
start:
mov ax,3456
jmp install
finish:
int 7ch
add ax,ax
adc dx,dx
;上面两条进行的是加法,dx存放高位,ax存放低位
mov ax,4c00h
int 21h
install:
push ax
push cx
push ds
push si
push es
push di
mov ax,cs
mov ds,ax
mov si,offset sqr
;将代码段的数据给data段
mov ax,0
mov es,ax
mov di,200h
mov cx,offset sqrend-offset sqr
cld
rep movsb
mov ax,0
mov es,ax
mov word ptr es:[7ch*4],200h
mov word ptr es:[7ch*4+2],0
pop di
pop es
pop si
pop ds
pop cx
pop ax
jmp finish
sqr:
mul ax
iret
sqrend:
nop
code ends
end start
达到的目的:
; assume cs:codeseg ; codeseg segment ; start:mov ax,0b800h ; mov es,ax ; mov byte ptr es:[12*160+42*2],'!' ; int 0 ; mov ax,4c00h ; int 21 ; codeseg ends ; end start assume cs:code data segment db 'conversation',0 data ends code segment start: mov ax,data mov ds,ax mov si,0 jmp install finish: int 7ch ; mov ax, 0b800h ; mov es, ax ; mov di, 12*160+36*2 ; mov bl,2 ; s:mov ch,0 ; mov cl,ds:[si] ; mov es:[di],al ; mov es:[di+1],bl ; inc si ; add di,2 ; loop s ; ;设置屏幕显示 mov ax,4c00h int 21h install: push ax push cx push ds push si push es push dx s_start: mov ax,cs mov ds,ax mov si,offset capital mov ax,0 mov es,ax mov di,200h mov cx,offset capitalend - offset capital cld rep movsb ;上面完成的是赋值 mov ax,0 mov es,ax mov word ptr es:[7ch*4],200h mov word ptr es:[7ch*4],0 ;将地址写入 pop dx pop es pop si pop ds pop cx pop ax jmp finish capital: ;小写转大写的功能函数 push dx push cx push si mov ax,data mov dx,ax mov si,0 change : mov cl,ds:[si] mov ch,0 jcxz ok and byte ptr [si],11011111b inc si jmp short change ok: pop si pop cx pop dx iret capitalend:nop code ends end start