1. 程式人生 > >5.1.1 描述暫存器機器的語言

5.1.1 描述暫存器機器的語言

 5.1.1 描述暫存器機器的語言
資料路徑圖和控制器圖對於表示像求最大公約數這樣的簡單機器是足夠的了。
但是用它們來描述如LISP直譯器這樣的大型機器就是不明智的了。為了能夠實現複雜的機器,
我們建立一個語言以文字的格式,來表示被資料路徑與控制器提供的資訊。
我們將開始一種標記法來直接對資料路徑圖與控制器圖進行表示。

通過描述暫存器和操作我們定義一個機器的資料路徑。為了描述暫存器,我們能給它一個名稱,
指定一個按鈕來控制對它的賦值。我們給這些按鈕的任何一個起名稱,並且指定在按鈕的控制下,
進行暫存器的資料的來源。(來源包括一個暫存器或者是常數,或者是一個操作)。為了描述一
個操作,我們給它一個名稱,指定它的輸入(暫存器或者是常數)

我們定義一個機器的控制器作為指令的序列加上標籤,標籤標識著在序列中的入口處。
一個指令是如下的內容的一種。

             .為了賦值給一個暫存器的按鈕的名稱( 這對應於控制器圖中的一個盒子)
             .一個測試指令,它執行指定的測試
             .一個條件的分支(分支指令)它的位置被一個控制器標籤指定,基於前面的測試結果
(在控制器圖中,測試加上分支一起對應於方塊)如果測試為假,控制器應該繼續序列中的下面的指令。
否則,控制器應該繼續標籤後面的指令。
            .一個非條件的分支(goto 指令)命名了一個標籤,直接執行這個標籤處的指令。

機器在控制器的指令序列的開始處啟動,並且在當它到達序列的結尾處時停止。除了一個分支改變了控制流,指令被執行按照它們被列出的順序。

(data-paths 
         (registers 
                ((name  a)  (buttons  ((name  a<-b)  (source  (register  b))))) 
                ((name  b)  (buttons  ((name  b<-t)   (source (register   t)))))
                ((name   t)  (buttons  ((name  t<-r)    (source (register  rem)))))) 
         (operations 
                ((name rem)  (inputs  (register  a)  (register  b))) 
                ((name  =)     (register  b) (constant  0)))))

(controller 
         test-b 
               (test  =) 
               (branch  (label  gcd-done))
               (t<-r)       
               (a<-b)
               (b<-t)
               (goto (label  test-b))
        gcd-done
)

圖5.3  一個GCD機器的規範

圖5.3顯示了以這種方式描述的GCD機器。這個例子僅提示了
這種描述的通用性,因為GCD機器是一個十分簡單的例子:每個
暫存器只有一個按鈕,每個按鈕和測試在控制器中僅使用一次。

不幸的是,閱讀這樣的描述是很困難的。為了理解控制器的指令,我們必須
馬上回到按鈕名稱和操作名稱的定義處,為了理解按鈕做什麼事,我們可能必須
引用操作名稱的定義。我們將因此轉換我們的觀念,來把我們已經看到的資料路徑
和控制器的描述的資訊組合起來。

為了得到這個形式的描述,我們將用按鈕和操作的名稱的行為來替換按鈕和操作的名稱。
也就是,代替了控制器圖中說的“按鈕 t<-r”和單獨在資料路徑圖中說“按鈕 t<-r
把rem操作的值賦給暫存器t”和 “rem操作的輸入內容是暫存器a和 b的內容”
我們將在控制器圖中說“按下按鈕,把rem操作的暫存器a,b的內容的值賦給暫存器t”
相似的,代替說在控制器圖中,“執行相等測試”和單獨地在資料路徑圖中說
“相等測試操作的是b的值和常數0” 我們將忽略了資料路徑圖的描述,僅留下控制器的序列。
因此,GCD機器的描述如下:

(controller 
         test-b 
               (test  (op =) (reg b) (const 0)) 
               (branch  (label  gcd-done))
               (assign  t  (op rem)  (reg  a) (reg b))       
               (assign  a  (reg b))
               (assign  b  (reg t))
               (goto (label  test-b))
        gcd-done
)

這種形式的描述是比圖5.3中顯示的那種形式更容易閱讀的,但是也有它的缺點:

.對於大型的機器 ,這有太詳細的細節了,因為在控制器圖中指令序列中
只要元素被提到,資料路徑圖的元素的完整描述就被重複了。(在GCD機器的例子中,
這不是一個問題,因為每個操作和按鈕都只用到了一次)進而,重複的資料路徑的描述妨礙了
機器的實際的資料路徑的結構,對於大型機器而言,有多少暫存器,操作,按鈕並且它們是
如何相互的,這些情況都是不明顯的。

.在一個機器定義中的控制器指令看起來像Lisp表示式,很容易忘記它們不是普通的Lisp表示式。
它們僅能標記合法的機器操作。例如,操作僅能直接操作常數和暫存器的內容,而不是其它操作的結果。

儘管這些不足,我們將在本章中使用這個暫存器-機器語言,因為我們將更加關注的是理解
控制器而不是理解資料路徑的元素與連線。我們應該記住,然而,在設計真實的機器時,
資料路徑的設計是關鍵的。

練習5.2 
使用這個暫存器-機器語言來描述練習5.1中的迭代的斐波那些數的機器。

*動作
讓我們修改GCD機器,讓我們能夠向我們要的GCD中輸入資料,並且在我們的終端上
得到答案。我們沒有討論如何讓一個機器能夠讀取和列印,但是將假定它們作為
原生的操作是可用的。(這正像我們在scheme中使用read,display時我們所做的那樣)

讀取像我們已經使用了的操作,它生成一個值被儲存在一個暫存器中。但是讀取沒有從任何
暫存器中得到輸入,它的值依賴我們設計好的機器的部分的外部發生的情況。我們將允許我們的
機器的操作有這樣的行為,並且因此將畫出與標識出讀取的使用,正如我們為了計算一個值
所做的其它的操作。

列印,另一個方面,它不同於以基礎的方式,我們已經使用的操作,它沒有生成一個儲存在
暫存器中的輸出值。儘管這有一個效應,這個效應也不是我們設計的機器的一部分。
我們將把這種操作叫做動作。在一個數據路徑圖中,我們表示一個動作,正如我們表示一個計算
一個值的操作,作一個 包括它的名稱。從任何的輸入(暫存器或者是常數)用箭頭指向動作的盒子。
按下按鈕,讓動作發生。為了讓一個控制器按下一個動作的按鈕,我們使用一種新型的指令叫做執行。
因此,列印暫存器a的值的動作是表示在控制器圖中的一個指令

(perform  (op print)  (reg a))

圖5.4顯示了新的GCD機器的資料路徑和控制器。代替了列印了結果之後機器停止,我們讓它重開始,為了
能夠重複讀取另一個數據對,計算它們的GCD,並且列印結果。這個結構類似於在第四章中的直譯器的驅動
迴圈。

(controller 
   gcd-loop
     (assign  a (op read))
     (assign  b (op read))
          test-b 
               (test  (op =) (reg b) (const 0)) 
               (branch  (label  gcd-done))
               (assign  t  (op rem)  (reg  a) (reg b))       
               (assign  a  (reg b))
               (assign  b  (reg t))
               (goto (label  test-b))
        gcd-done
              (perform  (op print)  (reg a))
              (goto (label  gcd-loop))
)

圖5.4  讀取輸入和列印輸出的一個GCD機器