实验5——编写、调试具有多个段的程序
一、实验内容
(1)将下面的程序编译、连接,用debug加载、跟踪,然后回答问题。
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
① CPU执行程序,程序返回前,data段中的数据为多少?
答: data中的数据为:0123h 0456h 0789h 0abch 0defh 0fedh 0cbah 0987h。与执行程序前data数据段中的数据一致。
②CPU执行程序,程序返回前,cs=076ch,ss=076bh,ds=076ah。
③设程序加载后,code段的段地址为X,则data段的段地址为 X-2,stack段的段地址为 X-1。
(2)将下面的程序编译、连接,用debug加载、跟踪,然后回答问题。
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
① CPU执行程序,程序返回前,data段中的数据为多少?
答: data中的数据为:0123h 0456h 。与执行程序前data数据段中的数据一致。
②CPU执行程序,程序返回前,cs=076ch,ss=076bh,ds=076ah。
③设程序加载后,code段的段地址为X,则data段的段地址为 X-2,stack段的段地址为 X-1。
④对于如下定义的段:
name segment
...
name ends
如果段中的数据占N个字节,则程序加载后,该段实际占有的空间为①N——N为16的倍数时 ②(N/16+1)*16——N不为16的倍数。
(3)将下面的程序编译、连接,用debug加载、跟踪,然后回答问题。
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
① CPU执行程序,程序返回前,data段中的数据为多少?
答: data中的数据为:0123h 0456h 。与执行程序前data数据段中的数据一致。
②CPU执行程序,程序返回前,cs=076ah,ss=076eh,ds=076dh。
③设程序加载后,code段的段地址为X,则data段的段地址为 X+3,stack段的段地址为 X+4。
(4)如果将(1)、(2)、(3)题中的最后一条伪指令"end start"改为"end"(也就是说,不指明程序的入口),则哪个程序仍然可以正确执行?请说明原因。
答:只有(3)中的程序可以正确执行。因为(3)中的程序在逻辑段的一开始就是代码段,即相应的汇编指令,而其余两个一开始为数据段,如果不指明程序入口,则计算机内部会把数据段即栈段中的信息也当做是相应的汇编指令,从而不能正确执行。不过在该实验中,(1)、(2)、(3)如果去掉start,最后data数据段中的结果同正确执行后一样,但这并不能说明未指明程序入口的程序都能如此,在这里,可能是由于data数据段即之后的栈段中的数据被当做汇编指令后不影响执行结果,倘若出现比如jmp bx等发生跳转的指令后,结果就会大变样了。
(5)程序如下,编写code段中的代码,将a段和b段中的数据依次相加,将结果存到c段中。
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 c1 segment db 8 dup(0) c1 ends code segment start:mov ax,a mov ds,ax mov ax,c1 mov es,ax mov bx,0 mov cx,8 s: mov al,[bx] mov es:[bx],al ;将a段中的数据放入c段中 inc bx loop s mov ax,b mov ds,ax mov bx,0 mov cx,8 s1: mov al,[bx] add es:[bx],al ;此时再将b段与c段相加 inc bx loop s1 mov ax,4c00h int 21h code ends end start
利用debug调试结果如下:
由debug调试可得:该程序可以正确实现数据相加。
(6)程序如下,编写code段中的代码,用push指令将a段中的前8个字型数据,逆序存储到b段中。
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,b mov ss,ax mov sp,11h ;定义栈 mov ax,a mov ds,ax mov bx,0 mov cx,8 s: mov ax,[bx] push ax add bx,2 loop s mov ax,4c00h int 21h code ends end start
利用debug调试如下:
由debug调试可得:a段中的前8个字型数据已逆序存储到b段中。
在该实验的debug调试中,在一个比较愚蠢的问题上纠结了好久,决定记录下来。一开始在b段中定义了8个字型数据,全为0,然后在执行push ax这一指令之前,我用d命令查看此时b段中的数据为多少,我理所当然的认为此时应该是16个字节全为0,结果却我和想的有所差异,一开始百思不得其解......后来在和同学讨论中发现,此时并没有入栈的操作,故此时栈中的数据全是初始时的数值,并不一定16个字节全为0,而之所以在b段中要定义8个全为0 的字单元,是因为要分配一块内存空间为接下来入栈等操作做准备,而这并不意味这此时栈中全为0。
为此,我还另写了一小段程序来验证是否正确。
assume cs:code a segment dw 8 dup(0) a ends code segment start:mov ax,a mov ss,ax mov sp,10h mov bx,0 mov cx,8 s: push bx ;将0压入栈 loop s mov ax,4c00h int 21h code ends end start
二、总结与体会
在本次试验中,主要收获有以下几点:
(1)掌握了用u命令准确地反汇编到int 21h结束。其中要用到试验(2)中的结论,不管段中的数据是否占满16个字节,或是否是16的倍数,该段实际占有的空间都是16的倍数,根据该结论将代码段之前的数据段占有的空间算出,再用 [(CX)-1] 减去该值即可得到u反汇编的结束地址。
(2)巩固了之前第3章所学的栈中的相关知识。当把一段内存空间当作栈段来使用时,初始状态栈是空的,SS的值比较好确定,SP的值需要具体分析,该段内存空间的大小及偏移地址的情况。
例如:
assume cs:code,ss:stack,ds:data ... stack segment dw 16 dup(0) stack ends code segment start:mov ax,stack mov ss,ax mov sp,20h ;20h=1e+2 ... mov ax,4c00h int 21h code ends end start
assume cs:code code segment dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h dw 16 dup(0) start:mov ax,cs mov ss,ax mov sp,30h ;30h=2e+2 ... mov ax,4c00h int 21h code ends end start
这两段代码都是书上的原代码,用于区分。第一个将数据与代码放入不同的段中,每一个段的偏移地址都是从0开始,而第二个则不然,数据和代码都是在code段中的,故而在计算偏移地址时需要注意叠加。
(3)关于 dw 8 dup(0) 指令,经过查阅资料知道了dup 在汇编中是一条伪指令,用来重复初始化数据。