实验准备:
复习第六章内容,了解实验内容,下载邮箱中的实验框架
实验一
1.实验代码:
assume cs:code, ds:data, ss:stack
data segment
dw 0123h, 0456h, 0789h, 0abch, 0defh, 0fedh, 0cbah, 0987h
data ends
stack segment
dw 0, 0, 0, 0, 0, 0, 0, 0
stack ends
code segment
start: mov ax,stack
mov ss, ax
mov sp,16
mov ax, data
mov ds, ax
push ds:[0]
push ds:[2]
pop ds:[2]
pop ds:[0]
mov ax,4c00h
int 21h
code ends
end start
2.对程序进行编译,连接,用Debug加载:
![](https://img2018.cnblogs.com/blog/1499456/201811/1499456-20181123212051803-386850827.png)
3.先用r命令查看寄存器。可以看到CX寄存器中的值为42,但这还包括数据段和栈段,数据段有8个数据,16个字节,即10h,栈段也是16个字节,即10h。所以代码段长度为42-20=22h,
CS:IP = 076C:0000,用u命令进行反汇编;
然后g命令,g 001D执行mov ax 4c00h之前的内容;
最后用d命令查看内存中的内容:
![](https://img2018.cnblogs.com/blog/1499456/201811/1499456-20181123212425059-242480568.png)
①CPU执行程序,程序返回前,data段中的数据为多少?
23 01 56 04 89 07 BC 0A-EF 0D ED 0F BA 0C 87 09
②CPU执行程序,程序返回前,cs=076C,ss=076B,ds=076A.
③设程序加载后,code段的短地址为x,则data段的短地址为x-2,stack段的短地址为x-1
实验二
1.实验代码:
assume cs:code, ds:data, ss:stack
data segment
dw 0123h, 0456h
data ends
stack segment
dw 0, 0
stack ends
code segment
start: mov ax,stack
mov ss, ax
mov sp,16
mov ax, data
mov ds, ax
push ds:[0]
push ds:[2]
pop ds:[2]
pop ds:[0]
mov ax,4c00h
int 21h
code ends
end start
2.重复和实验一的操作balabala,编译链接执行,debug ex5_2,
用r命令查看寄存器内容,u命令 u 0000 0021,g 001D执行到mov ax,4c00h之前的内容,d命令查看内存:
![](https://img2018.cnblogs.com/blog/1499456/201811/1499456-20181123213841011-1127901627.png)
①CPU执行程序,程序返回前,data段中的数据为多少?
23 01 56 04 00 00 00 00-00 00 00 00 00 00 00 00
②CPU执行程序,程序返回前,cs=076C,ss=076B,ds=076A.
③设程序加载后,code段的短地址为x,则data段的短地址为x-2,stack段的短地址为x-1
④对于如下定义的段:
name segment
...
name ends
如果段中的数据占N个字节,则程序加载后该段实际占有的空间为[(N+15)/16]*16。
其实我也不太懂,下面是我在百度文库里看到的详细解释……
在8086CPU架构上,段是以paragraph(16-byte)对齐的。程序默认以16字节为边界对齐,所以不足16字节的部分数据也要填够16字节。“对齐”是alignment,这种填充叫做padding。16字节成一小段,称为节
这首先要从8086处理器寻址原理说起。
8086这种处理器有二十根地址线(20个用于寻址的管脚),可以使用的外部存储器空间可达1MB(00000H~FFFFFH)。 但是,8086内部的寄存器都是16位的,用任何一个寄存器(比如BX或SI),都无法直接寻址8086所支持的1M地址空间,因为16位寄存器只能表示640KB的空间范围(0000~FFFFH)。
所以,Intel想了一个方法,设计出了CS/DS/ES/SS这几个段地址寄存器,用段地址寄存器与普通寄存器组合,来寻址1MB的地址范围,即:对于CPU取指来说,用CS:IP组合来寻址下一个要执行的指令(也在存储器中);对于堆栈操作PUSH/POP来说,用SS:SP组合来表示当前栈指针(栈也在存储器中);对于数据操作指令来说,用默认的DS/ES或指定的段地址(段前缀指令)与偏移量寄存器组合寻址。
组合后的实际地址=段寄存器内容×16+偏移量寄存器内容
从这个公式可以看到,每一个段的地址都对齐在16的倍数上。比如DS=1234H,则这个段就从 1234H×16+0000H=12340H开始,最大到1234H×16+0FFFFH=2233FH为止。
二、对同一个内存地址,有不同的段:偏移量组合方法,比如2233FH这个地址,既可以表示为1234H:0FFFFH(在1234H段中),也可以表示为2233H:0000FH(在2233H段中)。
那么,如果汇编程序中有下面两个连续的段定义,汇编编译程序会怎么做呢?
name1 segment
d1 db 0
name1 ends
name2 segment
d2 db 0
name2 ends
编译程序可以将name1和name2编译成一个段,d1和d2在内存中连续存放,这样可以节省内存空间。比如编译程序以name1为基准,将name1作为一个段的起始,程序加载后会被放在xxxx0H的地方,那么d1就放在该段偏移地址为0字节的位置,d2就放在该段偏移为1字节的位置。
这样的处理方式虽然可能会节省一点内存空间,但是对于编译器的智能化要求太高了,它必须将源程序中所有引用到name2和d2的地方,全部调整为以name1段为基准,这实在是太难了,而且也节省不了几个字节的空间,编译器是不会干这种吃力不讨好的事的。
编译器实际的处理方式是将name1中的所有内容放在一个段的起始地址处,name2里的所有内容放在后续一个段的起始地址处(这也是汇编指令segment的本义:将不同数据分段)。这样,即使name1中只包含一个字节,也要占一个段(16个字节),所以,一个段实际占用的空间=(段中字节数+15)/ 16。
所以,8086处理器的内部寻址原理和汇编程序编译器共同决定了segment定义的段必须放在按16的倍数对准的段地址边界上,占用的空间也是16的倍数。
实验三
1.实验代码:
assume cs:code, ds:data, ss:stack
code segment
start: mov ax,stack
mov ss, ax
mov sp,16
mov ax, data
mov ds, ax
push ds:[0]
push ds:[2]
pop ds:[2]
pop ds:[0]
mov ax,4c00h
int 21h
code ends
data segment
dw 0123h, 0456h
data ends
stack segment
dw 0,0
stack ends
end start
2.重复实验一二的操作
编译链接执行,debug ex5_3,
用r命令查看寄存器内容,u命令 u 0000 0021,g 001D执行到mov ax,4c00h之前的内容,d命令查看内存:
![](https://img2018.cnblogs.com/blog/1499456/201811/1499456-20181123215552996-1673036583.png)
①CPU执行程序,程序返回前,data段中的数据为多少?
23 01 56 04 00 00 00 00-00 00 00 00 00 00 00 00 00 00
②CPU执行程序,程序返回前,cs=076C,ss=076E,ds=076D.
③设程序加载后,code段的短地址为x,则data段的短地址为x+3,stack段的短地址为x+4。
实验四
如果将(1),(2),(3)题中的最后一条伪指令end start
改为end(也就是说,不指明程序的入口个)
,则哪个程序仍然可以正确执行?请说明原因。
以实验3先为例,重复操作得到结果和之前一致:
![](https://img2018.cnblogs.com/blog/1499456/201811/1499456-20181123220550767-1998072181.png)
这里我觉得改了和没改应该一样吧,再把实验一和实验二验证一下:
![](https://img2018.cnblogs.com/blog/1499456/201811/1499456-20181123221306203-1837641171.png)
![](https://img2018.cnblogs.com/blog/1499456/201811/1499456-20181123221441524-1889872983.jpg)
但是实验三可以正确执行,但是实验一和实验二反汇编后不能正确执行,查看源代码
3个实验的差异是实验3将代码段写在数据段和栈段之后,第1、2个实验数据段和栈段不是有效指令,如果不指定代码段入口,CPU会把数据当作指令来通过CS:IP指向。
如果没有end start就会默认以IP=0开始执行,而不是从start开始执行。
而实验内容(3)的data和stack都在code之后,所以IP本来就为0,可以正确执行。
实验五
1.实验代码:
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 8 dup(0)
c ends ;
code segment
start:
mov ax,a
mov ds,ax
mov ax,b
mov ss,ax
mov ax,c
mov es,ax
mov bx,0
mov cx,8
s:
mov al,ds:[bx]
add al,ss:[bx]
mov es:[bx],al
inc bx
loop s
mov ax,4c00h
int 21h
code ends
end start
2.编译连接执行,再用r查看寄存器内容,用u命令进行反汇编:
![](https://img2018.cnblogs.com/blog/1499456/201811/1499456-20181123225553891-2142975727.png)
3.用g命令执行到000A,再用D命令查看a和b中的数据(一开始看错了执行到0008,然后查看ss寄存器中的数据发现不对……):
![](https://img2018.cnblogs.com/blog/1499456/201811/1499456-20181123225721993-404546454.png)
4.最后执行到0020再查看es中的数据发现a和b已经相加:
![](https://img2018.cnblogs.com/blog/1499456/201811/1499456-20181123225832456-1386801590.png)
实验六
1.实验代码:
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 8 dup(0)
b ends
code segment
start:
mov ax,a
mov ds,ax
mov ax,b
mov ss,ax
mov sp,10h
mov bx,0
mov cx,8
s:
push ds:[bx]
add bx,2
loop s
mov ax,4c00h
int 21h
code ends
end start
2.编译连接执行,r查看寄存器,u反汇编:
![](https://img2018.cnblogs.com/blog/1499456/201811/1499456-20181123230542293-1996019619.png)
3.先执行到g 0005查看a中数据:
![](https://img2018.cnblogs.com/blog/1499456/201811/1499456-20181123230624138-990503302.png)
4.再g 001A执行完查看ss中内容:
![](https://img2018.cnblogs.com/blog/1499456/201811/1499456-20181123230644414-198858005.png)
发现b的8个字单元为a段前8个字节的逆序,程序完成
总结与体会
①这次实验时间比较长,反复的操作让我对汇编调试更加熟练
②课堂上的内容还是需要通过实验来巩固加深的,虽然这次实验很长而且到后面我其实有点烦躁,但是学习的过程并不是一个愉快的过程,而且很多之前不熟悉的操作现在也比较熟练了
实践才能学有所得