1. 程式人生 > >實驗5 —— 編寫、除錯具有多個段的程式

實驗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