D Parser 之前(二):彙編編譯器

  在《D Parser 之前:寫一個簡單的虛擬機》裏,其中計算 1 到 100 之和的程序 add.bin,是使用十六進制編輯器直接編輯出來的。虛擬機制作完後,考慮了一下,如果直接寫 Z 的編譯器,難度還是不小,所以決定,先寫一個彙編語言的編譯器,實現從彙編代碼到機器代碼的編譯工作。

 

  大體來說,彙編編譯基本上是一條一條對照生成,不過,行號的需求使得其中多了一些複雜性,另外,我還決定加入註釋的支持。所以,這也是一個比較好的機會實踐一下分析器生成器的使用。

 

  彙編語言部分做了少量修改,over 改爲 end,行號改爲加 @ 前綴,完成的 Grammatica 的分析文件如下:

%header%

GRAMMARTYPE = "LL"

DESCRIPTION = "A asm grammar for zvm."

AUTHOR      = "Lephone Liang"
VERSION     = "1.0"
DATE        = "7 January 2008"

LICENSE     = "."

COPYRIGHT   = "Copyright (c) 2008 Lephone. All rights reserved."


%tokens%

EAX                          = "eax"
EBX                          = "ebx"
ESP                          = "esp"
EIP                          = "eip"

SET                          = "set"
MOV                          = "mov"
TJMP                         = "jmp"
ADD                          = "add"
TGT                          = "gt"
TGTEQ                        = "gteq"
TEQ                          = "eq"
TNOT                         = "not"
IF                           = "if"
TOUT                         = "out"
TEND                         = "end"
POINT                        = "*"
COMMA                        = ","


NUMBER                       = <<(-)?([0-9])+>>
LABEL                        = <<@[a-z]+>>
COMMENT                      = <<;[^\n\r]*[\r\n]>> %ignore%
WHITESPACE                   = <<[ \t\n\r]+>> %ignore%


%productions%

Expression = Atom [Expression];

Atom
 = SetEax
 | MovEax8Esp
 | SetEbx
 | MovEbx8Esp
 | Mov8EspEax
 | Mov8EspEbx
 | AddEsp
 | AddEaxEbx
 | Gt
 | Gteq
 | Eq
 | Not
 | IfEaxJmp
 | Jmp
 | Out
 | End
 | LineLabel ;

SetEax            = SET EAX COMMA NUMBER;
MovEax8Esp        = MOV EAX COMMA POINT ESP;
SetEbx            = SET EBX COMMA NUMBER;
MovEbx8Esp        = MOV EBX COMMA POINT ESP;
Mov8EspEax        = MOV POINT ESP COMMA EAX;
Mov8EspEbx        = MOV POINT ESP COMMA EBX;
AddEsp            = ADD ESP COMMA NUMBER;
AddEaxEbx         = ADD EAX COMMA EBX;
Gt                = TGT;
Gteq              = TGTEQ;
Eq                = TEQ;
Not               = TNOT;
IfEaxJmp          = IF EAX TJMP LABEL;
Jmp               = TJMP LABEL;
Out               = TOUT EAX;
End               = TEND;
LineLabel         = LABEL;

 

  生成代碼後,加入新建的 ZasmC 工程,參照 Grammatica 的例子調試了一會兒,增加一些處理代碼後,編譯器可以正常工作了。用它編譯上一次的的 1 到 100 和的彙編代碼,發現幾個彙編代碼的格式錯誤 biggrin 後,編譯成功,加載入虛擬機,運行得到結果:5050。

 

  還想再寫一個程序驗證一下,Fibonacci 序列是一個不錯的例子,於是編寫 d 的原型如下:

import std.stdio;

static void main(char[][] args)
{
 int i=0;
 int a=1;
 write(a);
 int b=1;
 write(b);
 int t;
 next:
 t = a + b;
 write(t);
 a = b;
 b = t;
 i++;
 if(i<10) goto next;
}

void write(int n)
{
 writefln("%d", n);
}

  

 

  改寫爲彙編代碼如下:

; 斐波那契
; esp i, esp+4 a, esp+8 b, esp+12 t
; int i=0;
set eax, 0
mov *esp, eax
; int a=1;
; write(a);
set eax, 1
add esp, 4  ; a
mov *esp, eax
out eax
; int b=1;
; write(b);
add esp, 4  ; b
mov *esp, eax
out eax
add esp, -8  ; i
; int t;
@next
; t = a + b;
; write(t);
add esp, 4  ; a
mov eax, *esp
add esp, 4  ; b
mov ebx, *esp
add eax, ebx
add esp, 4  ; t
mov *esp, eax
out eax
; a = b;
; b = t;
add esp, -4  ; b
mov eax, *esp
add esp, -4  ; a
mov *esp, eax
add esp, 8  ; t
mov eax, *esp
add esp, -4  ; b
mov *esp, eax
; i++;
add esp, -8  ; i
mov eax, *esp
set ebx, 1
add eax, ebx
mov *esp, eax
set ebx, 10  ; 循環次數
gteq
not
if eax jmp @next
end

 

 

  用 ZasmC 編譯,生成 Fibonacci.bin,加載到虛擬機,第一次運行錯誤,後來發現是 d 轉匯編的時候的疏忽,修正彙編代碼後,編譯,加載運行,得到正確的結果。

 

  下一步就是寫 Z 的編譯器了,這一步可能要花比較長的時間,準備把 Z 編譯成彙編代碼,然後再用這個彙編編譯器編譯成機器代碼,這樣,Z 編譯器就不需要處理行號問題了。

 

  下面是虛擬機和彙編編譯器的源代碼,以及運行 Fibonacci 的截圖:

 

發佈了0 篇原創文章 · 獲贊 0 · 訪問量 1308
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章