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

Suh*_*pta 17 assembly

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

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

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

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

Pav*_*ath 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
Run Code Online (Sandbox Code Playgroud)

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

如果是这样的话

      jump AHEAD


 AHEAD add reg,#imm
Run Code Online (Sandbox Code Playgroud)

汇编程序翻译jump AHEAD0x45 **0x00 0x00**.0x45jumpAHEAD地址保留的4个字节的操作码

好的,现在告诉我一个传递汇编程序的工作原理

很简单,在路上,如果汇编程序遇到未定义的标签,它会将符号表与未定义符号值必须放置的地址一起放入符号表中.它对所有未定义的标签都是一样的,并且当它看到这些未定义的符号的定义时,它会在表中添加它们的值(从而定义该标签),并在它之前保留临时存储的内存位置.

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

在此输入图像描述

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

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

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

嗯..有趣.这两个通过汇编程序有什么优势吗?

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

PS:我在这里可能不是100%正确.我很乐意听到任何建议,使其成为更好的帖子.

  • -1一次通过汇编程序CAN和DO"解析对数据符号的前向引用"就好了,否则它们就没用了.看我的回答.(解决这个"递归"?没有必要). (2认同)

Alb*_*rst 10

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

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

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

  • 很简洁的回答,我喜欢它...我看到你的低分,请花更多的时间在SO提供更好的答案.(它不是关于IMO的分数,而是关于分享丰富的知识) (2认同)
  • dwelch:"仍然让你陷入困境......"尽管如此,但编程的动态有所改变.我在一台8 GB的RAM机器上打字.它无法在RAM中保存一个名称表的想法预示着一个非常荒谬的大型程序,从未编写过,永远不会被编写. (2认同)

Ira*_*ter 9

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

请注意,表达式可能会生成一个填充多个连续内存位置的值; 机器指令倾向于这样做,但是对于字符串文字,多精度数字,初始化结构等具有表达式是有用的.这仅影响簿记细节,但不会改变汇编器在抽象中所做的事情.

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

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

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

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

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

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

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