MIPS 汇编程序如何管理标签地址?
How does MIPS assembler manage label address?
MIPS 的汇编程序标签和 J 类型指令如何工作?
我目前正在使用 C++ 制作 MIPS 模拟器,遇到了一个大问题。 MIPS 汇编程序在执行 J 类型指令时究竟如何管理标签及其地址?
假设我们有以下代码。我们还假设 start:
从 0x00400000
开始。代码后的注释表示机器代码将存储在内存中的位置。
start:
andi $t0, $t0, 0 # 0x0040 0000
andi $t1, $t1, 0 # 0x0040 0004
andi $t2, $t2, 0 # 0x0040 0008
addi $t3, $t3, 4 # 0x0040 000C
loop:
addi $t2, $t2, 1 # 0x0040 0010
beq $t2, $t3, exit # 0x0040 0014
j loop # 0x0040 0018
exit:
addi $t0, $t0, 1000 # 0x0040 002C
据我目前了解,j loop
表达式会将 PC 设置为 0x0040 0010
。
当J型指令使用32位并且以MSB 6位作为操作码时,它只有26 bits 表示指令地址。那么如何只用26位来表示32位地址系统呢?
用上面的例子,只用24bits就可以表示0x00400010
。但是,在参考文献中,文本段位于0x00400000
到0x10000000
之间,需要32位来表示。
我试图使用 MARS 模拟器来理解这一点,但是它只是将 j loop
表示为 j 0x00400010
这对我来说似乎是无稽之谈,因为 0x00400010
是 32 位。
我目前的猜测
我目前的猜测之一如下。
汇编程序将loop:
标签的地址保存到某个26 位可达的内存地址中。然后当调用表达式j loop
时,标签loop
被翻译成包含0x00400010
的内存地址例如,0x00400010
被保存在0x00300000
这样的地址中并且当 j loop
被调用时,loop
被翻译成 0x00300000
并且它能够从 0x00300000
获取值并到达 0x00400010
。 (这只是我的猜测之一)
您有很多问题。
首先,让我们尝试区分汇编程序的操作和它生成并由处理器执行的 MIPS 机器代码。
汇编器以两种方式管理标签和地址。首先,它有一个符号 table,就像一个字典,一个 key-value 对的数据结构,其中名称是键和地址(当程序是 运行) 是成对的值。
其次,汇编程序使用位置计数器管理代码和数据部分。每次程序提供一些代码或数据时,该位置计数器都会增加。定义新标签时,当前位置计数器将用作新 key-value 对中的地址值。
处理器永远不会看到标签:它们不执行,也不占用代码或数据中的任何 space。处理器只能看到机器代码指令,在 MIPS 上这些指令都是 32 位宽的。每条机器代码指令都被划分为字段。指令类型或格式在 MIPS 上很简单:I-Type、J-Type 和 R-Type。然后这些格式定义了指令字段,汇编程序遵循这些编码。所有的指令格式共享一个 6 位的操作码字段,这个操作码字段告诉处理器指令是什么格式,因此它有哪些字段,以及如何解释和执行指令的其余部分。
汇编程序从程序集中删除了标签——标签及其名称不存在于二进制程序中。标签定义本身 (label:
) 从程序二进制文件中省略,但 标签的使用 被翻译成数字,因此使用标签的机器代码指令将有一些指令字段这是数字,并且汇编程序将为该数字字段提供适当的值,以便达到或以其他方式访问标签所指的内容的效果得以实现。 (标签不再在程序二进制文件中,但标签引用的代码或数据内存仍然存在)。
汇编程序设置分支指令、j
指令和 la
/lw
指令,使用数字告诉处理器将程序计数器向前或向后移动多远,或者,某些感兴趣的数据位于什么地址。 lw
/la
指令访问数据,这些指令使用 2 x 32 位指令,每条指令包含 16 位感兴趣的地址。在这两条指令之间,它们组合了一个完整的 32 位地址用于数据访问。对于完全到达任何 32 位地址的分支,他们必须以类似的方式(两个指令对)将 32 位地址放在一起并使用 indirect/register 分支。
MIPS 的汇编程序标签和 J 类型指令如何工作?
我目前正在使用 C++ 制作 MIPS 模拟器,遇到了一个大问题。 MIPS 汇编程序在执行 J 类型指令时究竟如何管理标签及其地址?
假设我们有以下代码。我们还假设 start:
从 0x00400000
开始。代码后的注释表示机器代码将存储在内存中的位置。
start:
andi $t0, $t0, 0 # 0x0040 0000
andi $t1, $t1, 0 # 0x0040 0004
andi $t2, $t2, 0 # 0x0040 0008
addi $t3, $t3, 4 # 0x0040 000C
loop:
addi $t2, $t2, 1 # 0x0040 0010
beq $t2, $t3, exit # 0x0040 0014
j loop # 0x0040 0018
exit:
addi $t0, $t0, 1000 # 0x0040 002C
据我目前了解,j loop
表达式会将 PC 设置为 0x0040 0010
。
当J型指令使用32位并且以MSB 6位作为操作码时,它只有26 bits 表示指令地址。那么如何只用26位来表示32位地址系统呢?
用上面的例子,只用24bits就可以表示0x00400010
。但是,在参考文献中,文本段位于0x00400000
到0x10000000
之间,需要32位来表示。
我试图使用 MARS 模拟器来理解这一点,但是它只是将 j loop
表示为 j 0x00400010
这对我来说似乎是无稽之谈,因为 0x00400010
是 32 位。
我目前的猜测
我目前的猜测之一如下。
汇编程序将loop:
标签的地址保存到某个26 位可达的内存地址中。然后当调用表达式j loop
时,标签loop
被翻译成包含0x00400010
的内存地址例如,0x00400010
被保存在0x00300000
这样的地址中并且当 j loop
被调用时,loop
被翻译成 0x00300000
并且它能够从 0x00300000
获取值并到达 0x00400010
。 (这只是我的猜测之一)
您有很多问题。
首先,让我们尝试区分汇编程序的操作和它生成并由处理器执行的 MIPS 机器代码。
汇编器以两种方式管理标签和地址。首先,它有一个符号 table,就像一个字典,一个 key-value 对的数据结构,其中名称是键和地址(当程序是 运行) 是成对的值。
其次,汇编程序使用位置计数器管理代码和数据部分。每次程序提供一些代码或数据时,该位置计数器都会增加。定义新标签时,当前位置计数器将用作新 key-value 对中的地址值。
处理器永远不会看到标签:它们不执行,也不占用代码或数据中的任何 space。处理器只能看到机器代码指令,在 MIPS 上这些指令都是 32 位宽的。每条机器代码指令都被划分为字段。指令类型或格式在 MIPS 上很简单:I-Type、J-Type 和 R-Type。然后这些格式定义了指令字段,汇编程序遵循这些编码。所有的指令格式共享一个 6 位的操作码字段,这个操作码字段告诉处理器指令是什么格式,因此它有哪些字段,以及如何解释和执行指令的其余部分。
汇编程序从程序集中删除了标签——标签及其名称不存在于二进制程序中。标签定义本身 (label:
) 从程序二进制文件中省略,但 标签的使用 被翻译成数字,因此使用标签的机器代码指令将有一些指令字段这是数字,并且汇编程序将为该数字字段提供适当的值,以便达到或以其他方式访问标签所指的内容的效果得以实现。 (标签不再在程序二进制文件中,但标签引用的代码或数据内存仍然存在)。
汇编程序设置分支指令、j
指令和 la
/lw
指令,使用数字告诉处理器将程序计数器向前或向后移动多远,或者,某些感兴趣的数据位于什么地址。 lw
/la
指令访问数据,这些指令使用 2 x 32 位指令,每条指令包含 16 位感兴趣的地址。在这两条指令之间,它们组合了一个完整的 32 位地址用于数据访问。对于完全到达任何 32 位地址的分支,他们必须以类似的方式(两个指令对)将 32 位地址放在一起并使用 indirect/register 分支。