类汇编语言编译器上的 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
3180
、F020
和F00C
是LDA
、IPT
和HLT
指令。
如代码所示,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 部分(与数据)。
我目前正在为自定义的类 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
,这意味着 "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 部分(与数据)。