创建汇编程序的好方法?

时间:2014-01-04 15:38:48

标签: c assembly

我要为我的学校做一些功课。目标是创建一个真正基本的虚拟机以及一个简单的汇编程序。我没有创建虚拟机的问题,但我想不出一个“很好”的方法来创建汇编程序。

这个汇编程序的语法非常基本:可选标签后跟冒号,然后是助记符,后跟1,2或3个操作数。如果有多个操作数,则用逗号分隔。此外,只要空格不出现在单词的中间,就会忽略空格。

我确信我可以用strtok()和一些黑魔法做到这一点,但我更愿意以“干净”的方式做到这一点。我听说过Parse Trees / AST,但我不知道如何将汇编代码转换成这些结构。

6 个答案:

答案 0 :(得分:4)

我在十几岁的时候写过一个这样的汇编程序。您根本不需要复杂的解析器。

您需要做的就是每行五个步骤:

  1. 令牌化(即将线分成令牌)。这将为您提供一系列令牌,然后您不必担心空格,因为您将在标记化过程中删除它。
  2. 将代表行部分的一些变量初始化为NULL
  3. 一系列if语句,用于遍历令牌数组并检查该行的哪些部分。如果它们存在则将令牌(或其处理版本)放在相应的变量中,否则将该变量保留为NULL(即什么都不做)。
  4. 报告任何语法错误(即不允许的令牌类型组合)。
  5. 代码生成 - 我想你知道如何做这个部分!

答案 1 :(得分:2)

您正在寻找的实际上是词法分析,最后解析编译代码的生成。有很多框架可以帮助创建/生成像Gold ParserANTLR这样的解析器。创建语言定义(并学习如何依赖于您使用的框架)通常需要做很多工作。

我认为你最好实施shunting yard algorithm。它将您的源转换为计算机可以理解的表示,这使您可以轻松理解虚拟机。

我还想说,潜入解析器,抽象语法树,网上提供的所有工具以及阅读有关此主题的大量论文都是非常好的学习经历!

答案 2 :(得分:2)

您可以查看一些已经制作的汇编程序,例如PASMO:Z80 CPU的汇编程序,并从中获取想法。这里是: http://pasmo.speccy.org/

我写了几个非常简单的汇编程序,它们都使用字符串操作和strtok()等。对于像汇编语言这样的简单语法,它就足够了。我的装配工的关键部分是:

符号表:只是一个结构数组,带有符号名称及其值。

typedef struct
{
    char nombre[256];
    u8 valor;
} TSymbol;

TSymbol tablasim[MAXTABLA];
int maxsim = 0;

符号只是一个与值相关联的名称。该值可以是当前位置(下一条指令将被组装的地址),也可以是EQU伪指令赋予的显式值。

此实现中的符号名称每个限制为255个字符,一个源文件仅限于MAXTABLA个符号。

我对源代码执行了两次传递:

第一个是识别符号并将它们存储在符号表中,检测它们是否后跟EQU指令。如果存在,则解析EQU旁边的值并将其分配给该符号。在其他情况下,分配当前位置的值。要更新当前位置,我必须检测是否有有效指令(虽然我还没有组装)并按顺序更新(这对我来说很容易,因为我的CPU有一个固定的指令大小)。

这里有一个我的代码示例,它负责使用当前位置的EQU更新符号表,并在需要时提前当前位置。

case 1:
    if (es_equ (token))
    {
        token = strtok (NULL, "\n");
        tablasim[maxsim].valor = parse_numero (token, &err);
        if (err)
        {
            if (err==1)
                fprintf (stderr, "Error de sintaxis en linea %d\n", nlinea);
            else if (err==2)
                fprintf (stderr, "Simbolo [%s] no encontrado en linea %d\n", token, nlinea);
            estado = 2;
        }
        else
        {
            maxsim++;
            token = NULL;
            estado = 0;
        }
    }
    else 
    {
        tablasim[maxsim].valor = pcounter;
        maxsim++;
        if (es_instruccion (token))
            pcounter++;
        token = NULL;
        estado = 0;
    }
    break;

第二遍是我实际组装指令的地方,当我找到符号时用它的值替换符号。这很简单,使用strtok()将一行分成其组件,并使用strncasecmp()将我发现的内容与指令助记符进行比较

答案 3 :(得分:1)

如果操作数可以是表达式,例如“1<<(x + 5)”,则需要编写解析器。如果没有,解析器非常简单,您无需考虑这些术语。对于每一行获取第一个字符串(跳过空格)。字符串是否以冒号结尾?然后它是一个标签,否则就是menmonic。等

答案 4 :(得分:1)

对于汇编程序,几乎不需要构建显式的解析树。一些汇编程序确实有能够在链接时解析复杂表达式的花哨链接器,但对于基本汇编程序而言,ad-hoc词法分析器和解析器应该可以正常工作。

本质上你会写一个小词法分析器,它逐个字符地使用输入文件并将所有内容分类为简单的标记,例如数字,标签,操作码和特殊字符。

即使您没有使用代码生成器,我也建议编写BNF语法。然后,该规范可以被翻译成几乎被写入的递归式解析器。解析器只是遍历整个代码并沿途发出汇编的二进制代码。

还需要一个符号表来注册每个标签及其值,传统上将其作为哈希表实现。最初遇到未知标签(例如前向分支)时,您可能还不知道该值。所以它只是归档以供将来参考。

然后诀窍是第一次为标签和表达式吐出虚拟值,但在程序计数器递增时计算标签地址,然后在整个文件中再次传递以填充实际值。

对于简单的汇编程序,例如没有链接器或宏设施和简单的指令集,你可以使用大约一千行代码。大部分都是从语法描述和操作码表中无脑无法翻译。

哦,我强烈建议您尽快从当地大学图书馆查看the dragon book

答案 5 :(得分:0)

至少根据我的经验,普通的词法分析器/解析器生成器(例如,flex,bison / byacc)对于此任务来说都是无用的。

当我完成它的时候,几乎整个事情都是由表驱动的 - 通常是一个助记符表,并且对于每个表中的一组索引到指令格式表中,指定哪些格式是可能的指令。根据具体情况,在每个操作数而不是每个指令的基础上执行此操作是有意义的(例如,对于源和目标都具有相当大的可能格式集的mov指令)。

在典型情况下,您必须查看操作数的格式以确定特定指令的指令格式。对于一个相当典型的示例,#x格式可能表示立即值,x表示直接地址,@x表示间接地址。间接地址的另一种常见形式是(x)[x],但对于您的第一个汇编程序,我会尝试坚持一种格式,该格式仅根据操作数的第一个字符指定指令格式/寻址模式,如果可能的话。

解析标签更简单,并且(大部分)是分开的。基本上,每个标签只是一个带地址的名称。

顺便说一下,如果可能的话,我可能会遵循以冒号(“:”)结尾的标签的典型格式,而不是分号(“;”)。更常见的是,分号将标志着评论的开始。