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

本实验为《汇编语言》(王爽著,第3版)第133页 实验 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

    编译和连接的步骤略。

    • r 命令查看 cx 的值得知,该程序总共占用 42H 个字节。现在扣除数据段和栈段的 16 个字数据 (共占用 20H 个字节) 可知代码段占用 22H 个字节。对 cs:ip 及其以后的 22H 个内存单元反汇编即可得知返回指令的地址,如下图所示:

    ex5_1@3x.png

    • 使用 g 命令执行到返回指令之前,如下图所示:

    ex5_2@3x.png

    • 使用 d 命令查看数据段的值,如下图所示:

    ex5_3@3x.png

    • 数据段的值是和运行前一样的,该程序并没有数据段的值。

    • 程序返回前 csssds 的值如下图所示:

    ex5_4@3x.png

    • 由以上实验推断,程序加载后,设代码段的段地址为 \(x\),则数据段的段地址为 \(x-2\),栈段的段地址为 \(x-1\)
  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
    

    编译和连接的步骤略。

    • 按照上一个实验的经验,这次数据段和栈段应该占用 8 个字节,但事实并没有这么简单。查看 cx 的值可见和先前的实验是一样的,都是 32H。如下图所示:

    ex5_5@3x.png

    • 由于代码段的大小不会发生改变,可以推测数据段和栈段分别占 10H 个字节,且 10H 个字节是系统为数据段和栈段分配的单位最小空间。
    • 现在用 debug 查看数据段和栈段中的数据,如下图所示 (反汇编步骤略):

    ex5_6@3x.png

    • 使用 g 命令执行到返回指令之前,如下图所示:

    ex5_7@3x.png

    • 数据段和预期的那样,没有发生改变。但栈段的未使用部分发生了改变,依据 ss:ass:d 的数据推测,这里存储了 cs:ip 的历史值 (cs:(ip - 1))。
    • 程序返回前 csssds 的值和上一个实验一样,csssds 的关系也一样,截图略。
    • 如果 10H 个字节是系统为数据段和栈段分配的单位最小空间,设段中的数据占 \(N\) 个字节,则程序加载后,该段实际占有空间为 $ \lfloor \dfrac{N+15} {16} \rfloor$。
  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:[0]
    
            mov ax,4c00h
            int 21h
    
    code ends
    data segment
      dw 0123h, 0456h
    data ends
    
    stack segment
      dw 0,0
    stack ends
    end start

    编译和连接的步骤略。

    • 理论上程序大小应该和前两个一样,但查看 cx 的值发现又不同了,如下图所示:

    ex5_8@3x.png

    • 这里猜测系统通过某种方式为代码段分配了 10H 的整数倍字节的空间,如下图所示:

    ex5_9@3x.png

    红框、黄框和蓝框分别对应代码段、数据段和栈段

    • 可见代码段占用了 30H 个字节,但是这与实际的大小矛盾,根据该程序比之前的大了 2 个字节,我的猜测是一个字节存放了补全的数 (这里是 0),另一个字节是补全的位数 (这里是 14)。至于为什么数据段和栈段没有使用这种补全机制,大概是因为其中的数据是多变的,直接将空间确定下来可以便于后期对数据的增减,以及减小因越界产生的安全隐患;还有一种可能只是与数据段和栈段保持统一。
    • 数据段中的数据保持不变,截图略。
    • 程序返回前 csssds 的值如下图所示:

    ex5_10@3x.png

    • 程序加载后,设代码段的段地址为 \(x\),则数据段的段地址为 \(x+3\),栈段的段地址为 \(x+4\)
  4. 如果将 1. 、2. 、3. 题中的最后一条伪指令 end start 改为 end,3 个程序理论都能执行,但数据会被误认为机器码,可能因为非法语句导致系统报错。没有 end start 也就没有 start 使得程序从头执行,也就是说只有 3. 中的程序才能正确执行,因为代码段写在了前面。

  5. 编写代码段中的代码,将 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 8 dup(0)
    c ends     
    code segment
    start:   mov ax, a
             mov ds, ax
    
             mov ax, b
             mov es, ax
    
             mov cx, 8
             mov bx, 0
    
    s:       mov dl, ds:[bx]     ; ds 可以省略不写
             add dl, es:[bx]
             mov ax, c
             push ds             ; 使用栈的特性来 "保护现场"
             mov ds, ax
             mov [bx], dl
             pop ds
             inc bx
             loop s
    
             mov ax, 4c00h
             int 21h
    code ends
    end start
    • 查看 cx 得程序所占空间,代码段的大小为 56H - 3 * 10H =26H,反汇编得 abc 的段地址,如下图所示:

    ex5_11@3x.png

    • 查看运行前 a 段、b 段和 c 段的数据,如下图所示:

    ex5_12@3x.png

    • 返回前的数据如下图所示:

    ex5_13@3x.png

  6. 编写代码段中的代码,用 push 指令将 a 段中的前 8 个字型数据,逆序存储到 b 段中。

    assume cs:code
    a segment
      dw 1, 2, 3, 4, 5, 6, 7, 8, 9, 0ah, 0bh, 0ch, 0dh, 0eh, 0fh, 0ffh
    a ends
    
    b segment
      dw 8 dup(0)
    b ends
    
    code segment
    start:  mov ax, a 
            mov ds, ax
    
            mov ax, b
            mov ss, ax       ; 利用栈先入后出的特性逆序存放到 b
            mov sp, 16
    
            mov bx, 0
            mov cx, 8
    
    s:      push ds:[bx]
            add bx, 2
            loop s
    
            mov ax, 4c00h
            int 21h
    code ends
    end start
    
    • 查看 cx 得程序所占空间,代码段的大小为 4FH - 20H - 10H =16H,反汇编得 ab 的段地址,如下图所示:

    ex5_14@3x.png

    • 运行前和返回前的数据如下图所示:

    ex5_15@3x.png

猜你喜欢

转载自www.cnblogs.com/jordangong/p/10016936.html
今日推荐