2通道汇编程序与单程汇编程序在解决未来符号方面有何不同?

时间:2012-04-20 10:24:03

标签: assembly

这是我不明白的两个问题:

  1. One-Pass汇编程序如何解决未来的符号问题?

  2. 在这方面,双通道汇编程序与单通道汇编程序有何不同?

    它是在第一次通过还是第二次通过时解决?如果它在第二遍中执行,它实际上与单通道汇编程序有什么不同?如果它在第二次传球中这样做,为什么不在第一次传球中呢?

4 个答案:

答案 0 :(得分:33)

阅读此PDF。它逐步解释了单通道和多通道装配器的工作原理。它还解释了两者的利弊以及两者之间的差异。

什么是单程汇编程序?

它是一种 Load-and-go 类型的汇编程序,它通常直接在内存中生成目标代码以立即执行!它只扫描您的源代码一次并完成。弗鲁姆...

很酷,如果真的这么神奇,为什么我们需要多通道装配工?

转发参考!也就是说,当一遍汇编程序沿着源代码进行编程时,它会以未定义的数据符号和未定义的标签(跳转地址)的形式遇到一些 陌生人 。你的汇编人员问这些陌生人他们是谁?陌生人说“我们稍后会告诉你的!”(前言参考)你的汇编器生气并告诉你完全消除这些陌生人。但这些陌生人是你的朋友,你不能完全消除它们。 因此,您与汇编程序 达成妥协协议。您承诺在使用它们之前定义所有变量。汇编程序无法在此方面妥协,因为它甚至无法为未定义的数据符号保留临时存储,因为它不知道它们的大小。数据可以有不同的大小

如果它像

那样
PAVAN EQU SOMETHING

; Your code here
 mov register, PAVAN


; SOMETHING DB(or DW or DD) 80 ; varying size data, not known before

就其本身而言,您的汇编程序同意在未定义的跳转标签上妥协。由于跳转标签只是地址和地址大小可以先验已知,因此汇编程序可以为未定义的符号保留一些确定的空间。

如果喜欢这个

      jump AHEAD


 AHEAD add reg,#imm

汇编程序将jump AHEAD翻译为0x45 **0x00 0x00**0x45jump的操作码,为AHEAD地址保留了4个字节

好的,现在告诉我一个传递汇编程序是如何工作的

简单,在路上,如果汇编程序遇到未定义的标签,它会将符号表与未定义符号的值必须放置的地址放在一起,当找到符号时将来 。它对所有未定义的标签执行相同的操作,并且当它看到这些未定义的符号的定义时,它会在表中添加它们的值(从而定义该标签),并在其先前保留临时存储的内存位置添加它们的值。

现在在解析结束时,如果还有更多可怜的灵魂仍然处于未定义状态,汇编程序会发出错误和错误:(如果没有任何未定义的标签,那么你就去吧!

enter image description here

一秒钟,我忘记了为什么我们需要一个2或多通道汇编程序?它们是如何工作的?

如上所述,一次通过汇编程序无法解析数据符号的正向引用。它要求在使用之前定义所有数据符号。双通道汇编程序通过专门解析所有(数据/标签)前向引用,然后在下一次传递中没有麻烦地生成目标代码来解决这个难题。

如果数据符号依赖于另一个,而另一个依赖于另一个,则汇编程序以递归方式解析此数据符号。如果我在这篇文章中尝试解释,那帖子就会变得太大了。阅读此ppt了解更多详情

嗯..有趣。这两个汇编程序是否有更多的优点?

是。它可以检测重新定义等事情。

PS :我在这里可能不是100%正确。我希望听到任何建议,使其成为更好的职位。

答案 1 :(得分:10)

一次通过汇编程序生成代码,对于任何未定义的符号,留下要填充的槽,并在表或其他数据结构中记住它。然后,在定义符号的位置,使用表格中的信息在正确的位置填充其值。

传统上使用双遍汇编程序的原因是目标程序不适合内存,而不考虑源代码。从打孔磁带读取器逐行读取巨大的源程序,并将标签表保存在内部存储器中。 (我实际上是在ISIS上完成的,这是英特尔的第一个开发系统,带有8080.) 第二次从源磁带开始再次读取,但所有标签的值都是已知的,并且在读取每一行时,目标程序被打孔到磁带。在内存不足的16位Intel 8086系统上,这仍然是一种有用的技术,可以使用大量文档的源文件,该文件可以大于64 KB,用硬盘或软盘代替纸带。

现在没有必要进行两次传递,但这种架构仍在使用中。它稍微简单一些,代价是I / O.

答案 2 :(得分:9)

考虑汇编程序的一种方法是想象它们计算分配给顺序增加内存位置的一系列表达式的值。表达式通常可以包括符号的值,对符号,常量和特殊变量进行的一些算术,例如"当前位置计数器" (通常用一个有趣的名字写成,例如" $"),或真正特殊的表达式,其语法是机器指令的语法。

请注意,表达式可能会生成一个填充多个连续内存位置的值;机器指令倾向于这样做,但是为字符串文字,多精度数字,初始化结构等提供表达式很有用。这只会影响簿记细节,但不会改变汇编程序的抽象内容。

要计算每个表达式的最终值,汇编程序必须知道可能涉及的任何符号的值。它仅以几种方式发现符号值。首先,符号值可以定义为表达式的结果。其次,可以为符号值分配当前位置计数器的值;典型的汇编程序在符号写入"标签"位置。在此类发现中,汇编程序将符号名称及其值记录在符号表中,以用于计算表达式。

汇编程序面临的一个关键问题是生成表达式的值,尚未遇到符号的所有定义。假设一个符号没有在某个特定的行中定义,它将在汇编器最终处理的某个后续行中定义。

两遍汇编程序尝试计算每个表达式遇到它时的值,在两个名为" first"和"第二"经过。在第一次传递期间,如果表达式中存在未定义的符号(假定为前向引用),则汇编器只是替换虚拟值(通常为零);无论如何,它计算表达式的值。如果正在处理机器指令或数据常量,则忽略结果,但 size 用于推进位置计数器以启用标签值分配。如果遇到标签,则将其值设置为当前位置计数器。如果符号分配" A EQU"遇到时,符号值被设置为表达式的结果;如果表达式包含未定义的符号,则汇编器将发出错误。如果找到原始陈述" ORG",则将其视为一个人写了" $ EQU"。在第一遍结束时,所有标签都已分配值;任何没有值的符号都标记为" undefined"在符号表中。第二遍重复第一遍的表达式评估,但不重复(重新)定义任何符号;由于所有符号都是(预期)定义的,因此表达式值是正确的并且会发送到输出流。在表达式中找到的任何未定义符号都会导致"未定义的符号"投诉。

单遍汇编程序在遇到表达式时会尝试计算每个表达式的值。如果表达式仅包含已定义的符号,则汇编器可以对其进行求值并生成最终值,并将该信息写入其输出流。 (这里的另一个答案表明,有些人通过汇编程序将他们的答案写入记忆。这只是一个特例)。如果表达式包含未定义的符号,则汇编器将存储一对(位置,表达式),以便稍后在符号定义时或在汇编结束时进行重新处理。某些表达式(例如设置位置计数器的表达式)不能包含未定义的符号;汇编程序会在这种情况下抱怨。

因此,棘手的部分是存储未解析的表达式,并决定何时重新评估它。存储表达式的一种方法是简单地保留文本;另一个是为表达构建相当于(反向)波兰表示法的内容。要确定何时需要重新计算表达式,可以将其与它包含的未定义符号相关联;然后,当一个符号被定义时,重新评估相应的未解析表达式,发出完整的表达式,并再次留下未解析的表达式进行再处理。或者,汇编程序可以简单地保存所有表达式,直到它遇到输入结束;此时,应定义所有符号,因此它应该能够确定每个表达式的最终值。人们可以根据存储前向引用表达式所需的内存量来选择这两种技术。

在上个世纪,我构建了一个在8k字节计算机上运行的单程汇编程序,它使用波兰语表达式表示。在定义符号时,评估波兰语表达式并计算任何可计算的子表达式,将得到的波兰语简化为最终值或较小的波兰语表达式,仅涉及未定义符号上的运算符。未定义值的符号表条目具有与未定义符号对应的所有波兰表达式槽的链表;当遇到符号定义时,链接列表的所有元素都会更新,并且波兰语表达式会在发生时重新进行评估。这使得波兰语表达式的大小尽可能小,并在定义所有符号时将其删除。这个汇编程序在小型机器上处理了十万行程序。在这么小的机器上进行一次通道汇编程序的原因是源代码来自纸带(Teletype,对于那些你已经足够记住的人而言)并且读取那些纸带甚至曾经非常痛苦和缓慢;第二次不是一个好主意,所以两通汇编程序不是一个合适的选择。

我的一个同事后来建立了一个有趣的两通道汇编程序。他没有处理 text 两次,而是在第一次传递时将文本标记(存储在内存中)以及收集符号值。传递两个处理了标记化文本。 这是一个非常快速的两次传递汇编程序。他有更多的记忆力。

答案 3 :(得分:-2)

未来符号问题表示符号在定义之前使用。

  • 这是可能的,因为在汇编语言程序中,程序员可以定义符号,如果符号在它定义之前使用它被称为“前向引用”

  • 所以一次从转换成机器(二进制)语言,汇编程序获得操作码-Reg-X2-B2但它不能在符号表中获得D2(位移)条目,所以它不可能转换为机器语言(二进制),它被称为“前向参考问题”。

  • 解决这个2通道汇编程序是定义。

**在第一次通过(第一次扫描)时,它将符号(标签)的条目放入符号表中。 **并在第二次通过(第二次扫描)汇编程序将其转换为机器语言(二进制)。这很简单:)