首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >制作汇编器的设计模式

制作汇编器的设计模式
EN

Stack Overflow用户
提问于 2011-04-08 03:49:28
回答 4查看 2.9K关注 0票数 8

我在做一个8051汇编程序。

在一切之前都是一个标记器,它读取下一个令牌,设置错误标志,识别EOF等。

然后是编译器的主循环,它读取下一个标记并检查有效的助记符:

代码语言:javascript
复制
mnemonic= NextToken();
if (mnemonic.Error)
{
    //throw some error
}
else if (mnemonic.Text == "ADD")
{
    ...
}
else if (mnemonic.Text == "ADDC")
{
    ...
}

它继续着几个案例。更糟糕的是,每个case中的代码都会检查有效参数,然后将其转换为编译后的代码。现在它看起来是这样的:

代码语言:javascript
复制
if (mnemonic.Text == "MOV")
{
    arg1 = NextToken();
    if (arg1.Error) { /* throw error */ break; }
    arg2 = NextToken();
    if (arg2.Error) { /* throw error */ break; }

    if (arg1.Text == "A")
    {
        if (arg2.Text == "B")
            output << 0x1234; //Example compiled code
        else if (arg2.Text == "@B")
            output << 0x5678; //Example compiled code
        else
            /* throw "Invalid parameters" */
    }
    else if (arg1.Text == "B")
    {
        if (arg2.Text == "A")
            output << 0x9ABC; //Example compiled code
        else if (arg2.Text == "@A")
            output << 0x0DEF; //Example compiled code
        else
            /* throw "Invalid parameters" */
    }
}

对于每个助记符,我必须检查有效的参数,然后创建正确的编译代码。在每种情况下检查每个助记重复的有效参数的非常相似的代码。

那么有没有一种设计模式来改进这段代码呢?

或者仅仅是一种更简单的实现方式?

编辑:我接受了plinth的回答,感谢他。不过,如果你对此有想法,我很乐意向你学习。谢谢大家。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2011-04-08 04:11:01

多年来,我已经编写了许多用于手动解析的汇编器,坦率地说,您最好使用语法语言和解析器生成器。

这就是为什么-一个典型的装配线可能看起来像这样:

代码语言:javascript
复制
[label:] [instruction|directive][newline]

一条指令是:

代码语言:javascript
复制
plain-mnemonic|mnemonic-withargs

一条指令是:

代码语言:javascript
复制
plain-directive|directive-withargs

等。

有了像Gold这样不错的解析器生成器,您应该能够在几个小时内完成8051的语法。与手工解析相比,这种方法的优势在于,您可以在汇编代码中使用足够复杂的表达式,例如:

代码语言:javascript
复制
.define kMagicNumber 0xdeadbeef
CMPA #(2 * kMagicNumber + 1)

这可能是一个真正的熊做手工。

如果您想手动完成,请制作一个表,其中包含所有助记符,其中还将包括它们支持的各种允许的寻址模式,以及每个寻址模式、每个变体将占用的字节数和它的操作码。如下所示:

代码语言:javascript
复制
enum {
    Implied = 1, Direct = 2, Extended = 4, Indexed = 8 // etc
} AddressingMode; 

/* for a 4 char mnemonic, this struct will be 5 bytes.  A typical small processor
 * has on the order of 100 instructions, making this table come in at ~500 bytes when all
 * is said and done.
 * The time to binary search that will be, worst case 8 compares on the mnemonic.
 * I claim that I/O will take way more time than look up.
 * You will also need a table and/or a routine that given a mnemonic and addressing mode
 * will give you the actual opcode.
 */

struct InstructionInfo {
    char Mnemonic[4];
    char AddessingMode;
}

/* order them by mnemonic */
static InstructionInfo instrs[] = {
    { {'A', 'D', 'D', '\0'}, Direct|Extended|Indexed },
    { {'A', 'D', 'D', 'A'}, Direct|Extended|Indexed },
    { {'S', 'U', 'B', '\0'}, Direct|Extended|Indexed },
    { {'S', 'U', 'B', 'A'}, Direct|Extended|Indexed }
}; /* etc */

static int nInstrs = sizeof(instrs)/sizeof(InstrcutionInfo);

InstructionInfo *GetInstruction(char *mnemonic) {
   /* binary search for mnemonic */
}

int InstructionSize(AddressingMode mode)
{
    switch (mode) {
    case Inplied: return 1;
    / * etc */
    }
 }

然后,您将拥有每条指令的列表,而该列表又包含所有寻址模式的列表。

因此,您的解析器如下所示:

代码语言:javascript
复制
char *line = ReadLine();
int nextStart = 0;
int labelLen;
char *label = GetLabel(line, &labelLen, nextStart, &nextStart); // may be empty
int mnemonicLen;
char *mnemonic = GetMnemonic(line, &mnemonicLen, nextStart, &nextStart); // may be empty
if (IsOpcode(mnemonic, mnemonicLen)) {
    AddressingModeInfo info = GetAddressingModeInfo(line, nextStart, &nextStart);
    if (IsValidInstruction(mnemonic, info)) {
        GenerateCode(mnemonic, info);
    }
    else throw new BadInstructionException(mnemonic, info);
}
else if (IsDirective()) { /* etc. */ }
票数 8
EN

Stack Overflow用户

发布于 2011-04-08 03:55:48

我认为你应该调查一下访问者模式。它可能不会使您的代码变得更简单,但会减少耦合并提高可重用性。SableCC是一个java框架,用于构建广泛使用它的编译器。

票数 0
EN

Stack Overflow用户

发布于 2011-04-08 03:55:49

您看过"Command Dispatcher“模式吗?

http://en.wikipedia.org/wiki/Command_pattern

一般的想法是创建一个处理每个指令(命令)的对象,并创建一个将每个指令映射到处理程序类的查找表。每一个命令类都有一个公共的接口(例如Command.Execute( *args )),这肯定会给你一个比你目前庞大的switch语句更干净/更灵活的设计。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/5586747

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档