這是計算機組成元素:從零開始構建計算機的第三篇總結,對應著書本的第四章,該章節主要講述了 Machine Language,即匯編語言。這里我們主要討論匯編是如何通過 processor 和 registers 來處理 memory。
首先,我們先分別來看下 memory,processor 和 register 是什么。
Memory:我們把所有能存儲數據與指令的設備都稱為 memory。有點類似我們寄存器的結構,就是由一個一個的字組成,每個字有一個地址。一般該地址所指向的值我們用 Memory[address] 來表示。
Processor:一般我們稱為 CPU。CPU 可以執行一些 operations,比如說,算術運算和邏輯運算,memory access,分支(類似 if-else 語句)。
Registers:這里的寄存器跟 memory 是類似的,只是這里特指 CPU 上的 Memory。因為 memory access 是一個相對耗時的操作,所以呢就在 CPU 上設置一些存儲空間,這樣可以滿足 CPU 即想存儲一些值,又能快速取得這些值的需求。我想 這里的 register 和 Memory 結構肯定有所不同,只是細的我還沒有去研究,所以暫時還不知道區別在那里。
接下來我們講講匯編。匯編本質上是一串 01 的數字,只是規定了一些要求,讓這串數字的可讀性大大提高。比如說一串16位的指令,前幾位代表操作符,中間幾位代表操作數,等等這樣類似的規則。這些根據不同的硬件和匯編語法規則,具體的要求都會有所差別。
有了規定,都是數字還是非常難以讓人理解啊。所以呢有出現了 符號 助記符。舉例來說,我又一個寄存器,它的地址是0,那我同時又可以設定 R0 ?也代表這個寄存器。那這個寄存器就有兩種表示方法了,一個是地址0,另一個就是R0,哪一種更容易閱讀是非常顯而易見的。
計算機中,將這種匯編語言轉化為最終計算機執行的二進制碼的是匯編器,我們后面幾章將會討論如何實現一個匯編器。不同的硬件設備就有可能有不同的匯編語言的語法。這一章我們討論的都是一些通用的匯編指令。
1. 算術和邏輯運算
就是加減法,與或非這幾種操作。
ADD R2, R1, R3 // ?R2 <—R1 + R3,R1,R2,R3 都表示寄存器
AND R1, R2, R3
2. Memory Access
Memory Access 有兩種方式:
(1)第一種我們已經看到了,就是上面 ADD R2,R1,R3 。進行加法運算的時候,我們獲取了 R2 和 R1 中的值,并將結果存進了 R3。這是一種 memory access。這是操作于寄存器上的 memory access。?
(2)第二種就是利用 load 和 store command。這種指令用于在寄存器和 memory 之間移動數據的。這種類型的指令會使用幾種不同的尋址模式,即如何確定我們想要找的字在 memory 中的地址,我們剛說過一個 字對應一個地址。不同的計算機可能會提供不同的尋址模式,但是有三種是都支持的。
? ? ? ? ?— ?直接尋址:比如說我們已經知道了地址是 67,那直接 LOAD R1,67 // R1<- ? ? Memory[67]
? ? ? ? ?— ?立即尋址:這種尋址模式是用于加載常量,這里的常量不是數據常量,而是某個指令的地 址。比如說:LOADI R1,67 ? ? ? // R1 <— 67
? ? ? ? ?— ?間接尋址:這種尋址模式常見于指針。即寄存器擁有的是指向某一塊地址的指針。比如說:LOAD* R2, R1 ? ? ? // ? R2 <— Memory[R1]
3. Flow of Control
?程序通常是順序執行的,一條接著一條執行的。但是有時候會程序會出現分支,出現分支的原因一般有如下三種:
(1)循環
(2)conditional execution(if-else)
(3)subroutine calling (調用函數)
為了滿足這種分支,所有的匯編語言提供了跳轉的命令(包括有條件和無條件的跳轉)。這里舉兩個跳轉命令的例子,詳細的我們后面會介紹。JMP 是無條件跳轉的一種,只要你確定了要跳轉的地址,它就直接跳轉了。JNG 是帶條件判斷的一種,只有條件為真才去跳轉到指定的地址。
4. Hack Computer
剛我們說過,不同的計算機會有不同的匯編語言,我們這里引進 Hack Computer,它跟我們普通的計算機沒什么不同,只是它簡化了很多東西,同時這個理想化的計算機,可以為我們的練習提供方便,第五章我們要實現的就是這樣一個計算機。
先來簡單介紹下硬件組成:16位的機器,一個CPU,兩塊獨立的存儲空間,一塊存指令,一塊存數據,一個鍵盤和一個屏幕。
先來介紹下存儲空間的分配。剛說了,一塊指令的 memory 和另一塊 data memory。這兩塊的 memory 都是 16 位寬,用 15 位地址表示。CPU只能執行在指令存儲區的程序,但是指令存儲區是一塊只能讀取的區域,所以程序只能用外部手段,而且只能 load 一次。
接下來說下 寄存器。Hack Programmer 有兩類寄存器。一類是 D型寄存器,另一類是 A型寄存器。兩者的寄存器用作不同的目的,D型寄存器只是用來存儲數據的,而 A型寄存器既可以存數據也可以存儲地址,這里的地址包括數據的地址或者是一條指令的地址。
說完這些,就可以講講 Hack Computer 上匯編語言的語法。我們剛講過匯編語言都支持三種類型的指令,Hack Computer 上的當然也支持。
首先是邏輯與算術運算。沒有用 ADD 這樣的符號去代替,而是依舊 采用 + ,- 等數學公式上常用的符號(乘和除還沒有這本書到現在為止都還沒講過,不過這章用的解決的一個方法是,累加代替乘)。
在接下來是 Memory Access。在 Hacker Computer 中,你可以直接獲取到 D型寄存器的值和A型寄存器的值(如果 A 是作為數據存儲的功能),比如說 D = xxxx 或者 A = xxxx。其次當 A 型寄存器是作為地址的是時候,要取得該地址指向的值,用的是 M。把兩者結合起來舉個例子,D = M + 1, 如果當寄存 A 的值是 516 時,這個例子其實就是 D = Memory[516] - 1(真是跟指針一摸一樣)。我們剛說過 A型寄存器還有一個作用是作為指令的地址,這里主要是在跳轉的時候要用到,Hacker Computer 的跳轉指令首先要確定跳轉到哪里,這個就是由 A型寄存器的值。所以一般來說,在我們做跳轉之前,都要給 A型寄存器賦值,來告訴CPU跳轉到哪,或者說下條要執行的指令的地址在哪里。
指令語法
現在給出指令的具體語法:
A-instruction,又稱 address instruction,是專門用來給 A 型寄存器設置值的(剛我們講過 Hack Computer 的計算機設定了,用 15 位的來表示地址),第一位表示該指令是 A-instruction。
A-instruction:的目的有三個:
(1)這是唯一一個可以直接加載常量的指令。
(2)提供數據地址
(3)提供指令地址
接下來介紹最重要的一個指令
介紹下C-instruction 的用法和意思。可以看出二進制碼對應著三個部分(不包括最前面的三面)。第一位 1 表示該指令是 C-instruction,后面兩位是沒有用到的,但是書上沒說能否是0,所以我們還是用1去寫。
接下來是 compution(comp) 部分。這部分的二進制碼指定我們要計算誰,我們是去D型寄存器去那,還是去取 M 的值,要用什么操作,是加法還是減法等等。
第二部分是 destination(dest) 部分。看這名字應該就知道這部分的指令是干什么的了,ALU 算出來的結果放哪去。
第三部分就是 jump 部分。這里的要注意的是,這部分代碼決定的是代碼的跳轉是有條件的還是無條件的,其次有條件的跳轉是什么樣的條件。要跳轉去哪,是由 A型寄存器決定的。
所以在做跳轉指令的時候,一般都要利用下A-instruction,先加載下一條要執行的指令的位置是哪。
可以看到的是在寫跳轉指令的時候,最前面的 dest 部分是不寫的,因為我們已經通過 A 型寄存器獲取到了 destination。
有一點要注意,因為A既可以去做數據存儲,也可以做地址存儲,所以很容易分不清楚 A型寄存器到底是干什么的。為了防止混淆,在跟跳轉有關的指令的時候,禁止寫M。
符號
一個常量既可以用數字表示,也可以用符號表示。在 Hacker Computer 中有三種類型的 symbols。
(1)predefined symbols。先前計算機就定義好的,內置的符號。屬于這種的有 :
? ? ?Virtual registers:R0 ~ R15,代表16個寄存器,和它們的地址
? ? Predefined pointers: SP,LCL,ARG,THIS,THAT 代表地址 0 ~4
? ?I/O pointers:SCREEN 和 KBD 表示 鍵盤和屏幕的地址
(2)Label symbols。當然我們也可以自定義。
(XXX) ?// ?這就是語法了,這個你自己定義的 Symbol 就代表了某一個地址
需要注意的是,你不一定要先定義在使用,你可以先使用,在使用之后在去定義它,但是你只能定義一次,而不能重復使用。
(3)Variable symbols。任何你沒定義過的 label 就是一個 Variable symbol,會被匯編器任意符一個地址,從16開始的(前面有 R1 - R15)。
輸入輸出的處理
我們之前提到過 Hacker Computer 有一個鍵盤和屏幕作為輸入輸出設備。Hacker Computer 與這兩個設備的交互,其實是對這兩個設備對應的 memory 作讀寫操作,這種處理方式叫做 memory map。我們來看下這兩個設備的 memory。
(1)Screen:是一塊黑白屏幕。分辨率是 256 * 512,每一行由 32 個 16位的字構成,所以是 一位 對應著 一個像素點。當某一位是 0 時,決定了該像素點顯示白色,1 則顯示黑色。在 Symbol 那一節,我們看到過系統有個預定義的符號,SCREEN。這個符號表示的就是屏幕最左上角的那個像素點對應的在內存中的地址,這個 symbol 值是 16384(0x4000)。例子如下:
這里其實會把最左邊的16個像素全部變為黑色。
(2)Keyboard:鍵盤也有一個符號表示,KBD。這個值是 24576(0x6000)。其實你算下,剛好就是就是在屏幕最后一個像素點的內存位置后面一位,256 * 32 + 16384 = 24576。然后 RAM[24576] 的值就是你敲的那個鍵的 ASCII 碼。沒有鍵按下,就是0了。
地址分配
每個存儲在指令存儲區的指令,都會分配一個地址。那匯編器是如何分配的呢?我們這里簡單的就把指令所在的行數定義為該指令的地址。這里的行數是忽略空的行數的結果。我們寫程序為了可讀性把空行加上,匯編器不會管這個的。
總結
現在看下來,Hack Computer 上的匯編語言非常簡單,總共就兩類指令。但實際上一般的匯編語言肯定有更多的數據類型,更多的寄存器與指令格式。而且這兩類指令其實非常接近高級語言了,也不是那么的難以讀懂。但是很懂東西本質都是一樣的,這樣做只是利于我們更好的理解與學習。
文章有任何問題也歡迎指出,還有最重要的一點,所有的詳細資料都在: