王德剛 朱鋒 王德強
摘要: 本文通過對.COM程式和.EXE程式載入時不同記憶體映象的研究進而得出:.COM程式只有一個物理段,段的最大長度為64KB:.COM程式只能從位移地址為100H處開始執行。DOS對.EXE檔案長度沒有約束,便於組織大型應用程式;以及.EXE檔案中用END啟動標號來說明啟動點,用PUSH DS來儲存程式段首碼的段地址,用SUB AX,AX 和 PUSH AX指令來儲存PSP中INT 20H指令首地址的必要性。從中還可看出:由於.COM程式比.EXE程式規模小而簡單,所以.COM程式裝入速度比.EXE程式裝入速度要快。為進一步發揮組合語言程式執行速度快的優勢提供參考。 在MS—DOS中,有兩種類型的可執行程式,即.COM程式和.EXE程式。8086/8088組合語言也有相應兩種程式結構的檔案,可分別編譯最終形成.COM程式和.EXE程式。而組合語言又是電腦能提供給使用者的最快而又最有效語言,因而在對於程式的空間和時間要求很高的場合,組合語言是必不可少的,所以從根本上闡明.EXE檔案和.COM檔案的程式結構特點,以及它們之間的區別,兩種程式的不同載入情形和程式段首碼結構特徵,是真正領會組合語言編程的必要條件。1 碼段的基本組成
對於1個組合語言小程式,可以不定義資料區段、附加段和堆棧段,但不能不定義程式碼片段,沒有碼段則不成為程式,所以首先來認識一下碼段的基本組成。
1.1 8086/8088組合語言程式的.EXE檔案(“標準序”)結構形式1
圖1 .EXE檔案的結構1.1.1 為了正確彙編,組譯工具在彙編時必須清楚程式中段的結構以及各種指令在執行時應該訪問哪一個段?即程式中段和段寄存器的關係,這可由ASSUME偽操作來實現。
格式為:ASSUME 段寄存器1:段名1,段寄存器2:段名2,…
如:ASSUME CS:CODE,DS:DATA,SS:STACK
這實際上就是說,碼段CODE的段地址值放在CS段寄存器中,資料區段DATA的段地址值放在DS段寄存器中,堆棧段STACK的段地址值放在SS段寄存器中。
1.1.2 ASSUME僅僅是指明段寄存器與段的關係,並未儲存段地址,所以如有資料區段、附加段、堆棧段,則必須儲存段地址。碼段不需儲存段地址,因為碼段的這一操作是在程式初始化時完成。
格式為: MOV AX,段名
MOV段寄存器,AX
MOVAX,DATA
如: MOVDS,AX
1.1.3 組合語言程式是DOS的一個子程式,所以要把組合語言程式的主體部分定義成一個遠過程,以便由DOS調用該過程,在程式結束時用RET指令返回DOS。
格式為: 過程名 PROC FAR
.
.
.
RET
過程名 ENDP
1.1.4 為了在程式結束時用RET指令能正確返回DOS,在碼段的開始處,必須儲存DOS現場即儲存返回地址,儲存返回地址的指令為:
PUSH DS
SUBAX,AX
PUSHAX
1.2 8086/8088組合語言程式的.COM檔案結構形式
2
使用.COM檔案時,程式不分段,其進入點必須是0100H,也不必設定堆棧段,且對所有的過程均應定義為NEAR屬性。 圖2 .COM檔案的結構2 程式的載入
程式的載入是指將.EXE檔案或.COM檔案裝入記憶體並執行之,載入1個程式是由DOS的4BH號系統功能來完成的。程式裝入記憶體時,DOS還在它的前面安裝1個256(100H)位元組長的程式段首碼(Program Segment Prefix),作為DOS與運行程式的軟體介面。
2.1 程式段首碼(PSP)
程式段首碼(PSP)簡言之就是1個資料結構,它和使用者程式本身位於同一記憶體配置塊中,構成一個不可分割的整體。它是DOS(作為載入程式的父程)和被載入程式的軟體介面。它主要是用來存放與使用者程式有關的一些控制資訊,並提供者正常或異常結束時返回DOS的途徑。它總共有256個位元組長,位於程式所在段的起始位置。PSP的DOS結構3所示:
圖3 程式段首碼結構2.2 .COM檔案的記憶體映象
一個.COM程式儲存在磁碟中的內容同執行時裝進記憶體的內容完全一致。執行時只是簡單地將.COM程式拷貝到程式段首碼的緊上端,起始地址為100H,.COM檔案裝入記憶體後的映象4所示:
圖4 .COM檔案的記憶體映象由此記憶體映象圖可看出:
(1)當1個.COM程式開始執行時,所有的段寄存器(CS,DS,SS和ES)都含有相同的值,都是程式段首碼(PSP)所在段的段地址。即4個段實際上是同一物理段,段的最大長度為64KB,這也是.COM檔案長度的最大限度。
(2).COM程式是從位移地址值為100H處開始執行的。DOS強行設定IP=0100H,即初始的CS:IP規定指向PSP之後的第1個位元組位置。於是在編寫.COM檔案來源程式時,在用ORG 100H偽指令預留PSP空間後,其後的第1條指令應該是1條可執行檔指令。
(3)整個.COM程式必須在1個物理段(64KB)內,段尾處的1個字作為堆棧使用。在將控制權移交給.COM程式前,MS-DOS總要將1個字0壓進堆棧。減去這個字和程式段首碼佔用的256個位元組空間,餘下的空間便是.COM程式可以使用的空間。也就是說,.COM程式的所有代碼和資料至多使用65,536—2—256=65,278位元組的儲存空間。
(4).COM檔案來源程式中不用設定堆棧段。堆棧指標寄存器SP指向該.COM程式可使用的儲存空間的最高端下面的1個字,即如果至少有64KB儲存空間可供使用,則SP寄存器的值為OFFFEH。DOS強行設定SS=PSP段地址,SP=OFFFEH。綜上所述,所以.COM程式檔案的結構形式必須2所示。
2.3 .EXE檔案的記憶體映象
2.3.1 .EXE檔案結構 1個.EXE檔案的來源程式經組譯工具彙編後產生目標檔案,目標檔案經串連程式的組合—分類—定位後,產生的.EXE檔案是1個可執行檔浮動代碼檔案。該檔案在磁碟空間內是由兩部分組成的:一部分為頭部資訊塊,另一部分為浮動的裝入模組。頭部資訊又分為前部的格式化區(30位元組)和後部的重定位項表,主要是用來說明如何裝入代碼和資料區段。在檔案裝入時雖曾被讀入記憶體高端緩衝區,但在分析使用後即被丟棄,真正留於記憶體空間的只是裝入模組。.EXE檔案的磁碟空間結構5所示:
圖5 .EXE檔案的磁碟空間結構2.3.2 .EXE檔案的記憶體映象 .EXE檔案裝入記憶體的只是裝入模組本身,DOS同時還為使用者程式安裝1個256位元組長的程式段首碼。.EXE程式執行時也是裝入在程式段首碼的緊上端,但程式碼片段,資料區段和堆棧段具體裝到記憶體何處,要由MS-DOS提供的4BH號系統功能調用根據.EXE檔案頭中的資訊來確定。EXE檔案的記憶體映象6所示:
圖6 .EXE檔案的記憶體映象 由此記憶體映象圖可看出:
(1).EXE檔案的程式中可有程式碼片段、資料區段、附加段和堆棧段4個分段,最能體現Intel 80x86體系儲存空間地址分段結構的特點,便於組織大型應用程式。
(2)DOS對.EXE檔案的長度沒有約束,可以佔用全部使用者程式空間。
(3).EXE檔案的來源程式中應該定義1個堆棧段,此段或者具有STACK組合類別型,或者用ASSUME語句把段名和SS段寄存器聯絡起來,或者兩者兼用之。在使用者程式裝入記憶體時,裝入程式自動令SS:SP指向這個堆棧區的底部,來源程式不用設定SS和SP。
(4).EXE檔案的來源程式中不應設定初始的CS和IP值,而應該使用END啟動標號這樣的偽指令來說明啟動點,載入時CS:IP寄存器會自動指向程式中第1條可執行指令處。
(5).EXE程式裝入記憶體後,DS和ES寄存器中的值均為程式段首碼PSP的段地址,同時從圖3(程式段首碼結構)中可看出,程式段首碼PSP中的頭兩個位元組的內容是十六進位“CD20”與8086/8088指令“INT 20H”相對應,該指令的作用是程式非駐留退出時的結束指令,但發出此非強制中斷指令時,要求CS段寄存器一定要為程式段首碼PSP的段地址。故在.EXE(“標準序”)結構中利用DS在堆棧中儲存程式段首碼的段地址,利用SUB AX,AX和PUSH AX指令來儲存程式段首碼中INT 20H指令的首地址。
PUSH DS ———→儲存PSP段地址
SUB AX,AX
PUSH AX———→INT 20H指令所在第1個位元組的位移地址為0
其次,主過程又定義為遠過程,因此RET是1個遠程(段間)返回指令。於是,程式最後執行RET指令時將使CS=PSP的段地址,IP=0000H。這樣一來系統就會自動轉移到PSP+00H,從而正確執行“INT 20H”指令,完成程式的結束退出。
標準序還有另外1個優點:程式的主體呈現為1個遠過程的結構,也便於程式模組化。
綜上所述,所以.EXE程式檔案的結構形式必須1所示。3 .COM程式與.EXE程式的比較
通過對.COM程式和.EXE程式結構的研究,從中可看出:.COM程式由程式本身的二進位程式碼群組成,它沒有.EXE程式所具有的格式化區和重定位項表,也就免去了最後修正裝入模組中某些位置的機器碼,所以它佔有的儲存空間比.EXE程式要小;.COM程式又不允許分段,它所佔有的儲存空間不允許超過64KB,因而只能用來編製較小的程式,由於其規模小而簡單,所以.COM程式的裝入速度比.EXE程式的裝入速度要快得多。DOS對.EXE程式的長度沒有約束,可以佔用全部使用者程式空間;.EXE程式中可有程式碼片段、資料區段、附加段和堆棧段4個分段,便於組織大型應用程式;.EXE程式主體呈現為1個遠過程的結構,也便於實現程式模組化。