第六章:包含多个段的程序

我们之前说0:200-0:2ff是安全空间,但容量其实只有256字节。如果我们的需要超出这个大小,还需要向操作系统申请。在操作系统允许的情况下,程序可以取得任意容量的空间。

我们通过在源程序中定义段来获取内存空间。我们定义不同的段来存放不同类型的数据,比如代码段,数据段,栈段。

image-20221001125543373

比如此类问题,我们在上一章已经知道,可以通过循环进行累加。但是,如何将这初始的数据存储在一组连续的内存单元中呢?

应该让系统为我们分配这样一段连续内存空间,我们用指令将其送入连续内存空间。比如如下程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
assume cs:code
code segment
dw 0123h,0456h,0789h,0abch

mov bx,0
mov ax,0

mov cx,4
s:add ax,cs:[bx]
add bx,2
loop s

mov ax,4c00h
int 21h

code ends
end

在这里dw代表定义字型数据也就是define words,显然这里定义了8个字型数据总共也就是16个字节。

但是这8个数据存放在哪里呢

因为我们初始定义的是代码段code,所以可以从cs中得到其段地址,并且因为dw定义的数据在代码段的最开始,所以偏移地址为0。这4个数据就在代码段的便宜0,2,4,6处。随着累加依次读取完毕。

我们可以调试一下上述程序

image-20221001135911500

开始调试,先查看当前cs指向的地址。可以看到我们看不懂076A处的指令,因为开始的数据,指令左边显示的2312,不应该翻译成指令,而是我们定义的字型数据!所以我们当然看不懂指令。于是我们向下查看8个字节。

image-20221001140250126

用此命令,找出前八个字节如图所示,正好为我们定义的字型数据!

但是问题来了,我们没法用debug直接执行啊,前面都是数据啊,所以我们应该定义一下程序入口,让程序执行的时候直接从入口开始。用start即可!

image-20221001140703865

既然有start,就可以再来分析一波end了,end其实不只通知编译器程序结束,还能通知入口在什么地方,也就是start处。

这个入口的便宜地址会被添加在可执行文件的描述信息中。

6.2代码段中使用栈

我们来看这样的问题

image-20221001141606754

所以要先让系统分配一段可当作栈的内存空间。可以通过刚才,在程序中定义数据的方式,来获取一段空间,然后把这段空间当作栈来使用!

来看程序

image-20221001143913840

定义了一段空的栈空间,16个字节大小,然后我们先入栈,把8个字节的数据入栈,然后再重置bx和cx之后出栈。

对于本程序,我们将cs:10-cs:2F当作栈空间来使用。所以要设置好栈底,即ss:sp指向栈底cs:30。

下面我们来看一个练习

image-20221001144600706

我们来做一个分析:

显然先定义了数据,然后定义了数据段ds,也就是要用ds中的内容改写我们程序中的内容。

所以我们要加入

1
mov cs:[bx], ax

这样即改变了程序中定义的8个字型数据。

再来看下一解,我们尝试用栈去修改此内容。

image-20221001145723742

image-20221001145733550

可以看到我们定义了10个空白空间用作栈空间。

当然,要定义好栈底,我们把10-23当作栈空间来使用,显然栈底指向24(图中标注有误)

我们既然想让数据段ds中的数据来改变现有程序中的数据,就可以让ds中数据先行入栈

1
push [bx]

如图中也有,并且让数据出栈道cs:[bx]即可!

6.3将数据,代码,栈放入不同的段

我们刚才把数据,代码都放入了一个段,其实非常混乱,我们可以定义多个段来分别存放代码,数据和栈!

我们下面举个例子,定义多个段,来实现上面程序实现的数据改写功能!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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,0,0,0,0,0,0,0,0
stack ends

code segment
start:mov ax,stack
mov ss,ax#ss指向stack段
mov sp,20h#设置栈底

mov ax,data
mov ds,ax#ds指向data段

mov bx,0#ds:bx指向data段第一个单元


mov cx,8
s:push [bx]#将数据段中数据入栈
add bx,2
loop s#以上将data段中16个字节的数据一次入栈!

mov bx,0
mov cx,8#该出栈了

s0:pop [bx]#依次仍出栈到data中
add bx,2
loop s0
code ends
end start

段名就相当于一个标号,代表了段地址,所以

1
mov ax,data表示将名称为data的段的地址送入ax

比如我们将data中第六偏移地址的数据送入bx,即

1
2
3
mov ax,data
mov ds,ax#回忆为什么需要导一步,因为不能直接将数值送入段寄存器
mov bx,ds:[6]

对于段的命名,完全是看个人意愿的。

下面是练习时间

image-20221001152818472

image-20221001152828692

我们来分析一下程序要做什么

程序在最后定义了数据段和栈段,最开始为代码段。程序实现不过是把数据段入栈,然后在按顺序出栈,注意出栈顺序,先进后出,后进先出!

image-20221001154151992

我们看这一问,非常精彩。我们先查看cx,cx中存放了所有机器码占用空间,比如(0444)也就是68个字节,我们用68减掉栈空间和数据空间的大小,也就是68-16-16,这里问什么减16呢,因为有这样一条规定:不满16的按16处理!

所以得到code段占用空间为3个段也就是48字节!所以,当start处开始时,先到code段,也就是经过48字节所以data段地址为X+3,stack段地址为X+4。

本章内容结束!