我写过一些汇编器和反汇编器,我不建议从x86开始学习。如果您已经了解x86或其他指令集,您可以在短时间内(一个晚上/下午)掌握并学习另一个指令集的语法,至少其中大部分语法。编写汇编器(或反汇编器)的行为将确实快速教授您一个指令集,并且相比于那些没有在该级别上检查微码的经验丰富的汇编程序员而言,您将更好地了解该指令集。msp430、pdp11和thumb(不是thumb2扩展)(或mips或openrisc)都是一个很好的起点,它们都没有太多的指令,也没有过度复杂等。
我建议先使用反汇编器,并选择一个固定长度指令集,例如arm、thumb、mips或openrisc等。如果不是这样,那么至少要使用反汇编器(一定要选择您已经拥有汇编器、链接器和反汇编器的指令集),并使用铅笔和纸来理解机器代码和汇编之间的关系,特别是分支,它们通常有一个或多个怪癖,比如偏移量加上时程序计数器会提前一两个指令,以获取另一个位,它们有时测量整个指令而不是字节。
用C程序暴力解析文本非常容易。更困难的任务可能是使用bison/flex来学习该编程语言,以允许这些工具创建(甚至更极端的)解析器,然后与您的代码接口,告诉您在哪里找到了什么。
汇编器本身相当直接,只需读取ASCII并设置机器码中的位即可。分支和其他pc相关指令可能会更痛苦,因为它们可能需要多次通过源/表进行完全解析。
mov r0,r1
mov r2 ,#1
汇编器开始解析文本行(定义为紧跟回车符0xD或换行符0xA后的字节),丢弃空格(空格和制表符),直到遇到非空格字符,然后使用已知助记符进行strncmp比较。如果匹配成功,则解析该指令的可能组合,在上面简单的示例中,在mov之后跳过空格到非空格,也许你所找到的第一件事必须是一个寄存器,然后是可选的空格,然后是逗号。移除空格和逗号并将其与字符串表进行比较,或者只需解析它即可。完成该寄存器后,继续查找逗号所在位置,假设找到的是另一个寄存器或立即数。如果是立即数,假设它必须有一个“#”符号;如果是寄存器,假设它必须以小写或大写字母“r”开头。解析该寄存器或立即数后,然后确保该行上没有不应出现在该行上的其他内容。为此指令构建机器代码或尽可能多地构建,然后继续下一行。解析ASCII可能会很繁琐,但并不困难...
最少你需要一个表/数组来累积创建的机器代码/数据,另外还需要一些方法来标记指令为不完整,以便在未来的通过中完成相对PC指令。同时还需要一个表/数组来收集找到的标签及其在发现时在机器代码表中的地址/偏移量。以及作为目的地或源使用的指令中使用的标签和它们所在的部分完成的指令所持有的表/数组中的偏移量。第一遍后,再次遍历这些表,直到将所有标签定义与用作源或目的地的标签匹配起来,使用标签定义地址/偏移量计算到所讨论的指令的距离,然后完成创建该指令的机器代码。(可能需要一些反汇编和/或使用某些其他方法来记住当你稍后返回它时什么类型的编码)。
下一步是允许多个源文件,如果您想要实现这个功能的话。现在必须有不会被汇编器解决的标签,因此必须在输出中留下占位符并创建某种最长跳转/分支指令,因为您不知道目标距离有多远,准备最坏情况。然后是选择创建/使用的输出文件格式,然后是链接程序,这大部分都很简单,但是您必须记住填写最终pc相对指令的机器代码,这与在汇编器本身中所需的相同。
请注意,编写汇编器并不一定涉及创建编程语言,然后为其编写编译器,这是两个不同的问题,存在不同的问题。实际上,如果您要制作一种新的编程语言,只需使用现有的基于现有指令集的汇编器即可。当然,这不是必需的,但是大多数教学和教程将使用bison / flex方法用于编程语言,并且有许多大学课程讲义/资源可用于入门编译器课程,您可以使用它们来开始,然后修改脚本以添加您的语言特性。中间和后端比前端更具挑战性。有很多关于这