1.假設一個C程式,有兩個檔案p1.c和p2.c。我們在一台IA32機器上,用Unix命令列編譯這些代碼如下:
unix> gcc -O1 -o p p1.c p2.c
實際上gcc命令調用了一系列程式,將原始碼轉化成可執行代碼。首先,C前置處理器擴充原始碼,插入所有用#include命令指定的檔案,並擴充所有用#define聲明指定的宏。然後,編譯器產生兩個原始碼的彙編代碼,名字分別為p1.s和p2.s。接下來,彙編器將彙編代碼轉化成二進位目標代碼檔案,名為p1.o和p2.o。目標代碼是機器代碼的一種形式,它包括所有指令的二進位表示,但是還沒有填入地址的全域至。最後,連結器將兩個目標代碼檔案與實現庫函數(例如printf)的代碼合并,併產生最終的可執行代碼檔案p。可執行代碼是我們要考慮的機器代碼的第二種形式,也就是處理器執行的代碼格式。
在整個編譯過程中,編譯器會完成大部分的工作,將把用C語言提供的相對比較抽象的執行模型表示的程式轉化成處理器執行的非常基本的指令。彙編代碼錶示非常接近於機器代碼。與機器代碼的二進位格式相比,彙編代碼有一個的主要特點,即它用可讀性更好的文字格式設定來表示。能夠理解彙編代碼以及它與原始C代碼的聯絡,是理解電腦如何執行程式的關鍵一步。
2. 運算元指示符 Eb基底位址暫存器 Ei變址寄存器
類型 |
格式 |
運算元值 |
名稱 |
立即數 |
$Imm |
Imm |
立即數定址 |
寄存器 |
Ea |
R[Ea] |
寄存器定址 |
儲存空間 |
Imm |
M[Imm] |
絕對定址 |
存取器 |
(Ea) |
M[R[Ea]] |
間接定址 |
儲存空間 |
Imm(Ea) |
M[Imm+R[Ea]] |
(基址+位移量)定址 |
儲存空間 |
(Eb, Ei) |
M[R[Eb]+R[Ei]] |
變址定址 |
儲存空間 |
Imm(Eb, Ei) |
M[Imm+R[Eb]+R[E]i] |
變址定址 |
儲存空間 |
(, Ei, s) |
M[R[Ei]*s] |
比例變址定址 |
儲存空間 |
Imm(, Ei, s) |
M[Imm+R[Ei]*s] |
比例變址定址 |
儲存空間 |
(Eb, Ei, s) |
M[R[Eb]+R[Ei]*s] |
比例變址定址 |
儲存空間 |
Imm(Eb, Ei, s) |
M[Imm+R[Eb,]+R[Ei]*s] |
比例變址定址 |
3.資料傳送指令 MOV類中的指令將源運算元的值複製到目的運算元中。源運算元指定的值是一個立即數,寄存在寄存器中或者儲存空間中。目的運算元指定一個位置,或是一個寄存器,或是一個寄存器地址。IA32加了一條限制,傳送指令的兩個運算元不能都指向儲存空間位置。講一個值從一個儲存空間位置複製到另一個儲存空間位置需要兩條指令-----第一條指令將源值載入到寄存器中,第二條將該寄存器值寫入目的位置。
4.算術和邏輯操作
載入有效地址 載入有效地址(load effective address)指令lead實際上是movl指令的變形。它的指令形式是從儲存空間讀資料到寄存器,但實際上它根本就沒有引用寄存器。它的第一個運算元看上去是一個儲存空間引用,但該指令並不是從指定的位置讀入資料,而是將有效地址寫入到目的運算元。這條指令可以為後面的存取器引用產生指標。
lead S, D D <---&S
另外,它還可以簡潔地描述普通的算術操作。例如 lead 7(%edx, %edx, 4) %eax。如果%edx的值為x,那麼%eax的值為5X+7。
5.控制 P125
彙編代碼不會記錄程式值的類型。相反地,不同的指令確定運算元的大小,以及他們是有符號還是無符號的。
組合語言中,直接跳轉是給出一個標號作為跳轉目標的,例如“.L1”。間接跳轉的寫法是“*”後面跟一個運算元指示符。(間接跳轉,即跳轉目標是從寄存器或儲存空間位置中讀出的)。條件跳轉只能是直接跳轉。在彙編代碼中,跳轉指令有幾種不同的編碼,但是最常用的都是PC(程式計數器)相關的。它們會將目標指令的地址與緊跟在跳轉指令後面那條指令的地址之間的差作為編碼。這些地址位移量可以編碼為1,、2或4個位元組。第二種編碼方式就是給出“絕對”地址,用4個位元組直接指定目標。
switch 語句 switch語句可以根據一個整數索引值進行多重分支。處理具有多種可能結果的測試時,這種語句特別有用。它們不僅提高了C代碼的可讀性,而且通過使用跳轉表這種資料結構使得實現更加高效,跳變表是一個數組,表項i是一個程式碼片段的地址,這個程式碼片段實現當開關索引值等於i時程式應該採取的動作。程式碼用開關索引值來執行一個跳板表內的數組引用,確定跳轉指令的目標。
***C語言中的if-else語句的通用形式模板是這樣的:
i