2008年1月26日 星期六

PC Assembly Language 學習筆記(1) - Computer Organization

Memory (記憶體)

在記憶體中的每個單位為 1 byte,每個 byte 在記憶體中都會被一個唯一的數字所標示出來,此數字即為該 byte 的 memory address(記憶體位址)。

以下用一張圖來表示 memory address:


上圖中,memory address 為 0 的值為 2A,為 1 的值為 45 .... 以此類推;一般來說,memory的使用不會單單只有一個 byte,因為一般的工作都會用到不少的 memory,因此都是一次處理多個 byte。

另外,所有儲存在 memory 中的資料都是數字,而 character(字元) 的呈現則必須仰賴一張對照表將數字轉為字元,其中最有名的即為 ASCII;ASCII 由於每個字元所佔的 memory 為 1 byte,因此可以表現的字元只有 256 個(其實只會用到 7 bits,因此實際僅能呈現 128 的不同字元),而目前越來越流行的 Unicode,則是每個字元所佔的 memory 為 2 bytes,相對的可以呈現的字元又更多,因此常被用來解決多國語言的需求。

以下舉個範例,字元 A 在 ASCII 與 Unicode 的情況下的儲存方式:(0x 開頭表示 Hexadecimal 十六進位)
  • ASCII:0x41
  • Unicode:0x0041


CPU (Central Processing Unit)

在 CPU 中,有一個很重要的部分,稱為 register(暫存器);將特定的資料放到特定的 register,進而執行特殊的指令,這是暫存器的主要用途,當然也可以存放一般資料,而且速度遠遠快過 memory,不過數量有限,使用上必須要注意。

而 CPU 在處理的,稱為 Machine Language(機器語言),這種語言一般人看不懂,通常都是由 compiler 將設計好的程式轉成 Machine Language;一般來說,每種 CPU 都會有他自己的 Machine Language,因此同樣的 Machine Language 在不同架構的 CPU 下是不能正常執行的。


80x86 CPU

x86 架構的 CPU 是目前普及率最高的 CPU,一般的電腦都搭配著這一類的 CPU,以下大概介紹一下這些 CPU:

8088、8086

這一代的 CPU 用於早期的 PC,其中所使用的 register 都是 16 bits,分別是 AX、BX、CX、DX、SI、DI、BP、SP、CS、DS、SS、ES、IP、FLAGS .... 等等。

由於此代 CPU 只能在 real mode 中運作;而在這種模式下,程式最大僅能用到 1MByte 的 memory,且程式中可以存取任何 memory address,甚至是屬於其他程式的 memory address 也可以,因此造成 debug 以及安全上的顧慮。

此外,程式記憶體也被分為多個 segment,其中每一個 segment 不能超過 64K。

80286

比起上一代的 CPU,register 的部分依然只是 16 bits;但此代的 CPU 多了幾個指令,並可以在 16-bit protected mode 中運作;在此種模式下,最大可以用到 16 MByte 的 memory,而且程式之間的 memory address 在存取上不會互相有衝突,但每個 segment 最大還是只有 64K。

80386

此代 CPU 可說是大躍進,除了每個 register(EAX、EBX、ECX、EDX、ESI、EDI、EBP、ESP、EIP、EFLAGS) 都變成 32 bits 外,還另外多了兩個 16 bits 的 register 為 FS 與 GS。

並加入了 32-bit protected mode,在此模式下,最大可以使用到 4 Gbyte 的 memory(此即為 32 bits 作業系統的最大記憶體限制)。

此外,程式依然會被分為多個 segment,但每個 segment 最大可以到 4 GByte。

80486 / Pentium / Pentium Pro

此代 CPU 沒有很特別,主要只是時脈(速度)上的提升。

Pentium MMX

此代的 CPU 加了 MMX(MultiMedia eXtensions) 指令集,用來加速圖形的處理。

Pentium II

此代 CPU 即為 Pentium Pro + MMX 而已,在主要架構上並沒有突破性的改變

Pentium III

僅為時脈(速度)上的提升。


8086 16-bit Registers

在 8086 CPU 中提供了許多 16-bit register,以下分別介紹他們的用途:

general purpose registers

此種 register 有四個,分別是 AX、BX、CX、DX,用來進行處理時存放一般資料用。

其中每一個 16-bit register 都包含了兩個 8-bit register,以下以 AX 為例:

其中 AH 為 higher 8 bits,而 AL 則為 Lower 8 bits。

而有一個觀念必須了解的是,AH 與 AL 實際上是獨立不互相影響的,但對於 AX 來說 AH 與 AL 又是其一部份,這是必須要注意的!

index register

此種 register 有兩種,分別是 SI 與 DI,通常作為 pointer 之用,但也可以拿來跟 general purpose register 一樣使用。

但是與 general purpose register 不一樣的是,index register 並非由兩個 8-bit register 所組成。

base/stack pointer

此種 register 分別為 BP(base pointer) 與 SP(stack pointer),用來指向 machine language stack 中的資料。

segment register

此種 register 有四種,分別是 CS(code segment)、DS(data segment)、SS(stack segment)、ES(extra segment),用來標示 memory 是被哪些不同的程式所使用,其中 ES 是被用來暫時作為 segment register 用。

instruction pointer register

此即為 IP,與 CS 搭配,用來指向下一個 CPU 準備執行的指令所在的 memory address,一般來說,當指令執行後,IP 就會自動指向下一個準備執行的指令所在的 memory address。

FLAGS register

此即為 FLAGS,用來儲存前一個指令執行完後所產生的重要訊息,而這些訊息是以單一 bit 的方式進行儲存。


80386 32-bit Registers

在 80386 CPU 的時代,register 大躍進變為 32 bits。

舉例來說,原本的 AX 變成了 EAX,而為了向下相容的考量,其中 AX 則變為 EAX 的 lower 16 bits,不過需要注意的是,沒有代表 higher 16 bits 的 register,因此沒辦法直接存取 32-bit register 中 higher 16 bits 的部分。

許多原本 16-bit register 都轉變為 32-bit 了,但 segment register 還是有維持原本的 16 bits,另外還多了 FS 與 GS 的 16-bit register,也是作為 segment register 之用。


Real Mode

在 real mode 中,memory 的使用被限制在 1 MByte(220 bytes) 內,可用的 address 範圍為 0x00000 ~ 0xFFFFF。

由 memory address 的可用範圍可知,存取需要 20 bits 的長度,但在 8086 的 CPU 架構中僅有 16-bit register,因此必須使用兩個 16 bits 值的組合來表示一個 memory address。
其中前面的 16 bits 稱為 selector,必須存於 segment register 中,而後面的 16 bits 稱為 offset,其中實際 memory address 的計算方式如下:
16 * selector + offset (即為 selector 補 0 到後方再與 offset 相加)
而 real mode 有幾個問題:
  1. 單一 selector 只能參照到 64K 大小的 memory,但如果程式碼大小超過 64K,就必須強制分成多個片段來執行;同樣的,不只程式碼,連太大的資料也是會有相同問題。

  2. segment address 的衝突問題,舉例來說:04808 可以被以下幾個組合所參照:
    • 047C:0048 (047C0 + 0048 = 04808)
    • 047D:0038 (047D0 + 0038 = 04804)
    • 047E:0028 (047E0 + 0028 = 04808)

由於種種問題,造成在 real mode 中設計程式是一件非常困難的事情。


16-bit Protected Mode

在 80286 CPU 出現後,提供了 16-bit protected mode,其中處理 selector 的部分與 real mode 是完全不同的。

在 real mode 中,selector 用來表示 memory address 的前段部分;而在 protected mode 中,selector 則是 descriptor table 的 index。

說到 program segment,在兩個不同的模式中都必須要做,只是在 real mode 中,program segment 是存於實體記憶體的固定位置上;而在 protected mode 中則不是。

在 protected mode 中儲存 program segment 的方式稱為 virtual memory,基本的觀念即是「僅儲存目前正在執行程式的 code 與 data 在記憶體中,其他程式的 code 與 data 則存放於磁碟機中」,換另外一支程式要跑時,再從磁碟機中移到記憶體中去執行,而這些步驟由 OS 處理,程式中不需考慮到這些實作細節。

在 protected mode 中,所有個 segment 都被視為是 descriptor table 中的一個 entry,此 entry 中包含了許多資訊,包括:是否目前存於記憶體中? 如果在記憶體中,存於那個位置? 存取權限 .... 等等;而每個 entry 的 index 則是 selector 的值,存於 segment register 中。

此外,雖然已經是 protected mode,程式間所使用的 memory 不會互相有影響,但是畢竟 register 還是只有 16 bits,可以處理的資料量還是很少,假設要處理大量的資料依然是一個很大的挑戰。


32-bit Protected Mode

80386 CPU 中出現了 32-bit protected mode,其中與 16-bit protected mode 最主要的差異有兩點:
  1. offset 長度擴充為 32 bits,因此表示每個 segment 最大可以到 4GBytes

  2. 每個 segment 被分為更小的單元,稱為 page,每個 page 大小為 4K。而在 virtual memory 中所處理的單元改為 page 而非之前的 segment;這表示每次程式處理時,只有 segment 中的一部份會在 memory 中,此舉是為了讓 memory 做更有效率的利用。
目前的 Windows 9X, Windows NT/2000/XP, Linux .... 等等,都是屬於 paged 32-bit protected mode。


Interrupts (中斷)

有時候在程式執行的過程中,需要對於特定事件即時回應,而這種機制稱為 interrupt(中斷)

會造成 interrupt 的情況不只一種,可能是滑鼠移動,或是按下鍵盤 .... 等等,而每種情況都會產生不同的 number 作為識別,並將程式的控制權交給 interrupt handler

在實體記憶體的開頭,包含了稱為 interrupt vector 的資訊,並存於 table 中,而每個由 interrupt 所產生的 number 則是包含在此 table 中的 index。

interrupt 有分為兩種,分別是 external interrupt 與 internal interrupt;其中 external interrupt 發生於 CPU 之外,有許多裝置都會產生此種 interrupt,包含滑鼠、鍵盤、磁碟機 ... 等等;而 internal interrupt 則是發生於 CPU 內,有可能是因為發生 error(稱為 traps) 或是 interrupt 指令的執行(稱為 software interrupts)。

一般情況下,中斷處理完後,interrupt handler 會將控制權移回程式中,也會復原在原本 interrupt 發生前所有在 register 中的值,但僅有 traps 例外,會造成程式的中斷。

5 則留言:

  1. 大大請問一下
    我本來以為selector的range應該是 0 ~ (2^16-1),offset是0~15.
    --因為20bit裡,16bit給selector...
    結果例子裡offset有到72.
    所以selector跟offset的range他到底分別是多少阿?
    為什麼明明有32bit
    卻不能每個byte都給unique的位址啊?
    這個當初設計這個位址的人的考量的點在哪裡呢?
    不勝感激阿....

    回覆刪除
  2. select & offset 各為 16 bits

    雖然兩個加起來共有 32 bits,可是在 real mode 下有 1MB 記憶體的限制,因此存取範圍僅限於 0x00000 ~ 0xFFFFF 之間

    也因為這樣限制,就產生了同一個 memory address 有可能會由不同組合的 selector & offset 所產生的情況發生。

    至於為何要這樣設計,我就不清楚了,我有查到再補上來吧!

    回覆刪除
  3. 謝謝大大的分享!

    回覆刪除
  4. 感謝您的分享與整理。

    回覆刪除
  5. 太感動
    好棒的教學

    回覆刪除