目标:我想编写一个X86_64汇编程序.注意:标记为社区维基
背景:我熟悉C.我以前写过MIPS汇编.我写了一些x86程序集.但是,我想编写一个x86_64汇编程序 - 它应该输出我可以跳转到并开始执行的机器代码(就像在JIT中一样).
问题是:解决这个问题的最佳方法是什么?我意识到这个问题看起来有点大.我想从一个基本的最小集开始:
只是一个基本的设置,使图灵完成.有人做过吗?建议/资源?
像任何其他"编译器"一样,汇编程序最好写成一个词法分析器,输入语言语法处理器.
汇编语言通常比常规编译语言更容易,因为您不需要担心跨越行边界的构造,并且格式通常是固定的.
两年前我为一个(虚构的)CPU编写了一个汇编程序用于教育目的,它基本上将每一行视为:
:loop).mov).ax,$1).最简单的方法是确保令牌易于区分.
这就是我制定标签必须开始的规则的原因:- 它使得对线的分析变得更加容易.处理生产线的过程是:
;在字符串外面到行尾).您可以轻松地坚持不同的操作数也有特殊的标记,让您的生活更轻松.所有这一切都假设您可以控制输入格式.如果您需要使用英特尔或AT&T格式,那就更难了.
就我走近它是有是得到了所谓的(例如,一个简单的每个操作功能doJmp,doCall,doRet)和功能上有什么被允许在操作数决定.
例如,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);
Run Code Online (Sandbox Code Playgroud)
这些hlpr...函数只需要操作数并返回一个包含指令的字节数组.当许多操作具有类似的操作数要求时,它们很有用,例如adc ,add和and`在上述情况下都需要两个寄存器操作数(第二个参数控制了为指令返回的操作码).
通过使操作数的类型易于区分,您可以检查提供的操作数,它们是否合法以及要生成哪些字节序列.将操作分离为它们自己的函数提供了一个很好的逻辑结构.
此外,大多数CPU遵循从操作码到操作的合理逻辑转换(以使芯片设计者更容易生活),因此在所有允许例如索引寻址的操作码上将进行非常类似的计算.
为了在允许可变长度指令的CPU中正确创建代码,您最好在两次通过中执行此操作.
在第一遍中,不生成代码,只生成指令长度.这允许您在遇到所有标签时为其分配值.第二遍将生成代码,并且可以填充对这些标签的引用,因为它们的值已知.的ignoreVars在该代码段上面用于此目的(被返回的代码的字节序列,所以我们可以知道的长度但以符号的任何引用只是使用0).