这是我不明白的2个问题:
One-Pass Assembler 如何解决未来的符号问题?
在这方面,两遍汇编器与单遍汇编器有何不同?
它是在第一遍还是第二遍中解决?如果它在第二遍中完成,它与一次性汇编器实际上有什么不同?如果它在第二遍中完成,为什么它不在第一遍中执行?
这是我不明白的2个问题:
One-Pass Assembler 如何解决未来的符号问题?
在这方面,两遍汇编器与单遍汇编器有何不同?
它是在第一遍还是第二遍中解决?如果它在第二遍中完成,它与一次性汇编器实际上有什么不同?如果它在第二遍中完成,为什么它不在第一遍中执行?
阅读此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**
. 0x45
是为地址jump
保留的操作码和 4 个字节AHEAD
很简单,如果汇编器在运行过程中遇到未定义的标号,它会在将来找到该符号时将其连同必须放置未定义符号值的地址一起放入符号表中。它对所有未定义的标签执行相同的操作,并且当它看到这些未定义符号的定义时,它会将它们的值添加到表中(从而使该标签定义)和它之前保留临时存储的内存位置。
现在在解析结束时,如果还有更多的可怜的灵魂仍然处于未定义状态,汇编器会发出错误并出错:(如果没有任何未定义的标签,那么你就走吧!
如前所述,一次性汇编器无法解析数据符号的前向引用。它要求在使用之前定义所有数据符号。两遍汇编器通过专门解析所有(数据/标签)前向引用,然后在下一遍中轻松生成目标代码,解决了这一难题。
如果一个数据符号依赖于另一个,而另一个又依赖于另一个,则汇编程序递归地解决这个问题。如果我尝试在这篇文章中解释这一点,这篇文章会变得太大。阅读此ppt了解更多详情
是的。它可以检测重新定义和类似的东西。
PS:我在这里可能不是 100% 正确的。我很想听听任何建议,使它成为一个更好的帖子。
A one pass assembler generates code and for any undefined symbols, leaves a slot to be filled in, and remembers it in a table or other data structure. Then where the symbol is defined, it fills in its value at the right place or places, using the information from the table.
The reason for using a two pass assembler traditionally has been that the target program doesn't fit in memory, leave alone the source. The gigantic source program is read, line by line, from the punch tape reader, and the table of labels is kept in internal memory. (I've actually done that, on ISIS, the first development system of Intel, with an 8080.) The second time around the source tape is again read from the beginning, but the value of all labels is known, and as each line is read, the target program is punched out to tape. On a memory starved 16 bit Intel 8086 system this was still a useful technique to have a heavily documented source file that can be much larger than 64 Kbyte, with hard disk or floppy substituted for paper tape.
Nowadays there is no need to do two passes, but this architecture is still in use. It is slightly simpler, at the expense of I/O.
考虑汇编器的一种方法是想象它们计算分配给顺序增加的内存位置的一系列表达式的值。表达式通常由符号的值、对符号进行的一些算术运算、常量和特殊变量组成,例如“当前位置计数器”(通常用一个有趣的名字写成“$”),或者真正特殊的表达式语法是机器指令的语法。
请注意,一个表达式可能会产生一个填充多个连续内存位置的值;机器指令倾向于这样做,但为字符串文字、多精度数字、初始化结构等提供表达式很有用。这只会影响簿记细节,但不会改变汇编程序在抽象中所做的事情。
要计算每个表达式的最终值,汇编器必须知道可能涉及的任何符号的值。它仅以几种方式发现符号值。首先,符号值可能被定义为表达式的结果。其次,可以为符号值分配当前位置计数器的值;通常,当符号写在“标签”位置时,汇编程序会这样做。在这样的发现中,汇编器将符号名称及其值记录在符号表中以用于评估表达式。
汇编器面临的一个关键问题是生成表达式的值,但尚未遇到符号的所有定义。假设是,如果一个符号没有在某个特定的行中定义,它将在汇编器最终处理的稍后的行中定义。
两遍汇编器尝试在遇到每个表达式时计算它的值,分为两遍,称为“第一遍”和“第二遍”。在第一次传递期间,如果表达式中有未定义的符号(假定是前向引用),汇编器只需替换一个虚拟值(通常为零);在任何情况下,它都会为表达式计算一个值。如果正在处理机器指令或数据常量,则忽略结果,但大小用于推进位置计数器以启用标签值分配。如果遇到标签,则将其值设置为当前位置计数器。如果遇到符号赋值“A EQU”,则将符号值设置为表达式的结果;如果表达式包含未定义的符号,汇编器将发出错误。如果找到一条原始语句“ORG”,则将其视为写有“$ EQU”。在第一遍结束时,所有标签都已被赋值;任何没有值的符号在符号表中都被标记为“未定义”。第二遍重复第一遍的表达式评估,但不(重新)定义任何符号;由于所有符号都(预计)已定义,因此表达式值是正确的并被发送到输出流。
一次性汇编器会在遇到表达式时尝试计算每个表达式的值。如果表达式仅包含已定义的符号,则汇编器可以对其求值并生成最终值,并将该信息写入其输出流。(这里的另一个答案建议一些通过汇编程序将他们的答案写到内存中。这只是一个特例)。如果表达式包含未定义的符号,则汇编器存储一对(location,expression)以便稍后在符号被定义时或在汇编结束时重新处理。一些表达式,例如那些设置位置计数器的表达式不能有未定义的符号;在这种情况下,汇编程序会抱怨。
所以棘手的部分是存储未解析的表达式,并决定何时重新评估它。存储表达式的一种方法是简单地保留文本;另一个是为表达式构建相当于(反向)波兰符号的内容。要确定表达式何时需要重新计算,可以将其与它包含的未定义符号相关联;然后当一个符号被定义时,相应的未解析表达式被重新评估,完成的表达式被发出,未解析的表达式再次被重新处理。或者,汇编器可以简单地保存所有表达式,直到遇到输入的结尾;此时,应该定义所有符号,因此它应该能够确定每个表达式的最终值。
在上个世纪,我构建了一个在 8k 字节计算机上运行的一次性汇编器,它使用波兰语表示的表达式。定义符号后,计算波兰表达式并计算任何可计算的子表达式,将生成的波兰表达式简化为最终值或仅涉及未定义符号上的运算符的较小波兰表达式。未定义值的符号表条目具有与未定义符号对应的所有波兰表达式槽的链表;当遇到符号定义时,链表的所有元素都被更新,波兰表达式在发生时被重新评估。这使波兰表达式的大小尽可能小,并在定义所有符号时将其删除。这个汇编程序在小机器上处理十万行程序就很好。在这么小的机器上进行一次通过汇编的原因是源代码来自纸带(一种电传打字机,对于那些年龄足够大的人来说)并且即使阅读一次纸带也是非常痛苦和缓慢的;第二次不是一个好主意,所以双通道汇编器不是一个合适的选择。
我的一个团队后来构建了一个有趣的两遍汇编器。他没有对文本进行两次处理,而是在第一遍对文本进行标记(将其存储在内存中)并收集符号值。通过两个处理标记化的文本。这是一个非常快速的汇编程序,可用于两遍。他有更多可用的内存。
未来符号问题意味着符号在定义之前使用。
这是可能的,因为在汇编语言程序中,程序员可以随时定义符号,所以如果在定义之前使用符号,则称为“前向引用”
所以每次从将其转换为机器语言(二进制),汇编器得到 Opcode-Reg-X2-B2 但它没有在符号表中获得 D2(位移)条目,因此无法转换为机器语言(二进制) ) 它被称为“前向参考问题”。
解决这个 2 Pass 汇编程序是定义的。
**在第一次通过(第一次扫描)时,它将符号(标签)输入到符号表中。**并且在第二次通过(第二次扫描)时,汇编程序将其转换为机器语言(二进制)。这很简单:)