汇编语言(王爽第三版) 实验5编写、调试具体多个段的程序
?
參考:http://blog.sina.com.cn/s/blog_171daf8e00102xclx.html
匯編語言實驗答案 (王爽):https://wenku.baidu.com/view/a1cd7c6c1fb91a37f111f18583d049649b660ede.html
?
?
一。將下面的程序編譯連接,用Debug加載、跟蹤,然后回答問題。
?
匯編代碼:
assume cs:code,ds:data,ss:stackdata segmentdw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h data endsstack segmentdw 0,0,0,0,0,0,0,0 stack endscode segment start: mov ax,stackmov ss,axmov sp,16mov ax,datamov ds,axpush ds:[0]push ds:[2]pop ds:[2]pop ds:[0]mov ax,4c00hint 21h code ends end start?
?
程序分析:由于是初次接觸,我們逐步講解,廢話多點。
?
(1)此程序考察的是內存中數據段和棧段的定義。? ??
???程序共定義了 3?個段(依次?是?數據段、棧段、代碼段。注意?段的前后順序)
將此程序編譯并連接后,使用 debug 調試,(這里需要注意,以下的段地址可能由于系統不同而有差異,主要是理解概念。)
C:\huibian>debug shiyan_5.exe?然后執行 r?命令:
程序分析:我們什么也沒執行,此時我們在 data段 定義的數據在哪 ?
在 ds:0100H 處 ( 原來講過,程序最開始時 ds:00~ds:100H 是留給程序與操作系統通訊使用的 psp內存段,參見書中p92),也就是說我們在 ds:100H、0760:100H ( 因為?ds?是 0760,所以 0760:100H ) 或 076F:00 處可以看見這些定義的數據。見下圖。
-d ds:100
?
(2)mov ax,stack
? ? ? ? ?mov ss,ax
? ? ? ? ?mov sp,16
? ? ? ?直到這3個指令執行完畢,此時stack數據段被人工指定為了棧結構,(ss)=offset stack,也就是說此時ss段寄存器變量才賦值為stack段的段地址。sp指針指向了棧頂。
我們在上圖中,看到 SS=076F,執行完這3個指令后,我們發現 SS=0771 ,我們使用 d 命令查詢下:
我們定義的數據在內存中的位置在程序裝載后,位置是固定的,也就是說數據段的物理地址一直是固定的,只不過我們表述這個數據段時,采用了不同的段地址和偏移地址。
我們將 ss 指向了 stack 段內存,也就是說,stack 這個內存段從現在開始被人工的當做了棧空間使用。在這16個字節空間里,原來都是00;為什么現在有其他數據了?這個我們先別管。它是一些其他的有用信息。
?
(3) mov ax,data
? ? ? ? ? mov ds,ax
直到上面2個指令執行完畢,ds 段寄存器的值才是 offset data,也就是說此時ds指向了data段,ds:[0] 和 data:[0] 是等價的。
此時的段地址存儲在ds中;也是默認的段地址寄存器;內存單元表示直接使用 [idata] 尋址就行,也可以使用 ds:[idata]。[0]代表第一個內存單元地址;[2]代表第三個內存單元地址。
同理:我們執行這二個指令后,將ds指向了data段。
?
(4)?push ds:[0]
? ? ? ?指令含義:將 data 段中從第一個內存單元地址開始,按照字單元(2個字節),壓棧到ss棧(或stack棧中);通俗的講,就是將 23 01 這二個字節按字為單元壓棧。此時sp變量有變化,原來sp=0010H(16),壓棧后:(sp)=(sp) - 2 = 16 - 2 = 000EH。
也就是說棧頂改變了。(這個變化,你可以使用debug中的t命令一步一步的執行后查看)。此時我們查看下棧中有變化嗎?
-d ss:0
我們發現棧中確實存儲了 01 23 這2個數據,而且明確了棧空間結構是從高地址向低地址發展的。至于棧中其他數據,我們不必理會。???
?push ds:[2]
???????指令含義:同理,將data段中從第三個內存單元地址開始,按照字單元(2個字節),壓棧到ss棧(或stack棧中);通俗的講,就是將56 04這二個字節按字為單元壓棧。此時sp變量有變化,原來sp=000EH(14);壓棧后(sp)=(sp)-2=14-2=000CH。也就是說棧頂改變了SP=000C。
-d ss:0
?
(5)pop ds:[2]
???????指令含義:將棧中數據按字彈出,寫入到段地址是ds(它的值是offset data或在我們的系統中是DS=0B65),偏移地址是[2]的內存單元中。如果默認段地址是ds,此指令直接可以寫成:pop [2]
???????指令執行后:sp值有變化,因為是彈出一個字,故(sp)=(sp)+2??=000CH+2=000EH。也就是說棧頂指針sp指向有變化了。
? ? ? ?這里注意棧空間中存儲棧幀的順序,也是在以后使用棧結構時候需要注意的原則:先進后出;后進先出。
???????我們查看下data段數據變化。
其實在內存第3、4字節中是pop彈棧回寫的數據。實際是沒有變化,但是經過了pop的回寫的。
??????pop ds:[0]
????指令含義:同理如上面,不多說了。
總結:觀察棧的結構,注意執行push和pop指令的匯編層面含義和CPU執行的步驟。進一步理解內存的直接尋址方式。返回前,各寄存器狀態如下:???
①CPU執行程序,程序返回前,data段中的數據?不變?。
②CPU執行程序,程序返回前,CS=0772,SS=0771,DS=0770?。(根據自己系統回答)
③設程序加載后,CODE段的段地址為X,則DATA段的段地址為?X-2?,STACK段的段地址為?X-1?。
?
?
?
二。將下面的程序編譯連接,用Debug加載、跟蹤,然后回答問題。
?
匯編程序:
assume cs:code,ds:data,ss:stackdata segmentdw 0123h,0456h data endsstack segmentdw 0,0 stack endscode segment start:mov ax,stack mov ss,axmov sp,16 mov ax,data mov ds,axpush ds:[0]push ds:[2]pop ds:[2]pop ds:[0]mov ax,4c00hint 21h code ends end start程序分析:(不再詳細分析了)
???????首先明確:雖然我們在 data段 和 stack段 中只定義初始化了4個字節的內存,但在匯編中,直接給你分配了16個字節的空間,不足的按00補全。
???????結論:數據段和棧段在程序加載后實際占據的空間都是以16個字節為單位的。如果不足,以0補全填充。
?
??在debug中查看:-d ds:100 (?不知道為什么?ds:100?的?往上看前面解釋)
g 1d?執行到?cs:1d?位置,程序中就是?mov ax, 4C00h
答案:
(1)CPU執行程序,程序返回前,data段中的數據為多少?
? ? ? ? ? ?執行程序后,data段有16個字節空間,前兩個字數據不變,其余為00補全了。
(2)CPU執行程序,程序返回前,CS=0772, SS=0771, DS=0770.
(3)程序加載后,code段地址設為X,則data段地址為(x-2),stack段的段地址為(X-1).
(4)對于如下定義的段:
? ? ? ? ? ? name segment
? ? ? ? ? ? ? ? ? ? ......
? ? ? ? ? ? name ends
? ? ? ?如果段中數據位 N 個字節,程序加載后,該段實際占據空間為:(N/16的取整數+1)*16個字節,如果 N小于16,那么實際占用16個字節(理解這個小問題);如果N大于16,那么實際占用(N/16的取整數+1)*16個字節。其實都是這個公式。
?
?
?
三。將下面的程序編譯連接,用Debug加載、跟蹤,然后回答問題。
?
匯編代碼:
assume cs:code,ds:data,ss:stackcode segment start:mov ax,stack mov ss,axmov sp,16 mov ax,data mov ds,axpush ds:[0]push ds:[2]pop ds:[2]pop ds:[0]mov ax,4c00hint 21h code endsdata segmentdw 0123h,0456h data endsstack segmentdw 0,0 stack endsend start程序分析:這次只不過是將 data 和 stack 段放到了 code 段后面了。那么就要注意它們段地址的變化了。
程序返回前查看(?程序執行結束前 )??
總結:在匯編源代碼中,我們定義的 code 是程序執行的代碼(它存儲在一個我們人為規定的段code中,在程序裝載時,分配空間,并將機器碼寫入到這段內存中);其他的數據段(無論是邏輯上的stack段,data段等)與代碼段都相鄰。只不過是裝載、分配內存前后的問題。
?
答案:
(1)CPU執行程序,程序返回前,data段中的數據為多少?
? ? ? ? ? ?執行程序后,data段有16個字節空間,前兩個字數據不變,其余為00補全了。
(2)CPU執行程序,程序返回前,CS=0B65, SS=0B69, DS=0B68.
(3)程序加載后,code段地址設為X,則 data 段地址為(x+3),stack段的段地址為(X+4)。
(為什么是這樣?怎么計算的?看cx,程序加載時,我們發現cx=0044,含義:此程序所有機器碼占用的空間是44H=68字節(cx?指示?程序機器碼占用空間的大小),data 和 stack 由于定義的都是小于16個字節,一律按照16個字節分配空間,其余補00;剩余的36個字節就是code段真正的可執行的機器碼。由于code段不足48個字節(3*16),故程序加載時也補0了)
? ? ? ?我們可以使用debug看看:-d cs:0
?
?
?
四。?如果將(1)、(2)、(3)題中的最后一條偽指令“end start”改為“end”(也就是說不指明程序的入口),則那個程序仍然可以正確執行?請說明原因。
?
???????答案:如果不指名程序的(code段的)入口,并且使用 end 替換 end start,都能正常運行。但只有(3)題中程序可以正確的執行(因為只有它是在內存中可執行代碼在最前面)。
???????講解:因為如果不指名入口,程序會從加載進內存的第一個單元起開始執行,前二個題中,定義的是數據,但CPU還是將數據當做指令代碼執行了。只不過程序執行時邏輯上是錯誤了。但真的能執行的。
???????如果指明了程序的入口,CPU會直接從入口處開始執行真正的機器碼,直到遇到中斷指令返回。此種方式能夠確保程序邏輯上的正確。因此有必要為程序來指明入口。
???????網上許多答案都是不太明確!
?
?
?
五。編寫 code?段中的代碼,將 a段 和 b段 數據依次相加,結果存入c段
?
書上解題思路:使用?段?es?首先指向?a?段 ,ds?指向?c?段,a?段?和?c?段相加保存在?c?段,然后?es?再?指向?b ,b?段再?和?c?段相加保存在?c?段:
assume cs:code a segmentdb 1,2,3,4,5,6,7,8 a endsb segmentdb 1,2,3,4,5,6,7,8 b endsc segmentdb 0,0,0,0,0,0,0,0 c endscode segment start:mov ax,amov es,axmov ax,cmov ds,axmov bx,0mov cx,8 s1:mov ax,es:[bx]add[bx],axadd bx,2loop s1mov ax,b mov es,axmov ds,ax mov bx,0mov cx,8 s2:mov ax,es:[bx]add[bx],axadd bx,2loop s2mov ax,4c00hint 21h code ends end start?
程序分析:
???????(1)這個題目一下子搞出3個數據段了。呵呵,貌似我們段寄存器不夠用了。cs(代碼段),ss(棧段),這二個千萬別碰!那只有ds和es了。思路:將a和b段我們用一個段地址表示,存儲在ds中;c段我們存儲在es中。?這種方式好嗎?不太好。
???????(2)上面已經體會了,當一個數據段不足16個字節時,按16個字節分配內存空間,其余的補0。我們發現a、b段都是定義了8個字節的數值。并且是相鄰的(肯定是的),那么a段的地址我們使用[bx+idata]表示,b段我們也使用[bx+idata]表示。這種方式沒有把a段和b段分開。
???????(3)最終決定:將es指向c段,ds分開分別的指向a段和b段,這樣我們在一個循環內完成所有的工作了;程序中使用了棧保存了ds的值;
匯編代碼
assume cs:codea segmentdb 1,2,3,4,5,6,7,8 a endsb segmentdb 1,2,3,4,5,6,7,8 b endscz segmentdb 0,0,0,0,0,0,0,0 cz endscode segmentstart:mov ax,amov ds,ax ;ds指向a段mov ax,bmov es,ax ;es指向b段mov bx,0mov cx,8 ;計算8次,故計數器為8s:mov dl, [bx] ;將ds:[bx]內存單元按字節送入dl,此循環用到axadd dl, es:[bx] ;將ds:[bx]與es:[bx]內存單元值相加push ds ;保護ds值,因為下面用到ds了mov ax, cz ;我的編譯器不認C這個段的標號,故改成了CZmov ds, ax ;將ds指向cz段mov [bx], dl ;將dl(a和b相對應內存單元內容之和)寫入cz中pop ds ;將ds恢復inc bx ;bx遞增loop smov ax,4c00hint 21hcode endsend start結果分析:
???????(1)ds段寄存器在程序中可以存儲不同的內存段的段地址,并不是唯一存儲一個段地址,es也是如此。
???????(2)合理利用系統自動創建的棧空間,利用棧空間來保存暫存的數據。注意壓棧和彈棧的順序,確保操作的是一個數據對象。
???????(3)在遇到多個數據段的情況下,這種方式可以利用一個段寄存器來對多個內存段尋址。
???????(4)在實際工程中,在程序中保存的數據,都是程序的一些必須的初始化的數據,其他的數據都應保存在磁盤文件中,需要時才讀入內存中。此例中的a、b、cz段都是其他的數據,在這里就是演示。
?
?
?
六。編寫code段中代碼,用push指令將a段中前8個字型數據逆序存儲到b段中。
?
程序分析:
???????(1)理解掌握棧的原理,先進后出,從高地址向低地址發展。也就是說先壓棧的數據,在棧底,最后被pop出。
???????(2)對于數據段,我們定義2個,ds指向a段,ss指向b。ss指向了b段,也就意味著b段是人工創建的一個棧結構了。
???????(3)對于push和pop指令:操作的是一個棧幀或棧單元,它的操作數是一個字,在8086CPU中是一個字,2個字節,這個在a、b段定義時我們應該發現,它們都是定義的字。如果定義的是db字節呢?呵呵。一樣的。
匯編代碼:
assume cs:codea segmentdw 1,2,3,4,5,6,7,8,9,0ah,0bh,0ch,0dh,0eh,0fh,0ffh a endsb segmentdw 0,0,0,0,0,0,0,0 b endscode segment start:mov ax,amov ds,ax ;ds指向a段mov ax,bmov ss,ax ;ss指向了b段mov sp,16 ;初始化棧頂,ss:sp指向了棧頂,意味著b段是個棧結構了。mov bx,0mov cx,8 ;循環讀取a段8次,因為是前8個字 s:push ds:[bx] ;直接將a段中的字單元內存壓棧即可。這樣在棧中的存儲結構就是逆序的add bx,2loop smov ax,4c00hint 21h code ends end start運行結果 debug:-d ds:0
?
?
?
總結
以上是生活随笔為你收集整理的汇编语言(王爽第三版) 实验5编写、调试具体多个段的程序的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: nessus 漏洞扫描器
- 下一篇: C++ 基本数据类型 的 字节数