1. 程式人生 > >Sample語言編譯與執行及簡單虛擬機器器的設計實現

Sample語言編譯與執行及簡單虛擬機器器的設計實現

記錄一下編譯原理課程設計的內容(Java )。這是一次課程設計,程式碼中有很多漏洞,歡迎來噴。

Sample語言的編譯包括,詞法分析,語法分析,語義分析及中間程式碼生成,中間程式碼轉化為彙編程式碼。詞法分析採用有窮自動機實現,語法分析和語義分析均為自上而下進行。中間程式碼轉化為彙編的過程中統一採用變址定址方式,簡單粗暴,未做更多優化。

Sample語言具有一般高階語言的共同特徵:字符集包括所有的大小寫字母、數字和一些分界符;有多種內部資料型別:整型、實型、布林型和字元型;語句說明包括說明語句和可執行語句;語句控制包括順序、條件和迴圈3種結構。具體來說主要包括如下一些語法成分。

(1)     由字母、數字以及下劃線構成的字符集。

(2)     由關鍵字、識別符號、常數、界符和運算子構成的單詞集。

(3)     有整形、布林型、實型和字元型4中資料結構。

(4)     包括算術表示式、邏輯表示式和關係表示式3種類型的表示式;語句由說明語句和可執行語句兩種型別構成;說明語句包括常量說明(用const定義)和變數說明(用var定義);可執行語句有賦值語句、if語句、while語句、do-while語句等。

(5)     程式由關鍵字program開頭。

語法分析階段涉及的句法如下:

算術表示式的語法定義如下。

(1)<算術表示式>::=<項> <加法運算子> <項> | <項>

(2)<項>::=<因子> <乘法運算子> <因子> | <因子>

(3)<因子>::=<識別符號> | <無符號整數> |(<算術表示式>)

(4)<加法運算子>::= + | -

(5)<乘法運算子>::=* | /

布林表示式的語法定義如下。

(1)<布林表示式>::=<布林項> or <布林表示式> | <布林項>

(2)<布林項>::=<布林因子> or <布林項> | <布林因子>

(3)<布林因子>::=<布林量> | !<布林因子>

(4)<布林量>::=<布林表示式> | <算術表示式> <關係符> <算術表示式>

(5)<關係符>::= < | > | < > | <=| >= | == |=

if,while、for、賦值等語句的定義如下。文字表達可能不太清楚,所以用圖的形式描述。


目的碼生成是將語義分析所產生的中間程式碼變換成目的碼,從而實現了源程式的最後翻譯,此階段的工作也是最複雜的。它的複雜性在於,翻譯工作有賴於目標機器的系統結構,涉及記憶體的使用、指令的選擇以及暫存器的排程。因此我們將四元式到機器碼的翻譯分為兩步,第一步是四元式到組合語言的翻譯,第二步是組合語言到機器碼的翻譯。

接下來就是簡單虛擬機器器的設計的實現了。該虛擬機器將完成從彙編程式碼到二進位制程式碼的轉換,並將二進位制程式碼載入到記憶體、執行。

我們首先需要設計一臺抽象的計算機——虛擬裸機,包括暫存器的設計與使用、記憶體的設計與使用、指令格式的設計、定址方式的設計等。

抽象計算機的虛擬硬體配置。

1、記憶體為256×256個單元(64K),一個單元成為一個字,一個字有兩個位元組,地址為0~65535。單元可以存放置零,也可以存放資料。記憶體單元若存放資料,則資料範圍為-32768~+32767或0~65535。一條機器指令佔用一個單元,若不考慮資料佔用的記憶體單元,程式最大長度為256×256。

2、虛擬機器具有四個通用暫存器(2位元組)、一個標誌暫存器FlagReg和一個堆疊暫存器TopReg。4個通用暫存器分別標記為R0、R1、R2、R3,除可用於存放運算元和計算結果外,還可用於變址定址。標誌暫存器FlagReg用於儲存cmp指令比較結果。堆疊暫存器TopReg用作系統棧頂指標。

定址方式及暫存器功能說明。

1、記憶體單元若存放指令,高四位(第15、14、13、12位)為操作碼、低12位(11~0)

       用於描述地址。第11、10位表徵第一地址,第一地址只能是暫存器。

                 00:表示第一地址為R0;

                 01:表示第一地址為R1;

                 02:表示第一地址為R2;

                 03:表示第一地址為R3;

                 第9、8位表徵第二地址的定址方式,第二地址可以是記憶體直接地址、暫存器或變

       址定址,還可以是0~255範圍內的直接數。

00:表示直接地址定址(M),第二運算元在記憶體低地址區域,地址範圍為0~255,7~0位表示記憶體地址。

01:表示暫存器定址,3~0位的值可為0~3,分別表示R0~R3(實際上是用第一位和第0位)。7~4位或為0,或為1(實際上使用第5位)。0表示暫存器直接定址(Ri),

第二運算元在暫存器Ri中;1表示暫存器間址定址(@Ri),在暫存器Ri中存放的是第二運算元地址。

       10:表示直接數訪問立即定址(D),7~0位表示直接數,直接數範圍為0~255。

       11:表示以C*255為基址,以R3為位移的變址定址(C[R3],0≤C≤FFh),7~0位表示C。C相當於段號或頁號,R3相當於段內位移或頁內位移。當R3用於變址定址時,R3僅低8位有效,地址計算公式為:C*256+(R3&0x00ff)。因二進位制位有限,這裡約定只有R3可以作為變址暫存器。

       2、堆疊暫存器TopReg(2位元組)的初始值為0,系統堆疊從記憶體地址65535開始(0-1=65535)。執行call指令時,TopReg減1,系統將斷點存入TopReg所指的記憶體單元,入口由call指令的第二地址確定;當執行ret指令時,從TopReg所指的單元獲取斷點,系統將TopReg增1。使用者程式從地址0開始存放,CPU模擬器從該地址開始執行程式指令。在編制使用者程式時,應注意和堆疊儲存區域的衝突。

       3、輸入輸出指令無第二地址,由於第一地址只能是暫存器,故輸入輸出指令只能對暫存器進行操作。jmp、jmpneg、jmppos、jmpzero和call指令無第一地址。因斷點在堆疊中,故ret指令無第一、第二地址。

       4、對於某些指令,某些二進位制位可能不使用,在編寫機器語言程式時,統一將其置位0。

機器語言指令描述。

1、read(0h):從鍵盤讀一個字到第一地址。等待使用者從鍵盤輸入一個十進位制資料,數值範圍為-32768~+32767。資料可以空格、Tab和回車結束。在輸入過程中,可利用Backspace鍵刪除已輸入的字元,也可鍵入Esc鍵終止程式執行。若輸入資料中存在非法字元,則虛擬裸機拒絕接受,要求使用者重新輸入。

2、write(1h):從第一地址寫一個字到螢幕。

3、load(2h):從第二地址將字裝入第一地址。

4、store(3h):將第一地址中的字存放到第二地址。

5、call(4h):轉移到第二地址指定的記憶體單元,執行子程式,斷點保留在系統堆疊中。

6、ret(5h):由系統堆疊獲得斷點,返回。

7、add(6h):將第一地址中的字加上第二地址中的字,結果保留在第一地址中。

8、sub(7h):將第一地址中的字減去第二地址中的字,結果保留在第一地址中。

9、mul(8h):將第一地址中的字乘以第二地址中的字,結果保留在第一地址中。

10、div(9h):將第一地址中的字除以第二地址中的字,結果保留在第一地址中。

11、cmp(Ah):將第一地址中的字和第二地址中的字比較,由系統置位標誌暫存器FlagReg。

標誌暫存器FlagReg=-1,表示第一地址中的字小於第二地址中的字。

標誌暫存器FlagReg=1,表示第一地址中的字大於第二地址中的字。

標誌暫存器FlagReg=0,表示第一地址中的字等於第二地址中的字。

12、jmp(Bh):無條件轉移到第二地址指定的記憶體單元。

13、jmpneg(Ch):若標誌暫存器FlagReg中的值為-1,轉移到第二地址指定的記憶體單元。

14、jmppos(Dh):若標誌暫存器FlagReg中的值為1,轉移到第二地址指定的記憶體單元。

15、jmpzero(Eh):若標誌暫存器FlagReg中的值為0,轉移到第二地址指定的記憶體單元。

16、halt(Fh):終止程式執行。

指令系統。

指令格式如下(16Bit):

15-12                11-10             9-8                  7-0

操作碼

第一地址

定址方式

第二地址

①    操作碼

②    第一地址為暫存器(R0-R3)

③    定址方式和第二地址

④    指令分類

到此為止一個簡單的編譯器已經完成了。測試程式中有一個輾轉相減法求最大公約數和最小公倍數的例程。


執行結果如下。


上圖中12和30為輸入資料,輸出6為最大公約數,60為最小公倍數。

程式碼地址:https://github.com/fhlt/Compile.git