一旦我们构建了一台计算机,下一步就是开发一种汇编语言,然后是一个可以汇编我们程序的汇编器。
在我之前的专栏中,我们介绍了在计算机内存中存储多字节(或在我们的例子中是多 nybble)数据块的大端与小端方式的概念。我们还概述了计算机设计者可能决定支持的一些寻址模式。现在是时候开始为我们的 4 位 HRRG 计算机考虑汇编语言和汇编程序了。
作为我们讨论的起点,让我们进行一个简单的思想实验。假设我们刚刚完成了 4 位 HRRG 计算机的构建。我们还假设它是地球上的第一台计算机;也就是说,没有任何其他计算机,或编程语言,或者……嗯,任何东西,真的。
让我们对自己大方一点,假设我们还开发了一些输入和输出设备——看起来有点像 QWERTY 键盘、VT100 终端和纸带阅读器/写入器——并且我们已经将它们连接到了一些 HRRG输入和输出端口,但我们还没有创建任何代码来驱动小流氓。
在继续本专栏的其余部分之前,为什么不暂停片刻来思考您的下一步是什么。
以机器代码捕获和输入程序
作为快速提醒,HRRG 有 16 个寄存器并支持 16 条指令,如下所示(关于各种指令执行其魔法的方式的更详细讨论在我们的“指令集”和“指令权衡”列)。
我们的首要任务是创建一个非常非常简单的程序,只是为了确保这个野兽能够正常工作。如果我们决定使用铅笔和纸来绘制带有相关注释的流程图来捕捉该程序的意图,我不会感到惊讶,如下图所示:
下一步将是计算出我们想要将哪些操作码和操作数加载到计算机内存中以实现我们的程序。再一次,这可能涉及铅笔和纸以及一些皱眉和挠头,导致如下所示:
这种类型的表示被称为“机器代码”,因为这些是我们的计算机(机器)将执行(处理)的二进制代码。
最后但并非最不重要的一点是,我们希望将机器代码加载到我们的计算机中并运行程序,但我们将如何做到这一点?好吧,我们可能会构建一个开关面板并将其连接到计算机。至少,这将涉及 12 个用于表示地址的拨动开关,4 个用于表示数据的拨动开关,以及几个控制开关和按钮,如下图所示。
为了进入程序,我们将“Program/Run”开关设置为“Program”,在地址开关上设置一个地址,在数据开关上设置一个相应的操作码或操作数,然后按下“Load”按钮将此值加载到内存中。我们将对构成我们程序的所有 nybbles 重复此过程。上图显示我们准备将 $C(跳转)操作码输入到内存位置 $106。
输入程序后,我们将地址开关设置为指向程序的起始地址(本例中为 100 美元),然后将“程序/运行”开关切换到“运行”。
用汇编语言捕获程序
许多设计原始计算机的团队认为,为了获得最佳结果,有必要尽可能靠近机器。也就是说,他们的理念是以尽可能接近机器内部表示的形式编写程序;即机器码。
然而,正如您可能想象的那样,以机器代码捕获和输入程序是耗时的、容易出错的,并且——最终——在下面的区域中是一种痛苦。抽象阶梯的下一步是用称为汇编语言的低级符号编程语言捕获程序,其中程序语句和计算机的机器代码指令之间存在非常强的对应关系。(英国数学家 Kathleen Booth 根据她 1947 年开始的理论工作发明了汇编语言的概念。)
当然,拥有汇编语言与拥有汇编程序不同,汇编程序一词是指将汇编源代码转换为可执行机器代码的实用程序。在我们的思想实验中,我们仍处于使用铅笔和纸捕捉程序的阶段。
假设我们已经定义了 HRRG 汇编语言(我们将在下一篇专栏中更详细地讨论这种语言)。在这种情况下,我们可以使用铅笔和纸以汇编语言捕获我们的程序,并将其手动组装成机器代码。让我们考虑一下在我们的原始测试程序的情况下这可能是什么样子,如下图所示:
非常有用的一件事是将标签与关键指令的地址相关联,例如标记循环开始的 LOOP 标签。在执行程序时,我们将构建一个标签及其地址的交叉引用表,如上图右上角所示。
对于我们的简单程序,我们在使用标签之前声明了它们,这让我们的生活变得轻松。在一个更复杂的程序中,我们可能会在声明它之前引用一个标签(例如,跳转到一个标签位于程序下方的子程序)。在这种情况下,我们将对源代码执行多次遍历,其中第一次遍历允许我们确定所有标签的地址,第二次遍历允许我们解析第一次不知道的任何地址-时间循环。
通过我们的引导来提升自己
这就是事情开始变得有趣的地方。首先,我们将创建几个简单的低级实用程序,以允许我们监控键盘并使用我们的纸带阅读器/写入器。我们将通过用铅笔和纸捕获源代码,将其手工组装成机器代码,然后使用我们的开关面板将机器代码加载到计算机的内存中来做到这一点(请注意,我们可以将多个程序存储在记忆)。
大约在这个时候,我们还将创建一个低级监控程序。这样的程序提供了一个简单的用户界面——通常基于单字符命令——以允许用户执行检查和更改内存、读取或写入 I/O 端口以及将控制权转移到内存中的其他程序等操作。再一次,该程序将使用铅笔和纸捕获,手工组装,并使用开关面板加载到计算机的内存中。
接下来,我们将创建一个简单的汇编程序,仅支持我们最终希望拥有的功能的一个子集。和以前一样,这个简单的汇编程序将使用铅笔和纸来捕获,手工组装,然后使用开关面板加载到计算机的内存中。
现在我们准备好摇滚了,因为我们可以使用类似电传终端的东西来捕获我们的第一遍汇编器支持的汇编语言子集中的程序,并将这些源代码程序写入纸带。接下来,我们可以使用监控程序和我们的实用程序从纸带中读取此源代码并将其存储在计算机内存的一个区域中。然后我们可以使用我们的第一遍汇编器将这个源代码汇编成可执行的机器代码并将其存储在另一个内存区域中。此时,我们可以对存储在计算机内存中的可执行机器代码做两件事:
例如,交叉汇编器是一种汇编器,它可以将指令转换为计算机的机器代码,而不是运行它的计算机。我在 HRRG 上的同谋,EEWeb 专家 Joe Farr,创建了一个在 PC 上运行的 HRRG 交叉汇编器,它采用 HRRG 的汇编语言编写的程序,并生成可执行的机器/目标代码以在 HRRG 上运行。下面的两张图片显示了同一程序片段的源代码和目标代码版本。
HRRG 交叉汇编源代码查看(来源:Joe Farr)
HRRG 交叉汇编器目标代码视图(来源:Joe Farr)
更棒的是,HRRG的汇编器是一个宏汇编器,它是一个可以进行宏替换和扩展的汇编器。这允许我们定义由一个或多个语句组成的宏,然后在程序中稍后使用这些宏名称,从而避免重写语句。
举个简单的例子,HRRG 的指令集不包含 HALT 指令。但是,我们可以在 HRRG 的汇编语言中将这样的指令实现为宏,如下所示:
.MACRO HALT
OR %0010, S1
.ENDMACRO
现在,我们可以将前面程序示例中的 OR 指令替换为 HALT,这有助于使程序更易于理解。此外,我们可以将所有宏捆绑到一个单独的文件中,我们可以使用 .INCLUDE 指令将其导入到我们的程序中。
在我的下一篇专栏中,我们将研究 HRRG 的汇编语言,我们还将考虑如何使用宏来实现 HRRG 本身不支持的一堆指令,例如 ADD、SUB、ROL、ROR、ASHL 、ASHR、LSHL 和 LSHR。与此同时,我一如既往地欢迎您提出意见和问题。
审核编辑 黄昊宇
评论
查看更多