【汇编语言】包含多个段的程序(二)—— 将数据、代码、栈放入不同的段-LMLPHP

前言

1. 存在的两个问题

在前面的内容中,我们在程序中用到了数据和栈,将数据、栈和代码都放到了一个段里面。我们在编程的时候要注意何处是数据,何处是栈,何处是代码。

这样做显然有两个问题:

(1)把它们放到一个段中使程序显得混乱。

(2)前面程序中处理的数据很少,用到的栈空间也小,加上没有多长的代码,放到一个段里面没有问题。但如果数据、栈和代码需要的空间超过64KB,就不能放在一个段中(一个段的容量不能大于 64KB,是我们在学习中所用的8086模式的限制,并不是所有的处理器都这样)。

2. 解决办法

所以,应该考虑用多个段来存放数据、代码和栈。

怎样做呢?

3. 示例代码

具体做法如下面的程序所示,这个程序将数据、栈和代码放到了不同的段中。

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		;设置栈顶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 2lh
code ends

end start				

3.1 程序说明

下面对这段程序进行说明。

3.1.1 定义多个段的方法

这点,我们从程序中可明显地看出,定义一个段的方法和前面所讲的定义代码段的方法没有区别,。

3.1.2 对段地址的引用

现在,程序中有多个段了。

如何访问段中的数据呢?

当然要通过地址,而地址是分为两部分的,即段地址和偏移地址

如何指明要访问的数据的段地址呢?

在程序中,段名就相当于一个标号,它代表了段地址。所以指令“mov ax,data”的含义就是将名称为“data”的段的段地址送入 ax。。程序中“data”段中的数据“0abch”的地址就是:data:6。要将它送入bx中,就要用如下的代码:

mov ax,data
mov ds,ax
mov bx,ds:[6]

我们不能用下面的指令:

mov ds,data
mov bx,ds:[6]

其中指令“mov ds,data”是错误的,。程序中对段名的引用,如指令“mov ds,data”中的“data”,将被编译器处理为个表示段地址的数值。

3.1.3 各种段完全是我们的安排

现在,我们以一个具体的程序来再次讨论一下所谓的“代码段”、“数据段”、“栈段”。在汇编源程序中,可以定义许多的段,比如在上面的示例程序中,定义了3个段,“code”、“data”和“stack”。我们可以分别安排它们存放代码、数据和栈。

那么我们如何让CPU按照我们的这种安排来执行这个程序呢?

下面来看看源程序中对这3个段所做的处理。

(1)

我们在源程序中为这3个段起了具有含义的名称。

  • 用来放数据的段我们将其命名为“data”

  • 用来放代码的段我们将其命名为“code”

  • 用作栈空间的段命名为“stack”

这样命名了之后,CPU是否就去执行“code”段中的内容,处理“data”段中的数据,将“stack”当做栈了呢?

当然不是,我们这样命名,仅仅是为了使程序便于阅读。。

(2)

我们在源程序中用伪指令“assume cs:code,ds:data,ss:stack”将cs、ds和ss分别和code、data、stack 段相连。

这样做了之后,CPU是否就会将cs指向code,ds 指向 data,ss指向 stack,从而按照我们的意图来处理这些段呢?

当然也不是,要知道assume是伪指令,是由编译器执行的,也是仅在源程序中存在的信息,CPU并不知道它们。我们不必深究assume的作用,只要知道需要用它将你定义的具有一定用途的段和相关的寄存器联系起来就可以了。

(3)

若要CPU按照我们的安排行事,就要用,源程序中的汇编指令是CPU要执行的内容。

CPU如何知道去执行它们?

我们在源程序的最后用“end start”说明了程序的入口,这个入口将被写入可执行文件的描述信息,可执行文件中的程序被加载入内存后,CPU的CS:IP被设置指向这个入口,从而开始执行程序中的第一条指令。。

我们在code 段中,使用指令:

mov ax,stack
mov ss,ax
mov sp,20h

设置 ss 指向stack,设置ss:sp指向stack:20,。CPU若要访问data段中的数据,则可,用其他的寄存器(如bx)来存放 data 段中数据的偏移地址。

4. 总结

总之,CPU到底如何处理我们定义的段中的内容,是当作指令执行,当作数据访问,还是当作栈空间,完全是靠程序中具体的汇编指令,和汇编指令对CS:IP、SS:SP、DS等寄存器的设置来决定的。

完全可以将之前的示例程序写成下面的样子,实现同样的功能。

assume cs:b,ds:a,ss:c
a segment
	dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
a ends

c segment
	dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
c ends

b segment
d:		mov ax,c
		mov ss,ax
		mov sp,20h		;希望用c段当作栈空间,设置ss:sp指向c:20

		mov ax,a
		mov ds,ax		;希望用ds:bx访问a段中的数据,ds指向a段
		
		mov bx,0		;ds:bx指向a段中的第一个单元		
		mov cx,8
	s:	push [bx]
		add bx,2
		loop s			;以上将a段中的0~15单元中的8个字型数据依次入栈

        mov bx,0
        mov cx,8
   s0:  pop [bx]
        add bx,2
        loop s0			;以上依次出栈8个字型数据到a段的0~15单元中
       
        mov ax,4c00h
        int 2lh
b ends

end d					;d处是要执行的第一条指令,即程序的入口

结语

今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下。

也可以点点关注,避免以后找不到我哦!

Crossoads主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是作者前进的动力!

【汇编语言】包含多个段的程序(二)—— 将数据、代码、栈放入不同的段-LMLPHP

11-15 13:58