前面我们写的程序中,只有一个 代码段
现在有一个问题是:
如果程序需要用其他空间来存放数据,我们使用哪里呢?
第5章中,我们讲到要使用一段安全的空间。
我们说0:200~0:300是相对安全的
可是这段空间的容量只有256个字节,如果我们需要的空间超过256个字节,该怎么办呢?
下面我们将以这样的顺序来深入的讨论多个段的问题:
1)在一个段中存放数据、代码、栈,我们先来体会一下不适用多个段时的情况。
2)将数据、代码、栈放入不同的段中。
程序第一行中的dw的含义是定义字型数据。dw即define word。
在这里,我们使用dw定义了8个字型数据(数据之间以逗号分隔),他们所占的内存空间的大小为16个字节。
由于这8个数据在代码段中,程序在运行的时候CS中存放代码段的段地址,所以我们可以从CS中得到他们的段地址。
这8个数据的偏移地址是多少呢?
因为用dw定义的数据处于代码段的最开始,所以偏移地址为0,这8个数据就在代码段的偏移0、2、4、6、8、A、C、E处。
程序运行时,他们的地址就是CS:0、CS:2、CS:4、CS:6、CS:8、CS:A、CS:C、CS:E
用debug加载后,我们可以将IP设置为10h,从而使CS:IP指向程序中的第一条指令.然后再用T P G命令
而我们直接编译连接形成可执行文件后,直接运行会出现问题,因为程序的入口处不是我们所希望执行的指令.
我们可以在源程序中指明程序的入口所在,如下:
我们在程序的第一条指令的前面加上了一个标号start,而这个标号在伪指令end的后面出现.
探讨end的作用:
end除了通知编译器程序结束外,还可以通知编译器程序的入口在什么地方.
6.2 在代码段中使用栈
程序的思路大致如下:
程序运行时,定义的数据存放在cs:0~cs:F单元中,共8个字单元.依次将这8个字单元中的数据入栈,然后再依次出栈到这8个字单元中,从而实现数据的逆序存放.
问题是,我们首先要有一段可当作栈的内存空间.如前所述,这段空间应该由系统来分配.我们可以在程序中通过定义数据来取得一段空间,然后将这段空间当作栈空间来用.
1 assume cs:codesg 2 codesg segment 3 dw 0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H 4 dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 5 ;用dw定义8个字型数据,在程序加载后,将取得8个字的 6 ;内存空间,存放这8个数据。我们在后面的程序中将这段 7 ;空间当作栈来使用。 8 start: mov ax,cs 9 mov ss,ax 10 mov sp,30h ;设置栈顶ss:sp指向cs:30 11 12 mov bx,0 13 mov cx,8 14 s: push cs:[bx] 15 add bx,2 16 loop s ;以上将代码段0~15单元中的8个字型数据依次入栈 17 18 mov bx,0 19 mov cx,8 20 s0: pop cs:[bx] 21 add bx,2 22 loop s0 ;以上依次出栈8个字型数据到代码段0~15单元中 23 mov ax,4c00h 24 int 21h 25 codesg ends 26 end start ;指明程序的入口在start处
注意程序中的指令:
mov ax,cs
mov ss,ax mov sp,30h
我们要将cs:10~cs:2f的内存空间当作栈来用,初始状态下栈为空,所以ss:sp要指向栈底,则设置ss:sp指向cs:30
6.3 将数据、代码栈放入不同的段
在前面的内容中,我们在程序中用到了数据和栈,我们将数据栈和代码都放到了一个段里面.我们在编程的时候要注意何处是数据,何处是栈,何处是代码
这样做显然有两个问题:
1)把他们放到一个段中使程序显得混乱
2)前面程序中处理的数据很少,用到的栈空间也小,加上没有多长的代码,放到一个段里面没有问题.
但如果数据栈和代码需要的空间超过64KB,就不能放在一个段中(一个段的容量不能大于64KB,8086模式是这样),所以我们应该考虑用多个段来存放数据代码和栈
怎样做呢?
我们用和定义代码段一样的方法来定义多个段,然后在这些段里面定义需要的数据,或通过定义数据来取得栈空间。
具体程序如下:
1 assume cs:code,ds:data,ss:stack 2 3 data segment 4 dw 0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H 5 data ends 6 7 stack segment 8 dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 9 stack ends 10 11 code segment 12 start:mov ax,stack 13 mov ss,ax 14 mov sp,20h ;设置栈顶ss:sp指向stack:20 15 16 mov ax,data 17 mov ds,ax ;ds指向data段 18 19 mov bx,0 ;ds:bx指向data段中的第一个单元 20 21 mov cx,8 22 s:push [bx] 23 add bx,2 24 loop s ;以上将data段中的0~16单元中的8个字型数据依次入栈 25 26 mov bx,0 27 mov cx,8 28 s0:pop [bx] 29 add bx,2 30 loop s0 ;以上使8个字型数据依次出栈并送到data段的0~16单元中 31 32 mov ax,4c00h 33 int 21h 34 code ends 35 end start
程序段中data段中的数据0abch的地址就是:data:6.
我们要将它送入bx中,就要用如下的代码:
mov ax,data
mov ds,ax
mov bx,ds:[6]
我们不能用下面的指令:
mov ds,data
mov ax,ds:[6]
其中指令mov ds,data是错误的,因为8086CPU不允许将一个数值直接送入段寄存器中。
程序中对段名的引用,如指令mov ds,data的data将被编译器处理为一个表示段地址的数值。
代码段、数据段、栈段完全是我们的安排,现在,我们以一个具体的程序再次讨论所谓的代码段、数据段、栈段
我们在源程序中用伪指令assume cs:code,ds:data,ss:stack将cs、ds和ss分别和code、data、stack段相连。
这样做了之后,CPU是否就会将cs指向code,ds指向data,ss指向stack,从而按照我们的意图来处理这些段呢?
当然也不是,要知道assume是伪指令,是由编译器执行的,也是仅在源程序中存在的信息,CPU并不知道他们
我们不必深究assume的作用,只要知道需要用它将你定义的具有一定用途的段和相关的寄存器练习起来就可以了
若要cpu按照我们的安排行事,就要用机器指令控制它,源程序中的汇编指令是cpu要执行的内容。
cpu如何知道去执行他们?
我们在源程序的最后用end start说明了程序的入口,这个入口将被写入可执行文件的描述信息,可执行文件中的程序被加载入内存后,cpu的cs:ip被设置指向这个入口,从而开始执行程序中的第一条指令。
标号start在code段中,这样cpu就将code段中的内容当作指令来执行
我们在code段中,使用指令:
mov ax,stack
mov ss,ax
mov sp,20h
设置ss指向stack,设置ss:sp指向stack:20,cpu执行这些指令后,将把stack段当作栈空间来用。
cpu若要访问data段中的数据,则可用ds指向data段,用其他的寄存器(如:bx)来存放data段中数据的偏移地址。
总之,CPU到底如何处理我们定义的段中的内容,是当作指令执行,当作数据访问,还是当作栈空间,完全是靠程序中具体的汇编指令,和汇编指令对cs:ip,ss:sp,ds等寄存器的设置来决定的。