實驗5 —— 編寫、除錯具有多個段的程式
阿新 • • 發佈:2018-11-25
本實驗為《組合語言》(王爽著,第3版)第133頁 實驗 5
將下面的程式編譯、連線,用
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
個記憶體單元反彙編即可得知返回指令的地址,如下圖所示:
- 使用
g
命令執行到返回指令之前,如下圖所示:
- 使用
d
命令檢視資料段的值,如下圖所示:
資料段的值是和執行前一樣的,該程式並沒有資料段的值。
程式返回前
cs
、ss
和ds
的值如下圖所示:
- 由以上實驗推斷,程式載入後,設程式碼段的段地址為 \(x\)
- 由
將下面的程式編譯、連線,用
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
。如下圖所示:
- 由於程式碼段的大小不會發生改變,可以推測資料段和棧段分別佔
10H
個位元組,且10H
個位元組是系統為資料段和棧段分配的單位最小空間。 - 現在用
debug
檢視資料段和棧段中的資料,如下圖所示 (反彙編步驟略):
- 使用
g
命令執行到返回指令之前,如下圖所示:
- 資料段和預期的那樣,沒有發生改變。但棧段的未使用部分發生了改變,依據
ss:a
到ss:d
的資料推測,這裡儲存了cs:ip
的歷史值 (cs:(ip - 1)
)。 - 程式返回前
cs
、ss
和ds
的值和上一個實驗一樣,cs
、ss
和ds
的關係也一樣,截圖略。 - 如果
10H
個位元組是系統為資料段和棧段分配的單位最小空間,設段中的資料佔 \(N\) 個位元組,則程式載入後,該段實際佔有空間為 $ \lfloor \dfrac{N+15} {16} \rfloor$。
- 按照上一個實驗的經驗,這次資料段和棧段應該佔用
將下面的程式編譯、連線,用
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
的值發現又不同了,如下圖所示:
- 這裡猜測系統通過某種方式為程式碼段分配了
10H
的整數倍位元組的空間,如下圖所示:
紅框、黃框和藍框分別對應程式碼段、資料段和棧段 - 可見程式碼段佔用了
30H
個位元組,但是這與實際的大小矛盾,根據該程式比之前的大了2
個位元組,我的猜測是一個位元組存放了補全的數 (這裡是 0),另一個位元組是補全的位數 (這裡是 14)。至於為什麼資料段和棧段沒有使用這種補全機制,大概是因為其中的資料是多變的,直接將空間確定下來可以便於後期對資料的增減,以及減小因越界產生的安全隱患;還有一種可能只是與資料段和棧段保持統一。 - 資料段中的資料保持不變,截圖略。
- 程式返回前
cs
、ss
和ds
的值如下圖所示:
- 程式載入後,設程式碼段的段地址為 \(x\),則資料段的段地址為 \(x+3\),棧段的段地址為 \(x+4\)。
- 理論上程式大小應該和前兩個一樣,但檢視
如果將 1. 、2. 、3. 題中的最後一條偽指令
end start
改為end
,3 個程式理論都能執行,但資料會被誤認為機器碼,可能因為非法語句導致系統報錯。沒有end start
也就沒有start
使得程式從頭執行,也就是說只有 3. 中的程式才能正確執行,因為程式碼段寫在了前面。編寫程式碼段中的程式碼,將
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
,反彙編得a
、b
和c
的段地址,如下圖所示:
- 檢視執行前
a
段、b
段和c
段的資料,如下圖所示:
- 返回前的資料如下圖所示:
編寫程式碼段中的程式碼,用
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
,反彙編得a
和b
的段地址,如下圖所示:
- 執行前和返回前的資料如下圖所示:
- 檢視