我自己写了一些(汇编器和反汇编器),我不会从 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 方法来编程语言,并且有许多大学课程讲义/资源可用于开始编译器类,您可以使用它们来帮助您开始然后修改添加您的语言功能的脚本。中后端是比前端更大的挑战。有很多关于这个主题的书籍和许多在线资源。