包含多个段的程序---汇编学习笔记

包含多个段的程序

要使用一段安全的空间,第五章中我们说0:200~0:2ff是相对安全的。可这段代码容量只有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   ;dw代表是字型数据

    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

编译、连接这段程序后,我们看到如下:
包含多个段的程序---汇编学习笔记_第1张图片
显然,我们程序从cs:ip开始,而cs开始部分就是数据部分。明显是我们程序在代码段的dw影响了这段代码开始执行的位置(更准确的说,是我们没有定义数据段)。

那么,我们怎样让程序正确的(或说cs:ip)指向程序入口呢?如下:

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

可执行文件中的程序执行过程如下:

  1. 由其他的程序(Debug、command或其他程序)将可执行文件中的程序加载入内存;
  2. 设置CS:IP指向程序的第一条要执行的指令(即程序的入口),从而使程序得以运行;
  3. 程序运行结束后,返回到加载者。

现在的问题是:我们由什么来判定程序入口呢?具体框架如下
包含多个段的程序---汇编学习笔记_第2张图片


6.2 在代码段中使用栈

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

assume cs:code
code segment

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

        ?   

        mov ax,4c00h
        int 21h

code ends
end

程序思路:定义的数据存放在cs:0~f单元中,我们只要将数据依次入栈,再依次出栈就可以逆序存放了。

问题是:我们首先要有一段可当作栈的内存空间。如下:

assume cs:code
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
        ;用dw定义16个字型数据,在程序加载后,将取得16个字的
        ;内存空间,存放这16个数据。后面的程序段中将这段空间
        ;当作栈来使用

start:  mov ax,cs
        mov ss,ax
        mov sp,30h  ;将设置栈顶ss:sp指向cs:30

        mov bx,0
        mov cx,8

s:      push cs:[bx]
        add bx,2
        loop s      ;将代码段0~15单元中的8个字型数据依次入栈

        mov bx,0
        mov cx,8

s0:     pop cs:[bx]
        add bx,2
        loop s0

        mov ax,4c00h
        int 21h

code ends
end start

自此,我们的程序段定义并且使用了,发现很麻烦。

检测点 6.1

(1)下面程序实现依次使用内存0:0~0:15单元中的内容改写程序中的呃数据,完成程序:

assume cs:code
code segment

        dw 0123h,0456h,0789h,0abch,0defh,0fedh,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

code ends
end start

(2)下面的程序实现依次内存0:0~0:15单元中的内容改写程序中的数据,数据的传送用栈来进行。栈空间设置在程序内。完成程序:

assume cs:code
code segment

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

        dw 0,0,0,0,0,0,0,0

start:  mov ax,cs   ;题目ax后面为__
        mov ss,ax
        mov sp,20h  ;题目ss后面为__

        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

code ends
end start

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

在前面的内容中,我们在程序中用到了数据和栈,将数据、栈和代码都放在了一个段里面。

这样做显然有两个问题:
(1)把它们放在一个段中使程序显得混乱
(2)前面程序中处理的数据很少,用到的栈空间也很小,加上没有很长的代码,放到一个段里面没有问题。但如果数据、栈和代码需要的空间超过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      ;设置栈顶ss:sp指向stack:20

        mov ax,data
        mov ds,ax
        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)
(3)“代码段”、“数据段”、“栈段”完全是我们的安排

  1. 我们在源程序中为这三个命名为code、stack、data
  2. 我们在源程序中用伪指令“assume cs:code,ds:data,ss:stack”将它们关联起来
  3. 若要CPU按我们的安排行事,就要用机器指令控制它,源程序中的汇编指令是CPU要执行的内容。CPU如何知道执行它们?我们在源程序的最后用“end start”说明程序的入口,这个入口将被写入可执行文件描述信息,可执行文件的程序被加载入内存后,CPU的CS:IP被设置指向这个入口,从而开始执行程序中的第一条指令。

实验 5 编写、调试具有多个段的程序

(1)将下面的程序编译连接,用Debug加载、跟踪,然后回答这个问题。

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
stack ends

code segment
start:  mov ax,stack
        mov ss,ax
        mov sp,16

        mov ax,data
        mov ds,ax

        push ds:[0]
        push ds:[2]
        pop ds:[2]
        pop ds:[0]

        mov ax,4c00h
        int 21h

code ends
end start

1.CPU执行程序,程序返回前,data段的数据为多少?
这里写图片描述
2.CPU执行程序,程序返回前,cs=?、ss=?、ds=?。
这里写图片描述
3.设置程序加载后,code段的段地址为X,则data段的段地址为?,stack段的段地址为?。
code=X、data=X-20、stack=X-10。

(2)将下面的程序编译连接,用Debug加载、跟踪,然后回答问题。

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

data segment
    dw 0123h,0456h
data ends

stack segment
    dw 0,0
stack ends

code segment
start:  mov ax,stack
        mov ss,ax
        mov sp,16

        mov ax,data
        mov ds,ax

        push ds:[0]
        push ds:[2]

        pop ds:[2]
        pop ds:[0]

        mov ax,4c00h
        int 21h

code ends
end start

1.CPU执行程序,程序返回前,data段中的数据为多少?
这里写图片描述
2.CPU执行程序,程序返回前,cs=?、ss=?、ds=?。
这里写图片描述
3.设程序加载后,code段的段地址为X,则data段的段地址为?,stack段的段地址为?。
cs = X , ds = X-20, ss = X-10。
4.对于如下定义的段:
name segment
···
name ends
如果段中的数据占N个字节,则程序加载后,该段实际占有的空间为?。
N/256取上整

(3)将下面的程序编译连接,用Debug加载、跟踪,然后回答问题。

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

code segment
start:  mov ax,stack
        mov ss,ax
        mov sp,16

        mov ax,data
        mov ds,ax

        push ds:[0]
        push ds:[2]
        pop ds:[2]
        pop ds:[2]

        mov ax,4c00h
        int 21h

code ends

data segment
    dw 0123h,0456h
data ends

stack segment
    dw 0,0
stack ends

end start

1.CPU执行程序,程序返回前,data段中的数据为多少?
这里写图片描述
2.CPU执行程序,程序返回前,cs=?、ss=?、ds=?。
这里写图片描述
3.设程序加载后,code段的段地址为X,则data段的段地址为?,stack的段地址为?。
code=X , data=X+(code指令字节数/256)取上整+1 , stack = data + 1。

(4)如果将(1)(2)(3)题中的最后一条伪指令“end start”改为“end”(也就是说,不指名程序的入口),则哪个程序仍然可以正确执行?请说明原因。

第3个可以,首先是指令从何处开始,3可以正确的指向指令开始出,而其他两个不行;其次,是指令结束,3个都可以正常结束,因为只要遇到mov ax,4c00h和int 21h执行完后,程序就返回了。

(5)程序如下,编写code段中的代码,将a段和b段中的数据依次相加,将结果存到c段中。

assume cs:code

a segment
    db 1,2,3,4,5,6,7,8
a ends

b segment
    db 1,2,3,4,5,6,7,8
b ends

c segment
    db 0,0,0,0,0,0,0,0
c ends
;题目code需要自己编写
code segment
start:
    mov ax,c
    mov ds,ax
    mov bx,0

    mov ax,a
    mov es,ax

    mov cx,8

s:  mov dl,0
    add dl,es:[bx]
    add dl,es:[bx+16]
    mov [bx],dl
    inc bx
    loop s

    mov ax,4c0h
    int 21h

code ends
end start

实验结果如下:
包含多个段的程序---汇编学习笔记_第3张图片
(6)程序如下,编写code段中的代码,用push指令将a段中的前8个字型数据,逆序存储到b段中。

assume cs:code

a segment
    dw 1,2,3,4,5,6,7,8,9,0ah,0bh,0ch,0dh,0fh,0ffh
a ends

b segment
    dw 0,0,0,0,0,0,0,0
b ends
;题目代码段需要自己编写
code segment
start:
    mov ax,b
    mov ss,ax
    mov sp,16

    mov ax,a
    mov ds,ax
    mov bx,0

s:  push [bx]
    add bx,2
    loop s

    mov ax,4c00h
    int 21h

code ends
end start

实验结果如下:
包含多个段的程序---汇编学习笔记_第4张图片


本章完

你可能感兴趣的:(汇编语言)