汇编语言(王爽第三版) 实验5编写、调试具体多个段的程序
?
參考:http://blog.sina.com.cn/s/blog_171daf8e00102xclx.html
匯編語言實(shí)驗(yàn)答案 (王爽):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?
?
程序分析:由于是初次接觸,我們逐步講解,廢話多點(diǎn)。
?
(1)此程序考察的是內(nèi)存中數(shù)據(jù)段和棧段的定義。? ??
???程序共定義了 3?個(gè)段(依次?是?數(shù)據(jù)段、棧段、代碼段。注意?段的前后順序)
將此程序編譯并連接后,使用 debug 調(diào)試,(這里需要注意,以下的段地址可能由于系統(tǒng)不同而有差異,主要是理解概念。)
C:\huibian>debug shiyan_5.exe?然后執(zhí)行 r?命令:
程序分析:我們什么也沒執(zhí)行,此時(shí)我們在 data段 定義的數(shù)據(jù)在哪 ?
在 ds:0100H 處 ( 原來講過,程序最開始時(shí) ds:00~ds:100H 是留給程序與操作系統(tǒng)通訊使用的 psp內(nèi)存段,參見書中p92),也就是說我們在 ds:100H、0760:100H ( 因?yàn)?ds?是 0760,所以 0760:100H ) 或 076F:00 處可以看見這些定義的數(shù)據(jù)。見下圖。
-d ds:100
?
(2)mov ax,stack
? ? ? ? ?mov ss,ax
? ? ? ? ?mov sp,16
? ? ? ?直到這3個(gè)指令執(zhí)行完畢,此時(shí)stack數(shù)據(jù)段被人工指定為了棧結(jié)構(gòu),(ss)=offset stack,也就是說此時(shí)ss段寄存器變量才賦值為stack段的段地址。sp指針指向了棧頂。
我們在上圖中,看到 SS=076F,執(zhí)行完這3個(gè)指令后,我們發(fā)現(xiàn) SS=0771 ,我們使用 d 命令查詢下:
我們定義的數(shù)據(jù)在內(nèi)存中的位置在程序裝載后,位置是固定的,也就是說數(shù)據(jù)段的物理地址一直是固定的,只不過我們表述這個(gè)數(shù)據(jù)段時(shí),采用了不同的段地址和偏移地址。
我們將 ss 指向了 stack 段內(nèi)存,也就是說,stack 這個(gè)內(nèi)存段從現(xiàn)在開始被人工的當(dāng)做了棧空間使用。在這16個(gè)字節(jié)空間里,原來都是00;為什么現(xiàn)在有其他數(shù)據(jù)了?這個(gè)我們先別管。它是一些其他的有用信息。
?
(3) mov ax,data
? ? ? ? ? mov ds,ax
直到上面2個(gè)指令執(zhí)行完畢,ds 段寄存器的值才是 offset data,也就是說此時(shí)ds指向了data段,ds:[0] 和 data:[0] 是等價(jià)的。
此時(shí)的段地址存儲(chǔ)在ds中;也是默認(rèn)的段地址寄存器;內(nèi)存單元表示直接使用 [idata] 尋址就行,也可以使用 ds:[idata]。[0]代表第一個(gè)內(nèi)存單元地址;[2]代表第三個(gè)內(nèi)存單元地址。
同理:我們執(zhí)行這二個(gè)指令后,將ds指向了data段。
?
(4)?push ds:[0]
? ? ? ?指令含義:將 data 段中從第一個(gè)內(nèi)存單元地址開始,按照字單元(2個(gè)字節(jié)),壓棧到ss棧(或stack棧中);通俗的講,就是將 23 01 這二個(gè)字節(jié)按字為單元壓棧。此時(shí)sp變量有變化,原來sp=0010H(16),壓棧后:(sp)=(sp) - 2 = 16 - 2 = 000EH。
也就是說棧頂改變了。(這個(gè)變化,你可以使用debug中的t命令一步一步的執(zhí)行后查看)。此時(shí)我們查看下棧中有變化嗎?
-d ss:0
我們發(fā)現(xiàn)棧中確實(shí)存儲(chǔ)了 01 23 這2個(gè)數(shù)據(jù),而且明確了棧空間結(jié)構(gòu)是從高地址向低地址發(fā)展的。至于棧中其他數(shù)據(jù),我們不必理會(huì)。???
?push ds:[2]
???????指令含義:同理,將data段中從第三個(gè)內(nèi)存單元地址開始,按照字單元(2個(gè)字節(jié)),壓棧到ss棧(或stack棧中);通俗的講,就是將56 04這二個(gè)字節(jié)按字為單元壓棧。此時(shí)sp變量有變化,原來sp=000EH(14);壓棧后(sp)=(sp)-2=14-2=000CH。也就是說棧頂改變了SP=000C。
-d ss:0
?
(5)pop ds:[2]
???????指令含義:將棧中數(shù)據(jù)按字彈出,寫入到段地址是ds(它的值是offset data或在我們的系統(tǒng)中是DS=0B65),偏移地址是[2]的內(nèi)存單元中。如果默認(rèn)段地址是ds,此指令直接可以寫成:pop [2]
???????指令執(zhí)行后:sp值有變化,因?yàn)槭菑棾鲆粋€(gè)字,故(sp)=(sp)+2??=000CH+2=000EH。也就是說棧頂指針sp指向有變化了。
? ? ? ?這里注意棧空間中存儲(chǔ)棧幀的順序,也是在以后使用棧結(jié)構(gòu)時(shí)候需要注意的原則:先進(jìn)后出;后進(jìn)先出。
???????我們查看下data段數(shù)據(jù)變化。
其實(shí)在內(nèi)存第3、4字節(jié)中是pop彈棧回寫的數(shù)據(jù)。實(shí)際是沒有變化,但是經(jīng)過了pop的回寫的。
??????pop ds:[0]
????指令含義:同理如上面,不多說了。
總結(jié):觀察棧的結(jié)構(gòu),注意執(zhí)行push和pop指令的匯編層面含義和CPU執(zhí)行的步驟。進(jìn)一步理解內(nèi)存的直接尋址方式。返回前,各寄存器狀態(tài)如下:???
①CPU執(zhí)行程序,程序返回前,data段中的數(shù)據(jù)?不變?。
②CPU執(zhí)行程序,程序返回前,CS=0772,SS=0771,DS=0770?。(根據(jù)自己系統(tǒng)回答)
③設(shè)程序加載后,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程序分析:(不再詳細(xì)分析了)
???????首先明確:雖然我們在 data段 和 stack段 中只定義初始化了4個(gè)字節(jié)的內(nèi)存,但在匯編中,直接給你分配了16個(gè)字節(jié)的空間,不足的按00補(bǔ)全。
???????結(jié)論:數(shù)據(jù)段和棧段在程序加載后實(shí)際占據(jù)的空間都是以16個(gè)字節(jié)為單位的。如果不足,以0補(bǔ)全填充。
?
??在debug中查看:-d ds:100 (?不知道為什么?ds:100?的?往上看前面解釋)
g 1d?執(zhí)行到?cs:1d?位置,程序中就是?mov ax, 4C00h
答案:
(1)CPU執(zhí)行程序,程序返回前,data段中的數(shù)據(jù)為多少?
? ? ? ? ? ?執(zhí)行程序后,data段有16個(gè)字節(jié)空間,前兩個(gè)字?jǐn)?shù)據(jù)不變,其余為00補(bǔ)全了。
(2)CPU執(zhí)行程序,程序返回前,CS=0772, SS=0771, DS=0770.
(3)程序加載后,code段地址設(shè)為X,則data段地址為(x-2),stack段的段地址為(X-1).
(4)對于如下定義的段:
? ? ? ? ? ? name segment
? ? ? ? ? ? ? ? ? ? ......
? ? ? ? ? ? name ends
? ? ? ?如果段中數(shù)據(jù)位 N 個(gè)字節(jié),程序加載后,該段實(shí)際占據(jù)空間為:(N/16的取整數(shù)+1)*16個(gè)字節(jié),如果 N小于16,那么實(shí)際占用16個(gè)字節(jié)(理解這個(gè)小問題);如果N大于16,那么實(shí)際占用(N/16的取整數(shù)+1)*16個(gè)字節(jié)。其實(shí)都是這個(gè)公式。
?
?
?
三。將下面的程序編譯連接,用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 段后面了。那么就要注意它們段地址的變化了。
程序返回前查看(?程序執(zhí)行結(jié)束前 )??
總結(jié):在匯編源代碼中,我們定義的 code 是程序執(zhí)行的代碼(它存儲(chǔ)在一個(gè)我們?nèi)藶橐?guī)定的段code中,在程序裝載時(shí),分配空間,并將機(jī)器碼寫入到這段內(nèi)存中);其他的數(shù)據(jù)段(無論是邏輯上的stack段,data段等)與代碼段都相鄰。只不過是裝載、分配內(nèi)存前后的問題。
?
答案:
(1)CPU執(zhí)行程序,程序返回前,data段中的數(shù)據(jù)為多少?
? ? ? ? ? ?執(zhí)行程序后,data段有16個(gè)字節(jié)空間,前兩個(gè)字?jǐn)?shù)據(jù)不變,其余為00補(bǔ)全了。
(2)CPU執(zhí)行程序,程序返回前,CS=0B65, SS=0B69, DS=0B68.
(3)程序加載后,code段地址設(shè)為X,則 data 段地址為(x+3),stack段的段地址為(X+4)。
(為什么是這樣?怎么計(jì)算的?看cx,程序加載時(shí),我們發(fā)現(xiàn)cx=0044,含義:此程序所有機(jī)器碼占用的空間是44H=68字節(jié)(cx?指示?程序機(jī)器碼占用空間的大小),data 和 stack 由于定義的都是小于16個(gè)字節(jié),一律按照16個(gè)字節(jié)分配空間,其余補(bǔ)00;剩余的36個(gè)字節(jié)就是code段真正的可執(zhí)行的機(jī)器碼。由于code段不足48個(gè)字節(jié)(3*16),故程序加載時(shí)也補(bǔ)0了)
? ? ? ?我們可以使用debug看看:-d cs:0
?
?
?
四。?如果將(1)、(2)、(3)題中的最后一條偽指令“end start”改為“end”(也就是說不指明程序的入口),則那個(gè)程序仍然可以正確執(zhí)行?請說明原因。
?
???????答案:如果不指名程序的(code段的)入口,并且使用 end 替換 end start,都能正常運(yùn)行。但只有(3)題中程序可以正確的執(zhí)行(因?yàn)橹挥兴窃趦?nèi)存中可執(zhí)行代碼在最前面)。
???????講解:因?yàn)槿绻恢该肟?#xff0c;程序會(huì)從加載進(jìn)內(nèi)存的第一個(gè)單元起開始執(zhí)行,前二個(gè)題中,定義的是數(shù)據(jù),但CPU還是將數(shù)據(jù)當(dāng)做指令代碼執(zhí)行了。只不過程序執(zhí)行時(shí)邏輯上是錯(cuò)誤了。但真的能執(zhí)行的。
???????如果指明了程序的入口,CPU會(huì)直接從入口處開始執(zhí)行真正的機(jī)器碼,直到遇到中斷指令返回。此種方式能夠確保程序邏輯上的正確。因此有必要為程序來指明入口。
???????網(wǎng)上許多答案都是不太明確!
?
?
?
五。編寫 code?段中的代碼,將 a段 和 b段 數(shù)據(jù)依次相加,結(jié)果存入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)這個(gè)題目一下子搞出3個(gè)數(shù)據(jù)段了。呵呵,貌似我們段寄存器不夠用了。cs(代碼段),ss(棧段),這二個(gè)千萬別碰!那只有ds和es了。思路:將a和b段我們用一個(gè)段地址表示,存儲(chǔ)在ds中;c段我們存儲(chǔ)在es中。?這種方式好嗎?不太好。
???????(2)上面已經(jīng)體會(huì)了,當(dāng)一個(gè)數(shù)據(jù)段不足16個(gè)字節(jié)時(shí),按16個(gè)字節(jié)分配內(nèi)存空間,其余的補(bǔ)0。我們發(fā)現(xiàn)a、b段都是定義了8個(gè)字節(jié)的數(shù)值。并且是相鄰的(肯定是的),那么a段的地址我們使用[bx+idata]表示,b段我們也使用[bx+idata]表示。這種方式?jīng)]有把a(bǔ)段和b段分開。
???????(3)最終決定:將es指向c段,ds分開分別的指向a段和b段,這樣我們在一個(gè)循環(huán)內(nèi)完成所有的工作了;程序中使用了棧保存了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 ;計(jì)算8次,故計(jì)數(shù)器為8s:mov dl, [bx] ;將ds:[bx]內(nèi)存單元按字節(jié)送入dl,此循環(huán)用到axadd dl, es:[bx] ;將ds:[bx]與es:[bx]內(nèi)存單元值相加push ds ;保護(hù)ds值,因?yàn)橄旅嬗玫絛s了mov ax, cz ;我的編譯器不認(rèn)C這個(gè)段的標(biāo)號,故改成了CZmov ds, ax ;將ds指向cz段mov [bx], dl ;將dl(a和b相對應(yīng)內(nèi)存單元內(nèi)容之和)寫入cz中pop ds ;將ds恢復(fù)inc bx ;bx遞增loop smov ax,4c00hint 21hcode endsend start結(jié)果分析:
???????(1)ds段寄存器在程序中可以存儲(chǔ)不同的內(nèi)存段的段地址,并不是唯一存儲(chǔ)一個(gè)段地址,es也是如此。
???????(2)合理利用系統(tǒng)自動(dòng)創(chuàng)建的棧空間,利用棧空間來保存暫存的數(shù)據(jù)。注意壓棧和彈棧的順序,確保操作的是一個(gè)數(shù)據(jù)對象。
???????(3)在遇到多個(gè)數(shù)據(jù)段的情況下,這種方式可以利用一個(gè)段寄存器來對多個(gè)內(nèi)存段尋址。
???????(4)在實(shí)際工程中,在程序中保存的數(shù)據(jù),都是程序的一些必須的初始化的數(shù)據(jù),其他的數(shù)據(jù)都應(yīng)保存在磁盤文件中,需要時(shí)才讀入內(nèi)存中。此例中的a、b、cz段都是其他的數(shù)據(jù),在這里就是演示。
?
?
?
六。編寫code段中代碼,用push指令將a段中前8個(gè)字型數(shù)據(jù)逆序存儲(chǔ)到b段中。
?
程序分析:
???????(1)理解掌握棧的原理,先進(jìn)后出,從高地址向低地址發(fā)展。也就是說先壓棧的數(shù)據(jù),在棧底,最后被pop出。
???????(2)對于數(shù)據(jù)段,我們定義2個(gè),ds指向a段,ss指向b。ss指向了b段,也就意味著b段是人工創(chuàng)建的一個(gè)棧結(jié)構(gòu)了。
???????(3)對于push和pop指令:操作的是一個(gè)棧幀或棧單元,它的操作數(shù)是一個(gè)字,在8086CPU中是一個(gè)字,2個(gè)字節(jié),這個(gè)在a、b段定義時(shí)我們應(yīng)該發(fā)現(xiàn),它們都是定義的字。如果定義的是db字節(jié)呢?呵呵。一樣的。
匯編代碼:
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段是個(gè)棧結(jié)構(gòu)了。mov bx,0mov cx,8 ;循環(huán)讀取a段8次,因?yàn)槭乔?個(gè)字 s:push ds:[bx] ;直接將a段中的字單元內(nèi)存壓棧即可。這樣在棧中的存儲(chǔ)結(jié)構(gòu)就是逆序的add bx,2loop smov ax,4c00hint 21h code ends end start運(yùn)行結(jié)果 debug:-d ds:0
?
?
?
總結(jié)
以上是生活随笔為你收集整理的汇编语言(王爽第三版) 实验5编写、调试具体多个段的程序的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: nessus 漏洞扫描器
- 下一篇: 轻量级分布式任务调度平台 XXL-JOB