第六章 包含多个段的程序
6.1 在代码段中使用数据
问题:编程计算以下8个数据的和,结果存在ax 寄存器中:0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H。
assume cs:codesg
codesg segment
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
mov bx,0
mov ax,0
mov cx,8
s:add ax,cs:[bx]
add bx,2
loop s
mov ax,4c00h
int 21h
codesg ends
end
解释:
- 程序第一行中的 “dw”的含义是定义字型数据。dw即define word。在这里,我们使用dw定义了8个字型(数据之间以逗号分隔),它们所占的内存空间的大小为16个字节。
- 程序中的指令就要对这8个数据进行累加,可这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。
如何让这个程序在编译后可以存系统中直接运行呢?
我们可以在源程序中指明界序的入口所在
assume cs:codesg
codesg segment
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
start: mov bx,0
mov ax,0
mov cx,8
s: add ax,cs:[bx]
add bx,2
loop s
mov ax,4c00h
int 21h
codesg ends
end start
我们在程序的第一条指令的前面加上了一个标号start,而这个标号在伪指令end的后面出现。
end的作用:
end 除了通知编译器程序结束外,还可以通知编译器程序的入口在什么地方。
有了这种方法,我们就可以这样来安排程序的框架:
assume cs:code
code segment
:
数据
:
start:
:
:
代码
:
:
code ends
end start
6.2 在代码段中使用栈
**利用栈,将程序中定义的数据逆序存放。 **
assume cs:codesg
codesg segment
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
?
code ends
end
大致思路:
-
程序运行时,定义的数据存放在cs:0~cs:15单元中,共8个字单元。依次将这8个字单元中的数据入栈,然后再依次出栈到这 8 个字单元中,从而实现数据的逆序存放。
-
我们首先要有一段可当作栈的内存空间,我们可以在程序中通过定义数据来取得一段空间,然后将这段空间当作栈空间来用。
-
mov ax,cs mov ss,ax mov sp,32
我们要讲 cs:16 ~ cs:31 的内存空间当作栈来用,初始状态下栈为空,所以 ss:sp要指向栈底,则设置ss:sp指向cs:32。
6.3 将数据,代码,栈放入不同的段
怎么做:
我们用和定义代码段一样的方法来定义多个段,然后在这些段里面定义需要的数据,或通过定义数据来取得栈空间。
8086CPU不允许将一个数值直接送入段寄存器中。
程序中对段名的引用,如指令“mov ds,data”中的“data”,将被编译器处理为一个表示段地址的数值。
CPU如何知道去执行它们(对数据,代码,栈的人为分段)
我们在源程序的最后用“end start”说明了程序的入口,这个入口将被写入可执行文件的描述信息,可执行文件中的程序被加载入内存后,CPU的CS:IP被设置指向这个入口,从而开始执行程序中的第一条指令。
标号“start”在“code”段中,这样CPU就将code段中的内容当作指令来执行了。
我们在code段中,使用指令:
mov ax,stack
mov ss,ax
mov sp,16
设置ss指向stack,设置ss:sp指向stack:16, CPU 执行这些指令后,将把stack段当做栈空间来用。
CPU若要访问data段中的数据,则可用 ds 指向 data 段,用其他的寄存器(如:bx)来存放 data段中数据的偏移地址。
总之,CPU到底如何处理我们定义的段中的内容,是当作指令执行,当作数据访问,还是当作栈空间,完全是靠程序中具体的汇编指令,和汇编指令对CS:IP、SS:SP、DS等寄存器的设置来决定的。