現在我們将開始編寫完整的彙編語言程式,用編譯器将它們編譯成為可執行檔案(如:*.exe檔案),在作業系統中運作。
1、一個源程式從寫出到執行的過程
一個彙編語言程式從寫出到最終執行的簡要過程:

(1)編寫彙編源程式:
使用文本編輯器(如記事本、Notepad++、UltraEdi等),用彙編語言編寫彙編源程式。
(2)對源程式進行編譯連接配接:
使用彙編語言編譯程式(MASM.EXE)對源程式檔案中的源程式進行編譯,産生目标檔案;
再用連接配接程式(LINK.EXE)對目标檔案進行連接配接,生成可在作業系統中直接運作的可執行檔案。
可執行檔案包含兩部分内容:
程式(從原程式中的彙編指令翻譯過來的機器碼)和資料(源程式中定義的資料)
相關的描述資訊(比如:程式有多大、要占多少記憶體空間等)
(3)執行可執行檔案中的程式:
在作業系統中,執行可執行檔案中的程式。
作業系統依照可執行檔案中的描述資訊,将可執行檔案中的機器碼和資料加載入記憶體,并進行相關的初始化(比如:設定CS:IP指向第一條要執行的指令),然後由CPU執行程式。
2、源程式
程式4.1(一段簡單的彙編語言的源程式)
彙編指令:有對應的機器碼的指令,可以被編譯為機器指令,最終為CPU所執行。
僞指令:沒有對應的機器碼的指令,最終不被CPU所執行。
誰來執行僞指令呢?僞指令是由編譯器來執行的指令,編譯器根據僞指令來進行相關的編譯工作
僞指令:segment……ends 和 end 和assume
(1)定義一個段:
segment和ends是一對成對使用的僞指令,這是在寫可被編譯器編譯的彙程式設計式時,必須要用到的一對僞指令。
segment和ends的功能是定義一個段,segment說明一個段開始,ends 說明一個段結束。
一個段必須有一個名稱來辨別,使用格式為:
段名 segment
……
……
……
段名 ends
一個彙程式設計式是由多個段組成的,這些段被用來存放代碼、資料或當作棧空間來使用。
一個有意義的彙程式設計式中至少要有一個段,這個段用來存放代碼。
(2)End 是一個彙程式設計式的結束标記,編譯器在編譯彙程式設計式的過程中,如果碰到了僞指令 end,就結束對源程式的編譯。
如果程式寫完了,要在結尾處加上僞指令end 。否則,編譯器在編譯程式時,無法知道程式在何處結束。
(3)assume 假設
它假設某一段寄存器和程式中的某一個用 segment … ends 定義的段相關聯。通過assume說明這種關聯,在需要的情況下 ,編譯程式可以将段寄存器和某一個具體的段相聯系。
源程式中的“程式”:
彙編源程式:
- 僞指令 (編譯器處理)
- 彙編指令(編譯為機器碼)
程式:源程式中最終由計算機執行、處理的指令或資料。
我們可以将源程式檔案中的所有内容稱為源程式,将源程式中最終由計算機執行處理的指令或資料 ,成為程式。程式最先以彙編指令的形式存在源程式中,經編譯、連接配接後轉變為機器碼,存儲在可執行檔案中。
标号:一個标号指代了一個位址:
codesg:放在segment的前面,作為一個段的名稱,這個段的名稱最終将被編譯、連接配接程式處理為一個段的段位址。
程式的結構:
任務:程式設計計算2^3。源程式應該怎樣來寫呢?
(1)定義一個段:
abc segment
……
abc ends
(2)實作處理任務
abc segment
mov ax,2
add ax,ax
add ax,ax
abc ends
(3)程式結束
abc segment
mov ax,2
add ax,ax
add ax,ax
abc ends
end
(4)段與段寄存器關聯
abc被當作代碼段來用,是以,我們應該将abc和cs聯系起來。
assume cs:abc
abc segment
mov ax,2
add ax,ax
add ax,ax
abc ends
end
最終,完整程式如下:
assume cs:abc
abc segment
mov ax,2
add ax,ax
add ax,ax
abc ends
end
程式傳回:
我們的程式最先以彙編指令的形式存在源程式中,經編譯、連接配接後轉變為機器碼,存儲在可執行檔案中,那麼,它怎樣得到運作呢?
DOS中的程式運作:
DOS是一個單任務作業系統。
- 一個程式P2在可執行檔案中,則必須有一個正在運作的程式P1,将P2從可執行檔案中加載入記憶體後,将CPU的控制權交給P2,P2才能得以運作。P2開始運作後,P1暫停運作。
- 而當P2運作完畢後,應該将CPU的控制權交還給使它得以運作的程式P1,此後,P1繼續運作。
現在,我們知道,一個程式結束後,将CPU的控制權交還給使它得以運作的程式,我們稱這個過程為:程式傳回。
應該在程式的末尾添加傳回的程式段。
mov ax,4c00H
int 21H
這兩條指令所實作的功能就是程式傳回。
段結束、程式結束、程式傳回!
文法錯誤和邏輯錯誤
- 文法錯誤:程式在編譯時被編譯器發現的錯誤;容易發現。
- 邏輯錯誤:程式在編譯時不能表現出來的、在運作時發生的錯誤;不容易發現。
3、編輯源程式
源程式如下:
assume cs:abc
abc segment
mov ax,2
add ax,ax
add ax,ax
mov ax,4c00H
int 21H
abc ends
end
運用編輯器(notepad++、ultraedit等),編輯程式,然後将程式儲存為檔案 *.asm。結束對源程式地編輯。
4、編譯
将編輯好的字尾為.asm的源程式,進行編譯,生成包含機器代碼的目标檔案。
5、連接配接
在對源程式/.asm進行編譯得到目标檔案/.obj後,我們需要對目标檔案進行連接配接,進而得到可執行檔案/.exe。
上面已經對1.asm進行編譯得到1.obj,現在将1.obj連接配接為1.exe。
連接配接的作用有以下幾個?
- 當源程式很大時,可以将它分為多個源程式檔案來編譯,每個源程式編譯成為目标檔案後,再用連接配接程式将它們連接配接到一起,生成一個可執行檔案;
- 程式中調用了某個庫檔案中的子程式,需要将這個庫檔案和該程式生成的目标檔案連接配接到一起,生成一個可執行檔案;
- 一個源程式編譯後,得到了存有機器碼的目标檔案,目标檔案中的有些内容還不能直接用來生成可執行檔案,連接配接程式将這此内容處理為最終的可執行資訊。是以,在隻有一個源程式檔案,而又不需要調用某個庫中的子程式的情況下,也必須用連接配接程式對目标檔案進行處理,生成可執行檔案。
強調一下,我們學習彙編的主要目的,就是通過用彙編語言進行程式設計而深入地了解計算機底層的基本工作機理,達到可以随心所欲地控制計算機的目的。我們用彙編語言程式設計,就要用到 :編輯器(Edit)、編譯器(masm)、連接配接器(link)、調試工具(debug)等所有工具,而這些工具都是在作業系統之上運作的程式,是以我們的學習過程必須在作業系統的環境中進行。
6、可執行檔案中的程式裝入記憶體并運作的原理
(1)我們在提示符“C:\masm”後面輸入可執行檔案的名字“1”,按Enter鍵。
然後正在運作的command,将1.exe中的程式加載入記憶體;
(2)1.exe中的程式運作;
command設定CPU的CS:IP指向程式的第一條指令(即程式的入口),進而使程式得以運作;
(3)運作結束,傳回,再次顯示提示符“C:\masm”。
程式運作結束後,傳回到command中,CPU繼續運作 command.
彙程式設計式從寫出到執行的過程:![]()
彙編語言知識點總結之四:第四章《第1個程式》
7、程式執行過程的跟蹤
為了觀察程式的運作過程 ,我們可以使用Debug。
Debug 可以将程式加載入記憶體,設定CS:IP指向程式的入口,但Debug并不放棄對CPU 的控制,這樣,我們就可以使用Debug 的相關指令來單步執行程式 ,檢視每條指令指令的執行結果。
在DOS系統中.EXE檔案中的程式的加載過程如下:
總結:
程式加載後,ds中存放着程式所在記憶體區的段位址,這個記憶體區的偏移位址為 0 ,則程式所在的記憶體區的位址為:ds:0;
這個記憶體區的前256 個位元組中存放的是PSP,dos用來和程式進行通信。從 256位元組處向後的空間存放的是程式。
是以,我們從ds中可以得到PSP的段位址SA,PSP的偏移位址為 0,則實體位址為SA×16+0。
因為PSP占256(100H)位元組,是以程式的實體位址是:可用段位址和偏移位址表示為:SA+10:0。
- 用U指令檢視一下其他指令:
- 用T指令擔不執行程式中的每一條指令,并觀察每條指令的執行結果
- 到了 int 21,我們要用P指令執行:
- int 21 執行後,顯示“Program terminated normally”,傳回到Debug中。
- 表示程式正常結束。
- 注意,要使用P指令執行int 21。
需要注意的是,在 DOS 中運作程式時,是command将程式加載入記憶體;
是以程式運作結束後傳回到command中,而在這裡是debug 将程式加載入記憶體,是以程式運作結束後要傳回到Debug中。
使用Q指令退出Debug,将傳回到command中,因為Debug是由command加載運作的。
我們在 DOS中用 “Debug 1.exe” 運作Debug對1.exe進行跟蹤時,程式加載的順序是:command加載Debug,Debug加載1.exe。
傳回的順序是:從1.exe中的程式傳回到Debug,從Debug傳回到command。
實驗3: