在操作系统的环境中,合法通过操作系统取得的空间都是安全的。程序取得所需要空间的方法有两种,一是在加载程序的时候为程序分配,二是程序在执行的过程中向系统申请。我们可以用 dw xxxxxh来申请内存空间,dw的含义是定义字型数据。dw即"define word"的意思。需要注意的是如果我们直接用dw来申请内存空间可能会导致直接运行汇编程序时出错,因为数据和指令本质上都是二进制数据罢了。所以我们需要在源程序中指明程序的入口所在,具体做法就是在指令开始的语句前加个 start标记,然后再程序结尾用 end start告诉程序入口到底在哪。
start:
end start
那么程序时如何知道程序入口在哪的呢 end start伪指令通过把标记转为偏移地址的方法 将ip改变为程序入口的偏移地址。
所以我们应该考虑设置多个段存放不同的内容。
如果段中的数据占N个字节,则程序加载后,该段实际占有的空间为(N/16 +1)*16,因为8086CPU规定一个段的起始地址为16的倍数,
在代码段中使用数据
考虑这样一个问题,编程计算以下8个数据的和,结果存在ax寄存器中:
0123h、0456h、0789h、0abch、0defg、0fedh、0cbah、0987h
之前我们不关心数据本身,但是现在指定了数据给我们累加所以需要关心数据本身,那么我们需要给这些数据安排一些存储空间,我们不能随意指定存储空间,最安全的方式是随着程序的加载让CPU给我们自动安排存储空间。当可执行文件中的程序被加载入内存时,这些数据也同时被加载入内存中。
具体做法可以看下面的程序:
assume cs:code
code 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
code ends
end
但是这样写的程序会有些问题,第一便是程序结构太混乱,代码和数据都放在一个段中,第二便是如果我们之间执行程序,那么便会出现问题,因为程序前面是数据而不是指令。
我们有解决方法在源程序中直接指明程序的入口所在,具体做法如下:
assume cs:code
code 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
code ends
end start
我们可以探讨下end的作用,end除了通知编译器程序结束外,还可以通知编译器程序的入口在什么地方,在上面的程序中我们用end 指令指明了程序的入口在标号start处。
那么程序时如何知道程序入口在哪的呢 end start伪指令通过把标记转为偏移地址的方法 将ip改变为程序入口的偏移地址既标记所在代码的偏移地址。归根结底,我们若要CPU从何处开始执行程序,只要在源程序中用“end 标号”指明就可以了。
在代码段中使用栈
如果我们要将一些数据按逆序存放,那么可能我们需要使用到栈。如何在代码段中使用栈呢,请看下面的程序:
assume cs:codesg
code segment
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
start: mov ax,cs
mov ss,ax
mov sp,30h
mov bx,0
mov cx,8
s: push cs:[bx]
add bx,2
loop s
mov bx,0
mov cx,8
s0: pop cs:[bx]
add bx,2
loop s0
mov ax,4c00h
int 21h
codesg ends
end start
我们在程序中用dw定义了16个为0的字型数据,我们把这16个字型数据所占的存储空间当作栈来使用,只要我们让ss指向字型数据所在段,sp指向栈底便可以了。
检测点6.1
assume cs:codesg
codesg segment
dw 0123h,0456h,0789h,0abch,0defh,0cbah,0987h
start:: mov ax,0
mov ds,ax
mov bx,0
mov cx,8
s: mov ax,[bx]
mov cs:[bx],ax
add bx,2
loop s
mov ax,4c00h
int 21h
codesg ends
end start
(2)下面的程序实现依次用内存0:0~0:15单元中的内容改写程序中的数据,数据的传送用栈来进行。栈空间设置在程序内。完成程序:
assume cs:codesg
codesg segment
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
dw 0,0,0,0,0, 0,0,0,0,0,0
start: mov ax,cs
mov ss,ax
mov sp,24h
mov ax,0
mov ds,ax
mov bx,0
mov cx,8
s: push [bx]
pop cs:[bx]
add bx,2
loop s
mov ax,4c00h
int 21h
codesg ends
end start
将数据、代码、栈放入不同的段
我们之前在程序中用到了数据和栈,将数据、栈和代码都放到一个段里面,我们可以把栈空间和数据和指令都放在一个代码块中,但是这样使用会带来一些问题,比如一个代码块的大小最都为64KB,把所有东西多放到一个段中会使得程序显得混乱,而且使用时必须自己小心哪些段是数据段哪些是代码段不好分辨。
所以我们应该考虑用多个段来存放数据、代码和栈。
我们可以像定义代码段一样来定义数据段和栈。
下面的程序我们将数据、栈和代码放到了不同的段中。
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
mov sp,20h
mov sp,20h
mov ax,data
mov ds,ax
mov bx,0
mov cx,8
s:push [bx]
add bx,2
loop s
mov bx,0
mov cx,8
s0:pop [bx]
add bx,2
loop s0
mov ax,4c00h
int 21h
code ends
end start
代码段、数据段、栈段完全是我们的安排,如何让CPU按照我们的这种安排来执行这个程序呢,我们在源程序中使用伪指令assume cs:code,ds:data,ss:stack将cs、ds和ss分别相连但是这样做并没有真的让CPU按照我们的意图来执行程序。所以我们需要在代码段中利用汇编指令设置ds和ss里的内容将它正确指向相应的段。
下面我们解读下程序:
对段地址的引用
程序中有多个段了,如何访问段中的数据呢,我们可以通过地址来访问段中的数据,而地址是分为两部分的既段地址和偏移地址,如何指明要访问的数据的段地址呢,在程序中,段名就相当于一个标号,它代表了段地址,所以指令“mov ax,data”的含义就是将data段的段地址送入ax中,偏移地址就是在data段中的偏移地址。