在《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 和的汇编代码,发现几个汇编代码的格式错误 后,编译成功,加载入虚拟机,运行得到结果: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 的截图:
- ZLan.zip (112.7 KB)
- 描述: 虚拟机和汇编编译器源代码
- 下载次数: 36
- 描述: Fibonacci 运行截图
- 大小: 13.2 KB
分享到:
相关推荐
计算机系统要素中的“汇编编译器”是一个关键组件,它是连接高级编程语言与机器语言的桥梁。在深入探讨这个主题之前,先要理解汇编语言和编译器的基本概念。 汇编语言是一种低级编程语言,每条指令通常对应着计算机...
**ahc:Haskell编译器** ahc是一款基于Haskell语言的编译器,它的全称可能为Advanced Haskell Compiler。作为一个编译器,ahc的主要任务是将Haskell源代码转换成机器可执行的代码,这个过程涉及到一系列复杂的语言...
同时,Java的面向对象特性使得构建复杂的编译器结构更加容易,可以将不同的编译阶段封装为独立的类,如Tokenizer、Parser、SemanticAnalyzer和CodeGenerator。此外,Java的异常处理机制也有助于在编译过程中捕获和...
C语言编译器是将C语言源代码转换成机器可执行代码的关键工具,它解析、优化并生成目标代码。这份资源包含了一个C语言编译器的完整源代码,这对于学习编译原理、深入理解C语言的底层机制以及进行编译器开发的爱好者来...
1. 编译器:编译器是一种特殊的程序,它的主要任务是将源代码(高级语言)转换为目标代码(机器语言或汇编语言)。C语言编译器就是这样的一个工具,例如GCC(GNU Compiler Collection)。 2. 编译过程:编译过程通常...
6. **目标代码生成**:最后,编译器将优化后的中间代码转换为特定机器架构的机器码,通常是汇编代码,然后汇编器将其转化为二进制可执行文件。 **新手学习编译器** 对于初学者,理解编译器工作原理可以从以下几个...
在编译原理课程中,PL:0语言是一个基础的教学工具,用于帮助学生理解编译器的工作原理。这个实验旨在引导学生构建一个简单的编译器,处理PL:0语言的源代码,并将其转换为等价的机器码或者中间代码。下面我们将深入...
6. **目标代码生成(Code Generation)**:最后,编译器将优化后的中间代码转换为特定平台的目标代码,如汇编语言或字节码。 在MxCompiler的实现中,开发者可能已经考虑了以上这些步骤,并针对Mx语言的特点进行了...
**二、编译器的组成部分** lcc编译器主要由以下几个关键组件构成: 1. **词法分析器(Lexer)**:负责将源代码转换成一系列的标记(tokens),这些标记代表了C语言的基本语法元素,如关键字、标识符、常量等。 2. ...
- 编译器:编译器是将高级语言(如C#)翻译成机器可执行的指令集的软件工具。它将源代码转换为目标代码(通常是汇编或机器码)。 - 解释器与编译器的区别:解释器逐行执行代码,而编译器则一次性将整个程序转换为...
4. **代码生成器(Code Generator)**:生成目标机器代码,可能包括汇编代码或直接二进制代码。 5. **中间代码生成(Intermediate Code Generation)**:在某些编译器中,会先生成一种与具体机器无关的中间代码,...
6. **目标码生成**:最后,编译器将优化后的中间代码转换为目标机器的汇编代码,或者直接生成机器码。 7. **链接器**:在编译器生成的目标文件中,往往包含了对其他模块的引用,链接器的任务就是把这些分散的、含有...
6. **目标代码生成**:最后,编译器将中间代码转化为目标机器的汇编代码或直接生成机器码。这个过程涉及到对目标架构的理解,包括指令集、寻址模式等。 学习TINY编译器的源代码,不仅可以了解编译器的基本工作流程...
C至MIPS汇编编译器是一个用于将高级C语言代码转换为低级MIPS汇编语言的工具。这个过程是编译器工作原理的一部分,它涉及到语法分析、语义分析、中间代码生成、优化以及目标代码生成等多个阶段。本文将深入探讨C语言...
《lmccompiler:深入探索C++构建的LMC汇编编译器》 在编程世界中,汇编语言是计算机程序设计的基础,它允许程序员直接操纵硬件资源,以实现高效且精确的控制。LMC(Little Man Computer)是一种简单的模拟计算机,...
二、C++实现编译器 C++作为一种强类型、静态类型的编程语言,非常适合用于实现编译器。它的模板、类和面向对象特性使得构建复杂的编译器结构变得更为简洁。 1. 词法分析器(Lexer):通常使用正则表达式库(如Boost...
在深入探讨GCC源代码之前,我们需要了解编译器的基本工作流程,它通常分为五个主要阶段:预处理、编译、汇编、链接。 预处理阶段,GCC会处理#include指令,展开宏定义,处理条件编译指令,并生成.i扩展名的中间文件...
5. **代码生成(Code Generation)**:最后,编译器将优化后的中间代码转换为目标机器的汇编代码或直接生成二进制机器代码。 **C++作为开发语言的优势** - **面向对象**:C++支持类和对象的概念,使得代码组织更加...
4. **代码生成**:最后,编译器将抽象语法树转换为目标代码,可能是汇编语言或者直接是机器码。对于这个C语言编译器,生成的可能是与C语言兼容的目标代码,便于进一步的链接和执行。 这个“伪编译器”可能并未实现...
1. **语法分析**:这是编译器的第一步,通常使用词法分析器(lexer)和解析器(parser)。词法分析器将源代码分解成一个个有意义的单元,称为符号或token;解析器则根据这些token构造出源程序的抽象语法树(AST),...