本文属于《X86架构指令基础系列教程》之一,欢迎查看其它文章。
我们用和定义代码段一样的方法来定义多个段,然后在这些段里面定义需要的数据,或通过定义数据来取得栈空间。具体做法如下面的程序所示,它将数据、栈和代码放到了不同的段中。
assume cs:code,ds:data,ss:stack
data segment
dw 0123h,0456h, 0789h, Oabch, Odefh, Ofedh, Ocbah, 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 ;设置栈顶 ss:sp 指向 stack:20
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段中的0~15单元中的8个字型数据依次入栈
mov bx,0
mov cx,8
s0: pop [bx]
add bx,2
loop s0 ;以上依次出栈8个字型数据到data段的0~15单元中
mov ax,4c00h
int 21h
code ends
end start
(1) 定义多个段的方法这点,我们从程序中可明显地看出,定义一个段的方法和前面所讲的定义代码段的方法没有区别,只是对于不同的段,要有不同的段名。
(2) 对段地址的引用现在,程序中有多个段了,如何访问段中的数据呢?当然要通过地址,而地址是分为两部分的,即段地址和偏移地址。如何指明要访问的数据的段地址呢?在程序中,段名就相当于一个标号,它代表了段地址。所以指令"mov ax,data”的含义就是将名称为“data”的段的段地址送入ax。一个段中的数据的段地址可由段名代表,偏移地址就要看它在段中的位置了。程序中"data”段中的数据"0abch”的地址就是: data:6。
(3)“代码段”、“数据段”、“栈段”完全是我们的安排
我们在源程序中为这3个段起了具有含义的名称,用来放数据的段我们将其命名为“data” ,用来放代码的段我们将其命名为"code” ,用作栈空间的段命名为“stack”。我们这样命名,仅仅是为了使程序便于阅读。这些名称同“start”、“s”、"s0”等标号一样,仅在源程序中存在, CPU并不知道它们。
我们在源程序中用伪指令"assume cs:code,ds:data,ss:stack”将cs、ds和ss分别和code, data、 stack段相连。这样做了之后, CPU是否就会将cs指向code, ds指向data,ss 指向 stack,从而按照我们的意图来处理这些段呢?当然也不是,要知道assume是伪指令,是由编译器执行的,也是仅在源程序中存在的信息,CPU并不知道它们。我们不必深究 assume的作用,只要知道需要用它将你定义的具有一定用途的段和相关的寄存器联系起来就可以了。
设置 ss 指向 stack,设置 ss:sp 指向 stack:20, CPU 执行这些指令后,将把 stack 段当做栈空间来用。CPU若要访问data段中的数据,则可用ds指向data段,用其他的寄存器(如bx)来存放data段中数据的偏移地址。
总之,CPU 到底如何处理我们定义的段中的内容,是当作指令执行,当作数据访问,还是当作栈空间,完全是靠程序中具体的汇编指令,和汇编指令对CS:IP、SS:SP、DS等寄存器的设置来决定的。
参考文档: