一二三章实验环境配置见:https://blog.csdn.net/plus_re/article/details/60761467
第四章加入了deit等指令,需要添加一些exe文件:https://blog.csdn.net/Ghost_jzy/article/details/104572876
我有时进入dos,输入数字有异常,多按几次数字键左上角的numlock就好
整本书都放一起了,有点乱,鼠标放在标题,右侧会显示目录导航~
除了两个课设和研究实验没写,其他都是一个个手打出来的,印象中只有实验十七没有上机实践,其他都是没问题的~
这本书我是学完计组,学操作系统之前看的。现在看来这本书有助于我理解操作系统,巩固以前学过的计组
可以配合《汇编语言笔记》看
第二章
实验一
任务一:
使用debug,将下面的程序段写入内存,逐条执行,观察每条指令执行后CPU中相关寄存器中内容的变化
1.进入debug模式,先查看寄存器的初始值和内存单元的初始情况
2.使用e指令将程序段存入内存中(使用A指令用汇编语言存入内存也行,使用e指令是机器码存)
3.至此,程序已经载入内存,接下来用r指令修改IP和CS
4.运行程序之前,将内存的机器码转化为汇编指令,检查程序是否输入正确
5.没问题,用t指令运行程序,以下是结果
任务二:
将下面3条指令写入从2000:0开始的内存单元中,利用这三条指令计算2的8次方
1.先改IP和CS,再存程序(不知道机器码,只能使用a指令,用汇编指令去存到内存
2.不出意外,按14次T(乱算的,我没数去验证,截图省略了若干次按T的过程..)ax就是结果
任务三:
修改ROM的生产日期
1.用d指令,查看一定范围的内存单元(段地址就是物理地址右移一位,右边少个0,所以是fff0)
2.看了看网上答案,右下角的01/01/92似乎就是要找的日期(网上的ROM跟我的ROM刚好同年同月同日生吗?太巧了吧?!)
3.试图修改
4.屁用没有!意料之中~因为我在步骤2看别人答案时,不小心看到他修改失败了..
5.其实因为题目也说了是ROM,所以修改失败,RAM才能改
任务四:
向内存从B8100H开始的单元填写数据
1.根据题目照做
2.换个数据会发现右上角的图案会变(忘记截图了),我以为ROM数据被改了出现乱码还是啥,再次查看ROM时发现,没有被修改
3.原来那地方是显存地址,有点意思
第三章
检测点3.1
1.在debug中,用“d 0:0 1f”查看内存,结果如下..
这题注意ax的起始位置就好,我是搞错了好几次。ax段地址是1,乘16之后就是10,ax指向62
2.内存中情况如图3.6所示。各寄存器的初始值:CS=2000H,IP=01,DS=1000H,AX=0,BX=0;
(1.写出CPU执行指令的指令序列
(2.写出CPU执行每条指令后,CS,IP和相关寄存器的值
(3.再次体会:数据和程序有区别吗?如何确定内存中信息哪些是数据,哪些是程序?
执行次数 | CS | IP | DS | AX | BX |
---|---|---|---|---|---|
1 | 2000h | 3h | 0 | 6622h | 0 |
2 | ff0h | 100h | 0 | 6622h | 0 |
3 | ff0h | 103h | 0 | 2000h | 0 |
4 | ff0h | 105h | 2000h | 2000h | 0 |
5 | ff0h | 108h | 2000h | c389h | 0 |
6 | ff0h | 10bh | 2000h | ea66h | 0 |
指令和数据都没区别。通过CS和IP访存,CPU当作指令,通过DS访存,CPU当作数据?
检测点3.2
道理我都懂,就是没想出来
实验二
任务一:
执行下面的程序填写实际结果
不知道这题意义是啥,程序起始位置随便放在1000:0了
任务二:
分析为什么2000:0~2000:f的内容会变
我没有悟性,不晓得
第四章 第一个程序
实验三
任务一
将下面的程序保存为t1.ASM文件,并生成可执行文件t1.exe
没啥好讲的,先生成obj目标文件,再生成exe文件
任务二
用debug跟踪t1.exe的执行过程,写出每一步执行后,相关寄存器和栈顶的内容。
见下图(执行INT指令的时候要用P命令,我用成T了,所以没能正常返回,但是过程是对的):
任务三
PSP的头两个字节是CD 20,用Debug加载t1.exe,查看PSP的内容(PSP是程序前缀的数据区)
debug的时候,DS寄存器存的是075A,我们去这个地址看,确实开头是CD20
第五章
实验四
任务一
编程,向内存0:200~0:23F依次传送数据0~63(3FH)
任务一二一起完成
任务二
编程,向内存0:200~0:23F依次传送数据0~63(3FH),程序中只能使用9条指令,9条指令包括“mov ax,4c00H”和“int 21h”
目标内存区域跨度为64,刚好对应64个要输入的数据,那么可以共用一个变量
1.dos里面运行edit,编写九行代码
2.编译并连接
3.进入debug跟踪,先看看0:200有没有数据,确实没有
4.再看看代码是否正确存入
5.运行后,查看0:200
任务三
补全下列程序。上机调试,跟踪运行结果
md写错了,其实还是有疑问的。
- 第一个空对了,cs代表着程序起始位置。第四章提到:程序加载后,DS寄存器存着内存区的段地址,此时指向PSP部分,后面的256字节才开始存程序,那么这一空能否写成“ds+10H”?
- 第二个空,正确做法:将程序编译连接,debug的时候会发现mov ax,4c00h前面所占内存为17H,所以答案为17H或者23(下图漏写H
第六章 包含多个段的程序
检测点6.1
- 答案:mov cs:[bx],cx
- 答案:cs,24h,pop cs:[bx]
- 第二题:cs不用说。因为dw定义数据,因此最后一个有用数据的偏移地址为0E~0F(有用数据指的是dw出来是为了当数据, 不是为了单纯开辟空间),随后开辟了20字节,F+20(十进制)=23H,栈空的时候指向23H后面一个地址,即24H
实验五 编写,调试具有多个段的程序
1.跟踪程序,回答问题
答案:data值不变,cs=076c,ss=076B,ds=075A,data段地址为X-1,stack段地址为X-2
第二三四空的答案(不同电脑可能答案不同)也印证了第五六空的结论,data和stack各占16字节,所以X减去16(十进制)和32(十进制)即可,由于段地址最后一位权值为16,因此分别为-1和-2
2.跟踪程序,回答问题
答案:data值不变,cs=076c,ss=076B,ds=076A。data段地址为x-2,stack段地址为x-1。【N/16】+1,【】表示取整
这里虽然程序通过dw定义的stack和data空间都是4字节的,但是因为段地址起始位置一定是X:0的形式,换句话说,组成段空间的最小单位是16字节,所以跟上一题答案相同。至于为什么,似乎和数据对齐有关。
3.跟踪程序,回答问题
答案:data不变。cs=076A,ss=076E,ds=076D,data段地址为x+3,stack段地址为x+4
4.将123题的end start改为end,哪个程序还能正常运行?
应该是只有第三个,前两个CS:IP指向的都是data段的东西。第三个刚好把code段放在了最前面,执行到后面正常返回。
5.编写code段的代码
这题需要注意定义数据的时候用的是db,一个数据一字节,以下是答案
code segment
start:
mov bx,0
mov cx,8
s:
mov ax,a
mov ds,ax
mov dx,0
mov dl,ds:[bx]
mov ax,b
mov ds,ax
add dl,ds:[bx]
mov ax,c
mov ds,ax
mov ds:[bx],dl
inc bx
loop s
mov ax,4c00h
int 21h
code ends
在075D:00D0处可以看到我们存的数据
程序运行后再次查看,发现075D:00F0存储着ab之和
6.编写code段的代码
这题是dw,一个数据两字节。要逆序存储栈空间,直接正序push就好了~
code segment
start:
mov ax,b
mov ss,ax
mov sp,10h
mov ax,a
mov ds,ax
mov ax,b
mov es,ax
mov cx,8
mov bx,0
s: push ds:[bx]
inc bx
inc bx
loop s
mov ax,4c00h
int 21h
code ends
下图是运行程序前,栈为空
下图是运行程序后,栈存储着逆序数据
第七章 更灵活定位内存地址的方法
实验六
1.将第七章所有程序上机调试,debug跟踪。这题就不说了
2.编程,完成问题7.9中的程序
答案如下:
codesg segment
start:mov ax,stacksg
mov ss,ax
mov sp,10H
mov ax,datasg
mov ds,ax
mov bx,0
mov cx,4
s0:push cx
mov si,0
mov cx,4
s:mov al,[bx+3+si]
and al,11011111b
mov [bx+3+si],al
inc si
loop s
add bx,16
pop cx
loop s0
mov ax,4c00H
int 21H
codesg ends
运行前先查看字符串初始值
运行后可以看到字符串前四个字母都是大写
第八章
实验七
将存年份的,存收入的,存雇员数的分别看作三个数组,将table看作一个二维数组一次性写完比较难调试,感觉寄存器不够用,都晕了,所以我是一个功能一个功能写的,最后合并就好
功能一:复制年份到table里每个数组的前4位
codesg segment
start:
mov bx,0;bx指向table不同的数组
mov ax,table
mov ds,ax
mov ax,data
mov es,ax
mov bp,0
mov cx,21
s0:
mov si,0;数组里的下标
mov al,es:[0+bp]
mov [bx+si],al
inc si
inc bp
mov al,es:[0+bp]
mov [bx+si],al
inc si
inc bp
mov al,es:[0+bp]
mov [bx+si],al
inc si
inc bp
mov al,es:[0+bp]
mov [bx+si],al
inc bp
add bx,10H
loop s0
mov ax,4c00h
int 21h
codesg ends
程序运行前查看内存
程序运行后查看内存,可知功能实现
功能二:复制收入到table里每个数组的5-8位
codesg segment
start:
mov bx,0;bx指向table不同的数组
mov ax,table
mov ds,ax
mov ax,data
mov es,ax
mov bp,0
mov cx,21
s1:
mov si,5
mov ax,es:[84+bp]
mov [bx+si],ax
add si,2
add bp,2
mov ax,es:[84+bp]
mov [bx+si],ax
add bp,2
add bx,10H
loop s1
mov ax,4c00h
int 21h
codesg ends
程序运行前
程序运行后,可知功能实现
功能三:复制雇员数到table里的每个数组的A-B位
codesg segment
start:
mov bx,0;bx指向table不同的数组
mov ax,table
mov ds,ax
mov ax,data
mov es,ax
mov bp,0
mov cx,21
s1:
mov si,0AH
mov ax,es:[168+bp]
mov [bx+si],ax
add bp,2
add bx,10H
loop s1
mov ax,4c00h
int 21h
codesg ends
程序运行前
程序运行后,可知功能实现
完整版答案:合并三个功能,并算出人均收入
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'
dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,19754
dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
dw 3,7,9,13,28,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
dw 11542,14430,15257,17800
data ends
table segment
db 21 dup ('year summ ne ?? ')
table ends
codesg segment
start:
mov bx,0;bx指向table不同的数组
mov ax,table
mov ds,ax
mov ax,data
mov es,ax
mov bp,0
mov cx,21
s3:;开始复制雇员数
mov si,0AH;雇员数要复制到table数组下标A-B,数据不能以字母开头,需要加0
mov ax,es:[168+bp];168表示雇员数数组的偏移地址
mov [bx+si],ax
add bp,2
add bx,10H
loop s3
mov bx,0
mov bp,0
mov cx,21
s2:;开始复制收入
mov si,5;收入要复制到table数组下标5-8
mov ax,es:[84+bp];84表示收入数组的偏移地址
mov [bx+si],ax
add si,2
add bp,2
mov ax,es:[84+bp]
mov [bx+si],ax
add bp,2
add bx,10H
loop s2
mov bp,0
mov bx,0
mov cx,21
s1:;开始复制年份
mov si,0;年份要复制到数组下标0-3
mov al,es:[0+bp];0表示年份数组的偏移地址
mov [bx+si],al
inc si
inc bp
mov al,es:[0+bp]
mov [bx+si],al
inc si
inc bp
mov al,es:[0+bp]
mov [bx+si],al
inc si
inc bp
mov al,es:[0+bp]
mov [bx+si],al
inc bp
add bx,10H
loop s1
mov cx,21
mov bp,0
mov bx,0
mov di,0
s4:;开始计算人均收入
mov si,0DH
mov ax,es:[84+bp];被除数低字节存在ax
add bp,2
mov dx,es:[84+bp];被除数高字节存在dx
add bp,2
div word ptr es:[168+di];做除法,指出操作的数据大小为两字节
add di,2
mov [bx+si],ax
add bx,10H
loop s4
mov ax,4c00h
int 21h
codesg ends
end start
程序运行前
程序运行后,看内存地址076A:00D0。5-8位是10 00 00 00是十进制16,A-B位是03 00是十进制3,D-E位是5,16/3取整就是5,答案正确!
第九章 转移指令的原理
检测点9.1
1.定义data段数据,使得CS:IP运行jmp后指向第一条指令
data segment
db 16 dup(0)
data ends
其实只要data:1和data:2存的是0就好了
2.补全程序,使得CS:IP运行jmp后指向第一条指令
mov [bx],bx;我本来写的是mov [bx],0
mov [bx+2],cs;我本来写的是mov [bx+2],offset start
因为我写的两个move指令没有指出操作的是字还是字节,所以还是上面的答案合适
3.CPU执行后,CS=?,IP=?
CS=0006,IP=00BE
检测点9.2
1.补全程序
s: mov cl,ds:[bx]
mov ch,0
jcxz ok
inc bx
jmp short s
检测点9.3
1.补全程序
s: mov cl,[bx]
mov ch,0
inc cx;这题确实没做出来,因为loop判断时先将cx--,因此要在cx减1之前加上1
inc bx
loop s
实验8 分析一个奇怪的程序
没想到居然能运行。。s段作用就是把s2的"jmp short s1"复制到s开头的两个nop。于是程序执行完s段,跳转至s开头,又进行跳转,但是复制的跳转指令机器码存的是IP的位移,存的是-8字节,因此歪打正着,往前八字节跳到mov ax,ac00h那两行返回指令去了
实验9 根据材料编程
有两个需要注意的地方:这里是在显存上修改内容,不需要太关注每时每刻地址上的内容,因为只要你屏幕显示变了,显存的内容也会变的。此外,不要从显存的开头B800:0000开始写数据!(我从0开始写,一直没找到哪里有变化,也许是dos框框太小了?
assume cs:codesg,ds:datasg
datasg segment
db 'welcome to masm!'
db 02h,24h,71h
datasg ends
codesg segment
start:mov ax,datasg
mov ds,ax
mov ax,0B860h
mov es,ax
mov cx,3
mov bp,0
s:;一个s循环显示一串字符,三行同一位置依次显示
mov si,0
mov di,0
push cx
mov ah,[10h+bp]
inc bp
mov cx,16
s1:;一个s1循环显示一个字符
mov al,ds:[si]
inc si
mov es:[di],ax
add di,2
loop s1
pop cx
loop s
mov ax,4c00h
int 21h
codesg ends
end start
;网上也有只写了一个执行16次循环的,一次循环显示一列字符,三个字符串在不同行同时显示
执行结果
第十章 CALL和 RET指令
检测点10.1
mov ax,1000H,mov ax,0000H;要注意先是pop IP,再pop cs
检测点10.2
ax=6;当一条指令读完之后,就指向下一条指令了,当执行call时,IP指向inc ax,于是将6压入栈,最后将6出栈给ax
检测点10.3
1010H;pop ax使得ax为8,add之后ax为16(十进制),bx为1000,于是相加为1010H
检测点10.4
BH
检测点10.5
第一题:ax=3;
第二题
实验十
1.显示字符串
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:;要用到的寄存器有:ax和bx放乘数,bx存行下标,di存列下标,si存字符串下标,cx判断是否为0
push ax
push es
push bx
push di
push si
mov ax,0B800H
mov es,ax
mov al,dh;准备乘法
mov bl,160
mul bl
mov bx,ax;行标放在了bx
dec dl;准备乘法
mov al,dl
push bx
mov bl,2
mul bl
mov di,ax;列标放在了di
pop bx
;bx,di行标列标已就绪
again:push cx
mov cl,ds:[si]
mov ch,0
jcxz ok;是0就退出程序,否则继续执行
pop cx
mov al,ds:[si]
mov byte ptr es:[bx+di],al
inc si
inc di
mov byte ptr es:[bx+di],cl
inc di
jmp near ptr again
ok:
pop si
pop di
pop bx
pop es
pop ax
ret
code ends
end start
2.解决除法溢出问题
md,这题我也是麻了。分析一下公式:两个大数相除的结果可以用两个小数的除法得出。商=int(N/H)65536+[rem(H/N)65536+L]/N的商,余数=[rem(H/N)*65536+L]的余数,其中与65536相乘可以用左移16位,后面补零代替
assume cs:code
data segment
db 16 dup(0)
data ends
code segment
start:
mov ax,4240h;被除数低16位
mov dx,000Fh;被除数高16位
mov cx,0Ah;除数
mov bx,data
mov ds,bx
mov bx,data
mov ss,bx
mov sp,16
call divdw
mov ax,4c00h
int 21h
divdw:push dx
push ax
mov ax,dx;ax放被除数的高位
mov dx,0;dx放0
div cx;商在ax,余数在dx
push dx
;接着商*65536,就是左移16位,后面补零,此时也就是ax000...00,0就我不存了,存ax就好
;此时栈内:000fh,4240h,余数
pop bx;取出余数
pop di;取出被除数低16位4240h
push ax;此时栈内:000fh,商
mov dx,bx;
mov ax,di
div cx;计算(rem(H/N)*65536+L]/N,商在ax,余数在dx
;根据公式,此时的ax和前面的商*65536相加,商*65536已经在栈中
;开始返回结果
mov cx,dx
pop dx
;对了,函数结束之前还要清空栈
pop bx
ret
code ends
end start
运行结果和答案一致
3.数值显示
show_str几乎直接复制过来就好
assume cs:code
data segment
dw 123,12666,1,8,3,38
data ends
out segment
db 32 dup(0);dtoc函数就是把data的数字以字符的形式,写到out这段内存中
out ends
stack segment
db 160 dup(0);因为这种方法求出来的余数是倒序的(比如123经过一系列除法,得出的余数依次是32),所以需要用栈,pop出来写入out内存就是正序。
stack ends
code segment
start:
mov bx,data
mov es,bx;es指向data
mov bx,out
mov ds,bx;ds指向out
mov bx,stack
mov ss,bx
mov sp,160;ss:sp指向栈空间
;参数初始化
mov ax,es:[0]
mov si,0
call dtoc
mov ax,es:[2]
call dtoc
mov ax,es:[4]
call dtoc
mov ax,es:[6]
call dtoc
mov ax,es:[8]
call dtoc
mov ax,es:[10]
call dtoc
mov dh,8
mov dl,3
mov cl,2
mov si,0
call show_str
mov ax,4c00h
int 21h
dtoc:push bx
mov bx,0;事先放个0,出栈的时候好知道这时候已结束
push bx
mov bx,10;bx放除数
s:mov dx,0
div bx;dx余数,ax商
push dx
mov ax,ax
mov cx,ax
jcxz s1;如果商为,说明求余数结束,开始出栈写内存
jmp short s
s1:pop ax
mov cx,ax
jcxz ok
add ax,30h
mov ds:[si],ax
inc si
jmp short s1
ok:pop bx
ret
show_str:;要用到的寄存器有:ax和bx放乘数,bx存行下标,di存列下标,si存字符串下标,cx判断是否为0
push ax
push es
push bx
push di
push si
mov ax,0B800H
mov es,ax
mov al,dh;准备乘法
mov bl,160
mul bl
mov bx,ax;行标放在了bx
dec dl;准备乘法
mov al,dl
push bx
mov bl,2
mul bl
mov di,ax;列标放在了di
pop bx
;bx,di行标列标已就绪
again:push cx
mov cl,ds:[si]
mov ch,0
jcxz ok2;是0就退出程序,否则继续执行
pop cx
mov al,ds:[si]
mov byte ptr es:[bx+di],al
inc si
inc di
mov byte ptr es:[bx+di],cl
inc di
jmp near ptr again
ok2:pop si
pop di
pop bx
pop es
pop ax
ret
code ends
end start
以下是程序运行结果,没错我偷懒了,输出字符串没分开,主要是题目也没说输出最终结果该是咋样的
课程设计1
搞了一下午我不想写了.....借口:反正核心的东西前面都实现了
第十一章 标志寄存器
检测点11.1
注意push,mov之类的转移指令不改变flag就行
sub al,al 110
mov al,l 110
push ax 110
pop bx 110
add al,bl 000
add al,10 010
mul al 010
检测点11.2
sub al,al 00011
mov al,10h 00011
add al,90h 00101
mov al,80h 00101
add al,80h 11011
mov al,0FCh 11011
add al,05h 10000
mov al,7Dh 10000
add al,0Bh 01101
检测点11.3
(1
cmp al,32
jb s0
cmp al,128
ja s0
(2
cmp al,32
jna s0
cmp al,128
jnb s0
检测点11.4
- 执行到popf时,ax赋给flag标志寄存器,此时flag全零
- add执行后,zf=1,pf=1,cf=1,其余为零
- FFF0
0010
———————
1,0000
- FFF0
- pop ax后,ah为0000 0000 ,al为0100 0101
- 经过两次and后,ax=0045H
实验11编写子程序
将大写转化为小写,ascii与11011111b进行与运算即可
assume cs:code
datasg segment
db "Beginner's All-purpose Symbolic Instruction Code.",0
datasg ends
code segment
start:
mov ax,datasg
mov ds,ax
mov si,0
call letterc
mov ax,4c00h
int 21h
letterc:;如果ascii≥97且≤122,就执行大写转化
cmp byte ptr ds:[si],0
je ok
cmp byte ptr ds:[si],97
jb next
cmp byte ptr ds:[si],122
ja next
transform:
and byte ptr [si],11011111b
next:inc si
jmp letterc
ok:ret
code ends
end start
实验结果
第十二章 中断
检测点12.1
注意:中断源是从0开始计数的,低位存的是偏移地址
1.
0号:00A7:1068
1号:0070:108B
2号:039D:0016
3号:0070:108B
2. 4n,4n+2
实验十二 编写0号中断
assume cs:code
code segment
start:
;编写程序
;复制程序到0000:0200处
;修改向量表
mov si,offset do0
mov ax,cs
mov ds,ax
mov di,0200h
mov ax,0
mov es,ax
mov cx,offset do0end-offset do0
cld
rep movsb
mov word ptr es:[0*4],200h
mov word ptr es:[0*4+2],0
mov ax,1000h
mov bh,1
div bh
mov ax,4c00h
int 21h
do0:jmp short do
db "divide error!";13字节
do:mov ax,0B800h
mov es,ax
mov si,160*12
mov cx,13
mov ax,cs
mov ds,ax
mov di,202h
s:mov al,ds:[di]
mov es:[si],al
inc di
inc si
inc si
loop s
mov ax,4c00h
int 21h
do0end:nop
code ends
end start
实验结果,可以看到确实输出了那行字~
第十三章 int指令
检测点13.1
1.我写的是65535.但是loop是短转移指令,所以最大转移是128(-128到127嘛,取最大绝对值)
2.程序装载和修改向量表就没写了,只写了中断程序
push bp
mov bp,sp
dec cx
jcxz ok
add ss:[bp+2],bx;书上并没有显式指出段寄存器,我觉得还蛮奇怪的
ok:pop bp
iret
检测点13.2
1.错误,网上说:FFFF:0 对应的指令只要在重新启动的时候,就被重置为BIOS中的硬件系统检测和初始化程序的指令[https://www.jianshu.com/p/4aed29e3bfc1]() 我也不懂!
2.错误,中断例程19h是引导操作系统,dos操作系统不能自己引导自己
实验十三 编写,应用中断例程
1.编写int 7ch中断例程,功能为显示一个用0结束的字符串
assume cs:code
data segment
db "welcome to masm!",0
data ends
code segment
start:;安装例程到0:200
mov ax,0
mov es,ax
mov di,200h
mov ax,cs
mov ds,ax
mov si,offset do0
mov cx,offset do0end-offset do0
cld
rep movsb
;更改向量表
mov ax,0
mov es,ax
mov word ptr es:[4*7ch],200h
mov word ptr es:[4*7ch+2],0
mov dh,10;行号
mov dl,10;列号
mov cl,2;颜色
mov ax,data
mov ds,ax;ds
mov si,0;ds:si指向字符串
int 7ch
mov ax,4c00h
int 21h
;编写中断例程
do0:push ax
push es
push di
push bx
mov bl,cl
mov ax,0B800h
mov es,ax
mov di,10*160+10*2;di是偏移地址
mov cx,16
s:mov al,ds:[si]
mov ch,0
mov cl,al
jcxz ok
mov es:[di],al
inc di
mov es:[di],bl
inc si
inc di
jmp short s
ok:pop bx
pop di
pop es
pop ax
iret
do0end:nop
code ends
end start
实验结果
2.编写并安装int 7ch中断例程,功能为完成loop指令
assume cs:code
data segment
db "welcome to masm!",0
data ends
code segment
start:;安装例程到0:200
mov ax,0
mov es,ax
mov di,200h
mov ax,cs
mov ds,ax
mov si,offset do0
mov cx,offset do0end-offset do0
cld
rep movsb
;更改向量表
mov ax,0
mov es,ax
mov word ptr es:[4*7ch],200h
mov word ptr es:[4*7ch+2],0
mov ax,0b800h
mov es,ax
mov di,160*2
mov bx,offset s-offset se;bx存偏移量
mov cx,80
s:mov byte ptr es:[di],'!'
add di,2
int 7ch
se:nop
mov ax,4c00h
int 21h
;编写中断例程
do0:push bp
mov bp,sp
dec cx
jcxz ok
add ss:[bp+2],bx
ok:pop bp
iret
do0end:nop
code ends
end start
实验结果
3.补全程序
mov bh,0
mov dh,ds:[si]
mov dl,0
mov ah,2
int 10h
mov dx,ds:[bx]
mov ah,9
int 21h
inc si
add bx,2
loop ok
第十四章 端口
检测点14.1
1.
mov al,2
out 70h,al
in al,71h
2.
mov al,2
out 70h,al
mov al,0
out 71h,al
检测点14.2
先逻辑左移1位得到2ax,经过两次ax+=ax,得到8ax,再和前面的2ax相加即为所求
shl ax,1
mov bx,ax
add ax,ax
add ax,ax
add ax,bx
实验十四 访问CMOS RAM
assume cs:code
data segment
db "// ::",0
data ends
code segment
start:
mov bx,0b800h
mov es,bx
mov si,160*12
mov di,0
mov bx,data
mov ds,bx
;打印年
mov al,9
out 70h,al
in al,71h
mov ah,al
mov cl,4
shr ah,cl
and al,00001111b
add ah,30h
add al,30h
call show
;打印月
mov al,8
out 70h,al
in al,71h
mov ah,al
mov cl,4
shr ah,cl
and al,00001111b
add ah,30h
add al,30h
call show
;打印日
mov al,7
out 70h,al
in al,71h
mov ah,al
mov cl,4
shr ah,cl
and al,00001111b
add ah,30h
add al,30h
call show
;打印时
mov al,4
out 70h,al
in al,71h
mov ah,al
mov cl,4
shr ah,cl
and al,00001111b
add ah,30h
add al,30h
call show
;打印分
mov al,2
out 70h,al
in al,71h
mov ah,al
mov cl,4
shr ah,cl
and al,00001111b
add ah,30h
add al,30h
call show
;打印秒
mov al,0
out 70h,al
in al,71h
mov ah,al
mov cl,4
shr ah,cl
and al,00001111b
add ah,30h
add al,30h
call show
mov ax,4c00h
int 21h
show:push ax
;打印函数,参数放在ax中
mov byte ptr es:[si],ah
add si,2
mov byte ptr es:[si],al
add si,2
mov al,ds:[di]
mov ch,0
mov cl,al
jcxz ok
mov es:[si],al
add si,2
inc di
ok:pop ax
ret
code ends
end start
实验结果
第十五章 外中断
检测点15.1
1.
pushf
call dword ptr ds:[0]
2.
cli
mov word ptr es:[9*4],offest int9
mov es:[9*4+2],cs
sti
实验十五
做了好久靠,关于这道题有个问题我一直没想明白:es段寄存器是0,模拟调用int 9h时,为什么不可以是call dword ptr es:[200h], 段地址好像非得是cs才行,用es搞不了
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
;为复制程序到0:204做准备
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]
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 9h的中断例程的入口地址恢复
push ds:[0]
pop es:[9*4]
push ds:[2]
pop es:[9*4+2]
mov ax,4c00h
int 21h
int9:push ax
push es
push di
push cx
in al,60h;读入键盘缓冲区
pushf
call dword ptr cs:[200h];调用原来的int 9h例程
cmp al,9eh
jne ok;如果不是a松开,退出例程
mov ax,0b800h
mov es,ax
mov di,0
mov cx,2000
s:mov byte ptr es:[di],'A'
add di,2
loop s
ok:pop cx
pop di
pop es
pop ax
iret
int9end:nop
code ends
end start
实验结果
第十六章 直接定址表
检测点16.1
mov ax,a[si]
add word ptr b[0]
adc word ptr b[2]
add si,2
loop s
检测点16.2
mov ax,data
mov es,ax
实验十六 编写包含多个功能子程序的中断例程
一大坑:这道题不能直接按照书上的table直接定址(不能call word ptr table[bx]),下面分析原因。
debug忘记截图了,这里借用一下网上的图。(这是他的博客https://blog.csdn.net/zhangyuzuishuai/article/details/52331447
- 下图是刚刚进入debug,安装之前int 9ch例程的指令。call [bx+005E]表示的是call word ptr table[bx],这表示安装前table在内存中的位置是cs:005E
- 下图是安装后,例程的指令。call指令没有变
- 当CPU执行中断例程的时候,执行到call时,去访问cs:005E处的table,但是此时的cs已经改变了,所以是找不到table的真正位置的,因为我将中断例程安装到了0:200,table也复制过去了。
- table的真正位置在哪呢?例程中的第一条指令是jmp,后面才是table,所以执行例程时,应该去0:202找table。因此我用的是call word ptr cs:[202h+bx]
二大坑:数据标号在编译的时候就变成了定值常数,不能直接table dw sub1,sub2,sub3,sub4
- 因为sub1,sub2....那些编号都代表着安装前,sub1,sub2...的偏移地址。当执行中断例程的时候,cs段地址已经改变了,因此这个偏移地址是不对的
- 于是我把int7c,sub1,sub2...全部复制过去了,利用int7c和sub1,sub2.....的相对距离调用程序
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
;为复制程序到0:200做准备
mov si,offset int7c;ds:si指向源地址
mov di,200h;es:di指向目的地址
mov cx,offset int7cend-offset int7c;cx存放传输长度
cld;设置传输方向
rep movsb
;设置向量表
mov word ptr es:[124*4],200h
mov word ptr es:[124*4+2],0
;调用函数
;mov ah,0
;mov al,0
;mov ah,1
;mov al,5
mov ah,3
int 7ch
mov ax,4c00h
int 21h
int7c: jmp short begin;安装后这条指令的地址是0:200
table dw offset sub1-offset int7c +200h
dw offset sub2-offset int7c +200h
dw offset sub3-offset int7c +200h
dw offset sub4-offset int7c +200h
begin: push bx
cmp ah,3
ja ok
mov bl,ah
mov bh,0
add bx,bx
call word ptr cs:[202h+bx];table 所在的位置是0:202
ok: pop bx
iret
sub1: push bx
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
sub2: push bx
push cx
push es
mov bx,0b800h
mov es,bx
mov bx,1;因为是奇地址,起始偏移地址为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
push cx
push es
mov cl,4
shl al,cl;左移4位,为后面与运算做准备
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
push si
push di
push es
push ds
mov si,0b800h
mov es,si
mov ds,si
mov si,160;ds:si指向第n+1行
mov di,0;es:di指向第n行
cld
mov cx,24
sub4s: push cx;暂存cx,因为里层循环还需要用到
mov cx,160
rep movsb
pop cx
loop sub4s
mov cx,80
mov si,0
sub4sl: mov byte ptr [160*24+si],' ';处理最后一行
add si,2
loop sub4sl
pop ds
pop es
pop di
pop si
pop cx
ret
int7cend:nop
code ends
end start
第十七章 使用 BIOS进行键盘输入和磁盘读写
检测点17.1
对,如果没有IF=1的指令,当IF=0时,将不能响应int 9中断例程,无法向键盘缓冲区填写字符。如果int 16h没有检测到键盘缓冲区的字符,而int 9例程又不能写入,int 16h将一直执行(等待等不到的用户输入
实验十七 应用int 13h中断例程对磁盘进行读写
这道题好像没给出具体的东西,似乎只是纸上谈兵,不需要上机验证?(没有验证,下面代码不保证能运行哦
assume cs:code
code segment
start:
push cs
pop ds
mov ax,0
mov es,ax
;为复制程序到0:200做准备
mov si,offset int7c;ds:si指向源地址
mov di,200h;es:di指向目的地址
mov cx,offset int7cend-offset int7c;cx存放传输长度
cld;设置传输方向
rep movsb
;设置向量表
mov word ptr es:[124*4],200h
mov word ptr es:[124*4+2],0
;象征性地调用一下中断例程7c,参数随便设置的
mov es,0
mov bx,1
mov ax,0
mov dx,1
int 7c
mov 4c00h
int 21h
int7c: push ax
push bx
;用被除数为16位,放在ax中
mov ax,dx
mov bx,1440
div bx;商存在al,余数在ah
push al;这是面号,先存起来
mov bx,18
mov al,ah
mov ah,0
div bx;逻辑扇区号/1440的余数/18,商在al,余数在ah
push al;这是磁道号,先存起来
mov al,ah
mov ah,0
add ax,1
;ax为扇区号
;下面开始初始化int 13h的参数
mov cl,al;扇区
pop ch;磁道号
pop dh;面号
mov dl,0;读写软盘
mov al,1;扇区数,随便设置了一个
pop bx
pop ax
cmp ax,0
je read
cmp ax,1
je write
read: mov ah,2
int 13h
jmp short ok
write: mov ah,3
int 13h
ok: iret
int7cend:nop
code ends
end start
标签:汇编语言,mov,al,si,第四版,ax,检测点,bx,es
From: https://www.cnblogs.com/tangxibomb/p/17208424.html