代码之家  ›  专栏  ›  技术社区  ›  anon

如何编写x86汇编程序?

  •  5
  • anon  · 技术社区  · 16 年前

    目标:我想写一个x86_64汇编程序。注意:标记为社区wiki

    背景:我熟悉C。我以前写过MIPS汇编。我写了一些x86程序集。但是,我想编写一个x86 Assembler——它应该输出我可以跳转到的机器代码并开始执行(就像在JIT中一样)。

    问题是:最好的方法是什么?我知道这个问题看起来很难解决。我想从一个基本的最小集合开始:

    • 载入寄存器
    • 寄存器上的算术运算(仅整数是好的,还不需要处理fpu)
    • 条件句
    • 跳跃

    只是一个基本的设置,使它图灵完成。有人这么做吗?建议/资源?

    2 回复  |  直到 14 年前
        1
  •  8
  •   paxdiablo    14 年前

    汇编程序和其他任何“编译器”一样,最好是作为一个词汇分析器来编写的,它将输入到语言语法处理器中。

    汇编语言通常比常规编译语言容易,因为您不需要担心跨行边界的构造,而且格式通常是固定的。

    两年前,为了教育目的,我为一个(虚拟的)CPU编写了一个汇编程序,它基本上将每一行处理为:

    • 可选标签(例如, :loop )
    • 操作(例如, mov )
    • 操作数(例如, ax,$1 )

    最简单的方法是确保令牌易于识别。

    这就是为什么我制定了标签必须以的规则 : -这使得对生产线的分析变得容易得多。处理生产线的过程是:

    • 删除注释(首先 ; 在字符串外到行尾)。
    • 提取标签(如果有)。
    • 首先是操作。
    • 其余的是操作数。

    你可以很容易地坚持不同的操作数也有特殊的标记,使你的生活更容易。所有这些都假定您可以控制输入格式。如果你被要求使用Intel或AT&T格式,那就有点困难了。

    我的方法是有一个简单的每操作函数被调用(例如, doJmp , doCall , doRet )这个函数决定了操作数中允许什么。

    例如, 多考尔 只允许数字或标签, 多丽特 什么也不允许。

    例如,下面是 encInstr 功能:

    private static MultiRet encInstr(
        boolean ignoreVars,
        String opcode,
        String operands)
    {
        if (opcode.length() == 0) return hlprNone(ignoreVars);
        if (opcode.equals("defb"))  return hlprByte(ignoreVars,operands);
        if (opcode.equals("defbr")) return hlprByteR(ignoreVars,operands);
        if (opcode.equals("defs"))  return hlprString(ignoreVars,operands);
        if (opcode.equals("defw"))  return hlprWord(ignoreVars,operands);
        if (opcode.equals("defwr")) return hlprWordR(ignoreVars,operands);
        if (opcode.equals("equ"))   return hlprNone(ignoreVars);
        if (opcode.equals("org"))   return hlprNone(ignoreVars);
    
        if (opcode.equals("adc"))   return hlprTwoReg(ignoreVars,0x0a,operands);
        if (opcode.equals("add"))   return hlprTwoReg(ignoreVars,0x09,operands);
        if (opcode.equals("and"))   return hlprTwoReg(ignoreVars,0x0d,operands);
    

    这个 hlpr... 函数只获取操作数并返回包含指令的字节数组。当许多操作具有类似的操作数需求时,例如ADC,它们很有用。 , 添加 and 和`在上述情况下都需要两个寄存器操作数(第二个参数控制为指令返回的操作码)。

    通过使操作数的类型易于区分,可以检查提供的操作数、它们是否合法以及要生成的字节序列。将操作分离为它们自己的函数提供了一个良好的逻辑结构。

    此外,大多数CPU都遵循从操作码到操作码的合理逻辑转换(以使芯片设计人员更容易生活),因此在所有允许索引寻址的操作码上都会有非常相似的计算。

    为了在允许可变长度指令的CPU中正确地创建代码,最好在两次传递中完成。

    在第一步中,不要生成代码,只生成指令的长度。这允许您在遇到所有标签时为其指定值。第二遍将生成代码,并可以填充对这些标签的引用,因为它们的值是已知的。这个 ignoreVars 在上面的代码段中,用于此目的(返回了代码的字节序列,这样我们就可以知道长度,但对符号的任何引用都只使用了0)。

        2
  •  6
  •   Nikolai Fetissov    16 年前

    不要让你泄气,但已经有了 many assemblers 各种各样的铃声和口哨声。请考虑为现有的开源项目做出贡献,例如 elftoolchain .