若要撰寫 assembly,就必須要了解處理器的架構,目前最常見的事 IA(Intel Architecture)-32 架構,市面上的 Pentium、Celeron、Xeon .... 等等 CPU 都是屬於 IA-32 架構,並支援兩種不同的記憶體架構,分別為 real mode 與 protected mode。
關於 80x86 處理器的演化過程以及每個階段所加入的改變,可以參考 小木偶的網頁 - 組合語言準備工作 一文得到詳細的說明。
Processor Execution Cycle
CPU 的執行週期大概可以分為三個階段:
- Fetch:從記憶體中取得指令
放置正確的位址到 address bus 上,並在 control bus 上輸入 memory read 訊號讓指令可以在正確的記憶體中被取出。 - Decode:將指令解碼
此階段必須辨識從記憶體中取出的指令為何,並進行解碼的動作,這裡有相對應的 schema 可以將其轉為 machine language。 - Execute:執行指令
執行指令時,CPU 中的控制電路會負責時間的控制,而 ALU(Arithmetic & Logic Unit) 則會負責針對資料進行數學及邏輯相關運算處理。
這三個階段通常被稱為 fetch-decode-execute cycle(或是 execution cycle),以下用一張簡單的圖來說明:
其實 Execution Cycle 通常不是在主記憶體中完成,而是在 cache 中,因為 cache 的速度比記憶體更快,不過由於這一段(CPU <-> cache <-> memory)是透明的,因此撰寫應用程式時不需要考量到這個。
Process Registers
在 IA-32 架構中,提供了 10 個 32-bit 以及 6 個 16-bit 的 register,根據用途不同可分為 general、control、segment .... 等幾種;其中 general 還細分為 data、pointer、index 三種 register。
以下針對不同的 register 進行說明:
Data Registers
首先先用一張圖說明 data register 的組成:
這 4 個 32-bit 的 data register 可以用在數學、邏輯、或是其他不同的處理。且看其結構,可以知道為了與上一代的 CPU 相容的設計:
- 4 個 32-bit register:EAX、EBX、ECX、EDX
- 8 個 16-bit register:AX、BX、CX、DX
- 8 個 8-bit register:AH、AL、BH、BL、CH、CL、DH、DL
每一個 register 都是獨立的,因此可以根據需求決定使用 8-bit、16-bit、或是 32-bit 的 register。
但存取時就必須注意一下,假設要讀取 EAX 中的 lower 16 bits 的資料,可以直接存取 AX,會得到相同的效果(因為 AX 本來就是 EAX lower 16 bit 的部分);若是要存取 AX 中 higher 8 bit 的資料,則可以直接存取 AH(因為 AH 為 AX 的 higher 8 bit)。
一般來說,這幾個 register 進行任何數學、邏輯處理都不會有問題,但還是有特例,例如:進行乘法時必須用到 EAX(或是 AX、或是 AL) register;進行迴圈處理時,ECX(或 CX) 則是儲存迴圈執行次數的 register;這些特例都是必須要注意的。
Pointer & Index Registers
首先用一張圖說明 pointer & index register 的組成:
首先,這兩種 register 與 data register 類似,也是 32-bit 與 16-bit 的組合,使用上就端看所處理的資料長度來進行不同的運用。
index register 通常用於字串處理的指令上,除此之外,可以當作一般的 data register 使用。
pointer register 則是專門負責管理 stack 狀態(其實也可以將其當作 data register 使用,不過還是多用於 stack 上)。
Control Registers
control register 有兩個,分別是 flags register 以及 instruction register,以下用一張圖來表示:
instruction pointer register(有時稱為 program counter register) 的作用在於儲存下一個準備被執行的指令所在的記憶體位址之用,當指令從記憶體中被取出後,instruction pointer register 的內容則會更新為下一個準備執行的指令所在的記憶體位址。
另外若是使用傳送 jump、procedure call、interrupt 等控制訊號,亦會更改 instruction pointer register 的內容。
使用上若是需要 32-bit 長度,可使用 EIP;若是 16-bit 則使用 IP。
flags register 也是相同,包含 32-bit 及 16-bit;以 EFLAGS(32-bit) 來說,包含了 6 個 status flag、1 個 control flag、10 個 system flag(詳細名稱可以參考上圖),而 EFLAGS 中的每個 bit 都是獨立有其意義的,可以是 1(set) 或 0(clear),可以用指令修改,例如:clc 指令可設定 carry flag 為 0,stc 指令則可設定 carry flag 為 1。
但 flag register 通常是用來記錄運算、邏輯處理的狀態之用,以下舉幾個例子來說明:
- 進行減法時結果為 0,ZF(zero flag) 會設定為 1
- 處理字串時,可以使用 DF(direction flag) 來決定掃瞄字串的方向(forward or backward)
- 將 TF(trap falg) 設定為 1,可以讓 CPU 以一行一行的方式執行程式,對於 debug 會有相當幫助
- ID(identification flag) 則是用來決定是否支援 CPUID 這個指令
Segment Registers
最後 6 個 16-bit register 就是 segment register,以下用一張圖來說明 segment register 的組成:
在 IA-32 架構下,記憶體會被分為數個 segment,每個 segment 是一小部分的記憶體空間,而 segment register 中儲存的則是指向這些記憶體空間的位址。
但由於只有 6 個 segment register,因此 CPU 最多同一時間僅能存取六個不同的 memory segment。
另外,程式在邏輯上分為兩個部分,分別是 code 與 data;其中 code 的部分所存的僅有指令,而 data 的部分則是僅有程式所需要的資料。
而 CS(code segment) 中所儲存的正是 code 所在的記憶體位址,而 DS(data segment) 儲存的則是 data 所在的記憶體位址,另外,SS(stack segment) 中儲存的則是指向程式的 stack segment 的記憶體位址。
另外三個 ES、FS、GS,則是額外的 segment register,可以作為類似其他的 segment register 使用,例如:當一個 DS 不足以儲存程式所使用資料的記憶體位址,則可能會借用這三個額外 segment register 來使用。
沒有留言:
張貼留言