汇编语言个人学习笔记——第六章 包含多个段的程序

引言

前面我们写的程序中只有一个代码段。现在有一个问题是:

如果程序需要用其他空间来存放数据,我们要使用哪里?

第5章中,我们讲到要使用一段安全的空间。

我们说0:200~0:300是相对安全的;

可这段空间的容量只有256个字节,如果我们需要的空间超过256个字节怎么办?

下面我们将以这样的顺序来深入来深入地讨论多个段的问题:

(1)在一个段中存放数据、代码、栈

(2)将数据、代码、栈放入不同段中

 

6.1在代码段中使用数据

考虑这样一个问题,编程计算以下8个数据的和,结果存放在ax寄存器中:

0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H。

代码:

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

codeseg ends

end

 

解释一下,程序第一行中的“dw”的含义是ding定义字型数据。dw即define word。

(定义字节数据db,define bytes)

在这里我们使用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。(待上机验证)

程序中,我们用bx存放加2递增的偏移地址,用循环来进行累加。

在循环开始前,设置(bx)=0,cs:bx指向第一个数据所在的字单元。

每次循环中(bx)=(bx)+2,cs:bx指向下一个数据所在的字单元。

将前面的程序编译连接为可执行文件p61.exe,先不要运行,我们用debug加载查看下。

汇编语言个人学习笔记——第六章 包含多个段的程序_第1张图片

可以看到,cs:0~cs:f处的几个数据刚好是前面设置的几个数据。

汇编语言个人学习笔记——第六章 包含多个段的程序_第2张图片

cs:0010之后便是代码。

如何让这个程序在编译后可以存在系统中直接运行呢?

我们可以在源程序中指明界序的入口所在,具体做法见下面的程序。

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

注意在上面程序中加入的新内容,我们在程序的第一条指令的前面加上了一个标号start,而这个标号在伪指令end的后面出现。

加入start可以让cpu不会读错,看一下原来没有加start的程序的调试结果:

汇编语言个人学习笔记——第六章 包含多个段的程序_第3张图片

我们会发现,在这里虽然已经大于16个字节了,但是根本就没有出现原来代码中的第一条指令。

当我们查看cs:0010时,就能看到第一条指令了

汇编语言个人学习笔记——第六章 包含多个段的程序_第4张图片

原因:CPU读取的是机器指令,CPU根据机器指令的不同组合会构成不同代码。

所以我们要给CPU指明一个程序的入口。

end的作用:

end除了通知编译器程序结束外,还可以通知编译器程序的入口在什么地方。

 

6.2在代码段中使用栈

完成下面的程序,利用栈,将程序中定义的数据逆序存放。

assume cs:code

code segment

 dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h

?

code ends

end

程序的思路大致如下:

程序运行时,定义的数据存放在cs:0~cs:15单元中,共8个字单元。依次将这8个字单元中的数据入栈,然后再依次出栈到这8个字单元中,从而实现数据的逆序存放。

问题是,我们首先要有一段可当作栈的内存空间。如前描述,这段空间应该由系统来分配。我们可以在程序中通过定义数据来取得一段空间,然后将这段空间当作栈空间来用。

代码如下:

assume cs:code
code segment
     dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h         ;用dw定义8个字型数据,程序加载后,将取得8个字的内存空间,存放这8个数据
     dw 0,0,0,0,0,0,0,0                                                                       ;我们在后面的程序中将这段空间当作栈来使用

start:mov ax,cs
      mov ss,ax
      mov sp,32                                                                                  ;设置栈顶ss:sp指向cs:32
      mov bx,0
      mov cx,8
    s:push cs:[bx]
      add bx,2
      loop s                                                                                          ;以上将代码段0~16单元中的8个字型数据依次入栈
      
      mov bx,0
      mov cx,8
    t:pop cs:[bx]
      add bx,2
      loop t                                                                                           ;以上依次出栈8个字型数据到代码段0~16单元中
      mov ax,4c00h
      int 21h
code ends
end start

我们要将cs:16~cs:31的内存空间当作栈来使用,初始状态栈为空,所以ss:sp要指向栈底,则设置ss:sp指向cs:32。

 

对于:

dw 0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H

我们可以说,定义了8个字型数据。

也可以说,开辟了8个字的内存空间,这段空间中每个字单元中的数据依次是:

0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H。

因为它们最终的效果是一样的。

先完成检测题,再进行后面的学习。

 

6.3将数据、代码、栈放入不同的段

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

这样会有两个问题:

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

(2)前面程序中处理的数据很少,用到的栈空间也小,加上没有多长的代码,放到一个段里面没有问题。

但如果数据、栈和代码需要的空间超过64KB,就不能放在一个段中(一个段的容量不能大于64KB,是我们在学习中用的8086模式的限制,并不是所有的处理器都这样)。

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

我们用和定义代码段一样的方法来定义多个段,然后在这些段里面定义需要的数据,或通过定义数据来取得栈空间。

具体做法如下面程序所示:

assume cs:code,ds:data,ss:stack

data segment
 dw 0123h,0456h,0789h,0abch,0defh,0987h
data ends

stack segment
 dw 0,0,0,0,0,0,0,0
stack ends
 
code segment
start :mov ax,stack
       mov ss,ax
       mov sp,16
       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
     t:pop [bx]
       add bx,2
       loop t
       
       mov ax,4c00h
       int 2h
code ends
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",将被编译器处理为一个表示段地址的数值。

“代码段”、“数据段”、“栈段”完全是我们的安排。

一、我们在源程序中为这三个段齐了具有含义的名称,用来存放数据的段我们将其命名为“data”,用来存放数据的段我们将其命名为“data”,用来放代码的段我们将其命名为“code”,用来作栈空间的命名为“stack”。

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

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

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

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段当作栈空间来使用。(stack通过编译器应该被翻译为一个地址)

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

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

你可能感兴趣的:(汇编语言个人学习笔记)