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 的截图:

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值