汇编语言——第六章实验

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

(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

分析如下:
(1)此程序考察的是内存中数据段和栈段的定义。

程序共定义了1个数据段,data段,首先明确,在程序运行开始(标号start处),这个数据段就已经被定义好了,并且分配了内存空间,
并赋值了。
一个栈段,stack。同理这个数据段在没有被人工定义为栈结构时,也被定义好了。并且分配了内存空间,并赋值了。
将此程序编译并连接后,使用debug调试,(这里需要注意,以下的段地址可能由于系统不同而有差异,主要是理解概念。

E:\assembly>debug DEMO.exe

-r

AX=0000 BX=0000 CX=0042 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000

DS=0B55 ES=0B55 SS=0B65 CS=0B67 IP=0000 NV UP EI PL NZ NA PO NC

0B67:0000 B8660B MOV AX,0B66

程序分析:我们什么也没执行,此时我们在data段定义的数据在哪?在ds:0100H处(原来讲过,程序最开始时ds:00~ds:100H是留给程序与操作系统通讯使用的psp内存段,参见书中p92);也就是说我们在ds:100H、0B55:100H或0B65:00处可以看见这些定义的数据。见下图。

-d ds:100

0B55:0100 23 01 56 04 89 07 BC 0A-EF 0D ED 0F BA 0C 87 09 #.V…

0B55:0110 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 …

(2)mov ax,stack

 mov ss,ax

    mov sp,16

直到这3个指令执行完毕,此时stack数据段被人工指定为了栈结构,(ss)=offset stack,也就是说此时ss段寄存器变量才赋值为stack段的段地址。sp指针指向了栈顶。

我们在上图中,看到SS=0B65,执行完这3个指令后,我们发现SS=0B66了,我们使用d命令查询下:

-d ss:0

0B66:0000 00 00 00 00 00 00 65 0B-00 00 0B 00 67 0B 68 05 …e…g.h.

有二个事实:

0B66:0000==0B55:0110,我们定义的数据在内存中的位置在程序装载后,位置是固定的,也就是说数据段的物理地址一直是固定的,只不过我们表述这个数据段时,采用了不同的段地址和偏移地址。

0B66:0000 00 00 00 00 00 00 65 0B-00 00 0B 00 67 0B 68 05 …e…g.h.
完全等价于:
0B55:0110 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 …
我们将ss指向了stack段内存,也就是说,stack这个内存段从现在开始被人工的当做了栈空间使用。在这16个字节空间里,原来都是00;为什么现在有其他数据了?这个我们先别管。它是一些其他的有用信息。

总结:观察栈的结构,注意执行push和pop指令的汇编层面含义和CPU执行的步骤。进一步理解内存的直接寻址方式。返回前,各寄存器状态如下:

AX=0B65 BX=0000 CX=0042 DX=0000 SP=0010 BP=0000 SI=0000 DI=0000

DS=0B65 ES=0B55 SS=0B66 CS=0B67 IP=001D NV UP EI PL NZ NA PO NC

0B67:001D B8004C MOV AX,4C00

答案

①CPU执行程序,程序返回前,data段中的数据 不变 。

②CPU执行程序,程序返回前,CS=0B67,SS=0B66,DS=0B65 。(根据自己系统回答)

③设程序加载后,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

首先明确:虽然我们在data段和stack段中只定义初始化了4个字节的内存,但在汇编中,直接给你分配了16个字节的空间,不足的按00补全。

结论:数据段和栈段在程序加载后实际占据的空间都是以16个字节为单位的。如果不足,以0补全填充。
在debug中查看:

-d ds:100

0B55:0100 23 01 56 04 00 00 00 00-00 00 00 00 00 00 00 00 #.V…

0B55:0110 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 …

(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

程序分析:

这次只不过是将data和stack段放到了code段后面了。那么就要注意它们段地址的变化了。

返回前,查看

-r

AX=0B68 BX=0000 CX=0044 DX=0000 SP=0010 BP=0000 SI=0000 DI=0000

DS=0B68 ES=0B55 SS=0B69 CS=0B65 IP=001D NV UP EI PL NZ NA PO NC

0B65:001D B8004C MOV AX,4C00

总结:在汇编源代码中,我们定义的code是程序执行的代码(它存储在一个我们人为规定的段code中,在程序装载时,分配空间,并将机器码写入到这段内存中);其他的数据段(无论是逻辑上的stack段,data段等)与代码段都相邻。只不过是装载、分配内存前后的问题。

答案:

(1)CPU执行程序,程序返回前,data段中的数据为多少?

执行程序后,data段有16个字节空间,前两个字数据不变,其余为00补全了。

(2)CPU执行程序,程序返回前,CS=0B65, SS=0B69, DS=0B68.

(3)程序加载后,code段地址设为X,则data段地址为(x+3),stack段的段地址为(X+4).

(为什么是这样?怎么计算的?看cx,程序加载时,我们发现cx=0044,含义:此程序所有机器码占用的空间是44H=68字节,data和stack由于定义的都是小于16个字节,一律按照16个字节分配空间,其余补00;剩余的36个字节就是code段真正的可执行的机器码。由于code段不足48个字节(3*16),故程序加载时也补0了)

我们可以使用debug看看:

-d cs:0

0B65:0000 B8 69 0B 8E D0 BC 10 00-B8 68 0B 8E D8 FF 36 00 .i…h…6.

0B65:0010 00 FF 36 02 00 8F 06 02-00 8F 06 00 00 B8 00 4C …6…L

0B65:0020 CD 21 00 00 00 00 00 00-00 00 00 00 00 00 00 00 .!..

0B65:0030 23 01 56 04 00 00 00 00-00 00 00 00 00 00 00 00 #.V…

0B65:0040 00 00 68 0B 68 0B 68 0B-00 00 1D 00 65 0B 68 05 …h.h.h…e.h.

红色的代表了代码段。紫色代表了data段。绿色是stack段

四。 如果将(1)、(2)、(3)题中的最后一条伪指令“end start”改为“end”(也就是说不指明程序的入口),则那个程序仍然可以正确执行?请说明原因。

答案:如果不指名程序的(code段的)入口,并且使用end替换end start,都能正常运行。但只有(3)题中程序可以正确的执行(因为只有它是在内存中可执行代码在最前面)。

讲解:因为如果不指名入口,程序会从加载进内存的第一个单元起开始执行,前二个题中,定义的是数据,但CPU还是将数据当做指令代码执行了。只不过程序执行时逻辑上是错误了。但真的能执行的。

如果指明了程序的入口,CPU会直接从入口处开始执行真正的机器码,直到遇到中断指令返回。此种方式能够确保程序逻辑上的正确。因此有必要为程序来指明入口。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值