計算機系統要素 C4

存儲

D、A、M

Hack 機器包含兩個16位寄存器——D 和 A,分別是 Data 和 Address 的縮寫。他們本質上是一樣的,用途的不同是分配出來的。

如當訪問 M 這個符號的時候,總是以 A 寄存器的值作爲地址去操作內存。即 M 解釋爲 Memory[A]

又如 goto 指令,跳轉到的位置也是指令存儲器的 A 地址,即 goto 跳到 InstructionMemory[A]

最後,A 寄存器也可以像 D 寄存器一樣用於存儲數據值,而到底它存的是什麼含義,其實取決於上下文的解釋。

指令

從寄存器 A 的用法上可以看出,Hack 程序的很大一部分指令應該都是在操作地址。寫一個地址,操作一下,寫一個地址,操作一下這樣。因此他的指令也分爲兩類:

A 指令

A 指令的第一位是 0,後面跟 15 位值。表示將 A 寄存器設爲這個值。

它的意義首先是允許了輸入常數(15位),這是在將 A 寄存器的值做數字處理的時候;其次爲操作內存、跳轉指令 提供地址。

因此也可以推導 Hack 機器的內存地址空間是 15 位的。

C 指令

C 指令第一位是 1,之後兩位沒有使用。後面的 13 位被分爲三個域:7 位的計算域(Comp)、3 位的目標域(Dest)、3 位的跳轉域(Jump)。

爲什麼空出兩位沒有使用,而目標域和跳轉域都是 3 位呢?

目標域是因爲 C 指令操作的存儲對象只有三個:A 、D 、M。每個對象都需要表達 “存入” 或 “不存入” 兩種選擇,因此需要三個位的域寬。

因爲計數器的存在,程序的默認執行流程是順序執行的,即執行完 ROM[N] 後執行 ROM[N+1]。跳轉域需要表達下一步指令是否需要跳轉的問題,且跳轉的本質是實現流程控制,故該域還需要能夠對計算域輸出的值做出反應。計算域的輸出值範圍有 16 位寬,判斷條件則是一個確定的數,比如 10086。那麼輸出值和判斷條件之間的關係就應該定義三種:小於、等於和大於。這三種關係的輸出值之間互相獨立,故每種都需要表達 “跳” 或 “不跳” 兩種選擇,因此需要三個位的域寬。

另外兩點:1. 至於說輸出值和判斷條件之間的關係爲什麼定義三種,是出於效率考慮的,兩個數字之間的關係也可以表達爲 兩種:等於或不等於。但這樣在做大於或小於判斷的時候就會非常麻煩。 2. 因爲指令已經沒有空間存放判斷條件的比較數,因此跳轉域實際使用的條件值是 0,即你需要先減一次比較數。

計算域有 7 位,用於控制 ALU。記得 A 寄存器有兩種意義嗎,它既可以解釋爲值,也可以解釋爲地址。這個解釋操作其實就是用計算域的第一位控制的,通常管他叫 a-bits,爲 0 時表示輸入值,爲 1 時表示輸入 M[A] 的值。剩餘 6 爲稱爲 c-bits ,用於控制 ALU 函數的選擇。具體對應需要參考表 4-3。但如果你看的是中文版,注意該表右半部助記符有個錯誤,應該是 (當a=1)。

說到勘誤,4.1.1 的寄存器段還有一個錯誤,寄存器的寬度與內存相等,都是 16 位。因此那裏應該是 “只存儲 1 個值”,而不是書中的 “只存儲 1 位”.
以及本章錯誤真雞兒多,感覺跟前三章不是同一個人翻的。

彙編

本章和附錄都沒有一個很完善的彙編參考或教程,因此在做題的時候雖然思路是有的但不知道應該怎麼寫,或者不應該怎麼寫。於是把章內的 sum(1..100) 程序抄了一遍,並且用編譯器和CPU模擬器嘗試跑了一下。

原始的 asm 是這樣的:(OSC 的 markdown 渲染有點傻逼)

    [@i](https://my.oschina.net/izhuchao)		// i refers to some mem. location
    M=1
    [@sum](https://my.oschina.net/Asum)
    M=0
(LOOP)
    [@i](https://my.oschina.net/izhuchao)
    D=M
    [@100](https://my.oschina.net/laoka)
    D=D-A
    [@END](https://my.oschina.net/u/567204)
    D;JGT		// if (i-100)>0 goto END
    @i
    D=M
    @sum
    M=D+M
    @i
    M=M+1
    @LOOP
    0;JMP		// goto LOOP
(END)
    @END
    0;JMP		// infinit loop

我疑惑的點在於,這些 isum 啊的變量到底是怎麼分配內存的?還有 (LOOP) 這種標記算什麼?爲什麼有縮進,他們有具體的含義嗎?當把它編譯之後,得到的文件是這樣的:

0   @16
1   M=1
2   @17
3   M=0
4   @16
5   D=M
6   @100
7   D=D-A
8   @18
9   D;JGT
10  @16
11  D=M
12  @17
13  M=D+M
14  @16
15  M=M+1
16  @4
17  0;JMP
18  @18
19  0;JMP

程序自動爲 i 分配了 16(10000),而 sum 分配了 17(10001)。之後不論我怎麼修改程序,發現總是按順序從 16 開始賦值,即使你的程序寫成有衝突的樣子,比如:

@a
M=1
@16
M=2

它依然會編譯成:

0   @16
1   M=0
2   @16
3   M=1

這個故事告訴我們,不要隨便硬編碼內存地址,儘量都通過符號來分配。

後記:眼瞎如我,其實有一段(4.2.4)講解符號的。

套路

把題做完後總結了一些套路:

  1. 所有變量存在內存裏,使用符號指定
  2. 只有一個A寄存器,它不能既用來存地址,又用來存值。這意味着:當使用跳轉命令時,A 的值只能是跳轉的目標地址,因此表達式只能是 D 或常數。即當使用 C 指令時,總是要先把值存入 D 或使用常數。
  3. 流程控制全靠跳轉,跳轉全靠比較。看到有流控的需求的時候,第一反應把程序分幾段。
  4. 因爲 hack 機器只有一個 D 和 一個 A 寄存器,寫彙編就像玩漢諾塔,總想着怎麼倒數據
  5. 將 M 的值賦給 A 要使用 C 指令而不是 A 指令,即 A=M, 而不是 @A
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章