我目前正在为自定义类似asm的编程语言编写编译器 我真的很困惑如何对数据标签进行适当的PC相对寻址。
main LDA RA hello
IPT #32
HLT
hello .STR "Hello, world!"
编译后,上面的伪代码会产生以下十六进制:
31 80 F0 20 F0 0C 48 65 6C 6C 6F 2C 20 77 6F 72 6C 64 21 00
3180
,F020
和F00C
是LDA
,IPT
和HLT
说明。
如代码所示,LDA
指令使用标签hello
作为参数。在编译时,它变为值02
,这意味着"增加PC + 0x02" (如果您查看代码,那就是" Hello,world!"行相对于LDA
调用的位置。
问题是:.STR
不是指令,因为它只告诉编译器它需要在可执行文件的末尾添加一个(0终止的)字符串,所以,在{{1}之后是否有其他指令} label声明,该偏移量是错误的。
但我无法找到计算正确偏移量的方法,除了让编译器能够穿越时间。我是否必须"编译"它两次?首先是数据标签,然后是实际指示?
答案 0 :(得分:2)
是的,大多数装配工(至少)是两次通过 - 正是因为像这样的前向参考。添加宏功能可以添加更多传递。
查看汇编列表,而不仅仅是操作码。正如你所说的实际偏移是“2”,我假设存储器是字寻址的。
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
前两列是PC和操作码。我不确定0000 3180 main LDA RA hello
0001 F020 IPT #32
0002 F00C HLT
0003 4865 hello .STR "Hello, world!"
指令是如何编码的(那里的LDA
偏移位置在哪里?)
在第一遍中,假设所有寻址都是相对的,则assmebler将发出操作码的固定部分(覆盖+2
部分)以及标记,以显示需要修补指令第二遍中LDA RA
的地址。
此时它知道最终机器语言的大小,但不知道它的完整值。
然后继续,计算每条指令的地址并构建其符号表。
在第二遍中,现在知道上述信息,它通过计算相对偏移等来修补每条指令。它还经常重新生成整个输出(包括PC值)。
偶尔会在第二遍中检测到某些东西,这会阻止它继续。例如,您可能只能引用256个单词(-127到+128)内的对象,但标签hello
结果超过128个单词。这意味着它应该使用双字指令(带有绝对地址),这会改变它在第一次传递过程中学到的所有内容。
这通常被称为“修复”错误。在链接阶段也会发生同样的事情。
只有坚持'使用前定义'才能实现单通道汇编程序。在这种情况下,您的代码会将hello
报告为未定义的符号。
您还需要阅读“计划部分”。虽然hello
不是可执行指令,但 是汇编程序的指令,用于将字符串的二进制表示放入图像的CODE部分(vs DATA)。