类汇编语言编译器上的 PC 相对寻址

PC-relative addressing on an assembly-like language compiler

我目前正在为自定义的类 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

3180F020F00CLDAIPTHLT指令。

如代码所示,LDA 指令使用标签 hello 作为参数。在编译时,它变成值 02,这意味着 "Incremented PC + 0x02"(如果你看代码,那是 "Hello, world!" 行的位置,相对于 LDA 调用. 问题是:.STR 不是一条指令,因为它只告诉编译器它需要在可执行文件的末尾添加一个(以 0 结尾的)字符串,所以 [=19= 之后还有其他指令吗? ] 标签声明,那个偏移量是错误的。

但除了让编译器能够穿越时空之外,我找不到计算正确偏移量的方法。我必须 "compile" 两次吗?首先是数据标签,然后是实际说明?

是的,大多数汇编器(至少)是两次通过的——正是因为像这样的前向引用。添加宏功能可以添加更多通行证。

查看汇编列表,而不仅仅是操作码。正如你所说的实际偏移量是“2”,我假设内存是字寻址的。

0000 3180   main    LDA RA hello
0001 F020           IPT #32
0002 F00C           HLT

0003 4865   hello   .STR "Hello, world!"

前两列是 PC 和操作码。我不确定 LDA 指令是如何编码的(+2 偏移在哪里?)

在第一遍中,假设所有寻址都是相对的,assmebler 将发出操作码的固定部分(覆盖 LDA RA 部分)以及一个标记以表明它需要修补第二遍中地址为 hello 的指令。

此时它知道最终机器语言的大小,但不知道完整值。

然后继续,计算出每条指令的地址并构建其符号table。

在第二遍中,现在知道了上述信息,它通过计算相对偏移量等来修补每条指令。它还经常重新生成整个输出(包括 PC 值)。

有时,在第二遍中会检测到某些东西,从而阻止它继续进行。例如,也许您只能引用 256 个单词(-127 到 +128)以内的对象,但标签 hello 却超过 128 个单词。这意味着它应该使用双字指令(带有绝对地址),这会改变它在第一遍中学到的所有内容。

这通常称为 'fix up' 错误。在 link 阶段可能会发生同样的事情。

单程汇编器只有在您坚持 'define before use' 时才有可能。在这种情况下,您的代码会将 hello 报告为未定义的符号。

您还需要阅读 "program sections"。虽然 .STR 不是 executable 指令,但它 指令,用于让汇编器将字符串的二进制表示形式放入图像的 CODE 部分(与数据)。