繁体   English   中英

(MIPS 汇编器)我们可以自己初始化程序计数器吗?

[英](MIPS Assembler) Can we initialize the Program Counter on our own?

作为我任务的一部分,我仍在尝试开发 MIPS 汇编器。 我得到了这些输入和输出文件。

main:   lw $a0, 0($t0)          
begin:  addi $t0, $zero, 0      # beginning
    addi $t1, $zero, 1
loop:   slt $t2, $a0, $t1       # top of loop
    bne $t2, $zero, finish
    add $t0, $t0, $t1
    addi $t1, $t1, 2
    j loop              # bottom of loop
finish: add $v0, $t0, $zero

并且输出应该是机器代码,如下所示:

10001101000001000000000000000000
00100000000010000000000000000000
00100000000010010000000000000001
00000000100010010101000000101010
00010101010000000000000000001000
00000001000010010100000000100000
00100001001010010000000000000010
00001000000000000000000000000011
00000001000000000001000000100000

我注意到代表指令“j循环”的机器代码是

00001000000000000000000000000011

根据 J 型指令格式,最后 26 位将代表目标地址。 我从上面写的二进制代码中注意到,这条跳转指令的目标地址(基本上是“循环”的地址)是 00000000000000000000000011 即 3。

我开发了我的程序版本,但它为“循环”检索的目标地址远大于此。

我想知道是否有任何方法可以将程序计数器初始化为 0 或其他任何内容,以便获得与给定的输出文件中的目标地址相同的目标地址。 程序计数器究竟是如何工作的? 它是否为每一行代码增加自己?

请指教。 谢谢!

这里任何类型的完整答案都有很多复杂性。 不过,对于 MIPS 组装,我们可能 [请参阅下面的评论] 休息一下。

我们需要考虑寻址模式相对寻址绝对寻址的概念。 这是因为,正如zwol 在评论中提到的,编译器和汇编器的输出通常实际上并不是准备好运行的代码,而是目标文件,充满了由链接器和/或加载解释的指令。

链接器是一个程序,它接受多个目标文件并将它们组合成一个更完整的程序。 这可能采用另一个目标文件的形式,或者是一个本质上是目标文件集合的库。 如果库格式足够简单,库可以简单地通过聚合目标文件来构建,并可以选择添加目录,但有时您想要进行一定量的预链接,将特定的目标文件连接到一起一个牢不可破的单元,用于稍后链接更多目标文件或库。 链接器可能非常复杂,因为它们可能必须处理符号名称(函数和变量名称)并为调试器提供信息(符号表、内存区域描述等)。

加载器获取通常至少由链接器部分解析、有时完全解析的目标文件,并将其加载到内存中。 一些加载器本身就是链接器,其类型通常称为运行时链接器或运行时加载器。 这允许可执行目标文件在运行时加载其他目标文件,而不是预先预先链接所有内容。

但是,无论如何,通常是加载时操作将实际地址分配给代码和数据。 目标文件可能包含说明代码可以在任何地方运行,或者代码必须在某个特定(固定)地址运行的指令。 相同的规则可能适用于数据。 如果需要固定地址,则该地址可能不可用,因此可重定位代码(可以从某种默认地址移动到另一个不同地址的代码)通常是可取的。

这就引出了相对寻址的概念。 假设一台机器通过重复执行一些非常简单的步骤来工作:

  1. 从 IP(指令指针)或 PC(程序计数器)寄存器给定的地址加载指令。
  2. 将此寄存器增加一些常数,例如 4。
  3. 执行刚刚加载的指令。

分支指令包含一条指令,用于将 IP/PC 寄存器更改某个新值,或者通过添加或减去某个值来更改IP/PC 寄存器。

现在,假设可执行目标文件建议在地址0x04000000处加载程序,例如。 进一步假设第十条指令——将在地址0x04000028 ——是一条分支指令,它需要进行设置以便从0x0400000c加载下一条指令,即第三条指令:

04000000       instruction#0
04000004       instruction#1
04000008       instruction#2
0400000c loop: instruction#3
04000010       #4
04000014       #5
04000018       #6
0400001c       #7
04000020       #8
04000024       #9
04000028       j   loop
0400002c

鉴于我们上面的模型,IP 或 PC 寄存器将在执行指令 #10 期间,跳转到指令 #3 的j loop保持值0400002c ,因为我们将操作描述为“加载,递增 4,执行”。

如果我们需要使用绝对寻址,我们需要实际的j loop指令将文字值0400000c直接填充指令指针寄存器中。 但是,可能只有加载程序知道程序是否真的04000000运行。 如果该地址正在使用中,则加载程序可能已将程序移至08000000 ,而要塞入 ip 寄存器的值现在0800000c

但是,如果我们使用相对寻址,则j loop指令需要组装成机器代码,而不是“转到0400000c ”,而是“从现在的位置0400002c前进或后退到我们想要的位置在0400000c ”。 这显然是向后飞跃, 0400002c - 0400000c或 20(十六进制,32 十进制)字节,或八条指令的价值。

编辑:请参阅下面的评论,下一部分是错误的——我依赖于其他 StackOverflow 答案和我引用的网页,以假设 PC 相对跳转。 我已将其更新为对j指令使用绝对寻址。

MIPS 处理器使用称为pc的寄存器(但难以访问),并支持条件分支中的相对寻址(例如, beq ;请参阅汇编 PC 相对寻址模式)。 因此,一些复杂性可能会消失:我们只需要指示 CPU 向后跳转 8 条指令,即向 PC 寄存器添加负 8。 CPU 会自动将此值乘以 4,从而添加负 32。 如果我们真的在04000000加载, pc将是0400002c并将其移回这么多将其更改为0400000c ,这就是我们想要的。 如果我们真的在08000000加载,则相同的相对移动0800000c我们0800000c ,这就是我们想要的。

如果我们使用b指令,就会出现这种情况。 但是j指令在 256 MB 区域内是绝对的:它们只是覆盖程序计数器的低 28 位。

通常,我们将有一个汇编器输出我们的绝对jump指令,其重定位类型告诉任何运行时加载器:添加所需的任何加载时间偏移量。 所以我们只需要确保,当我们组装时,我们知道我们打算在哪里加载——无论是0 ,还是04000000 ,或者其他什么——我们将发出,对于j指令,目标的绝对地址指令,但还有一些额外的链接器/加载器指令,这些指令说:此指令中的常量可能需要在链接或加载时调整。 请注意,链接器和加载器必须足够聪明才能理解寻址约束:如果代码段使用j指令跳转到一个 256 MB 的区域内,那么移动程序是不行的,现在跨越两个这样的区域一区。

(网站https://en.wikibooks.org/wiki/MIPS_Assembly/MIPS_Details声称j指令是相对的,但这似乎是错误的;见评论。)

(请注意,负数表示为二进制补码。由于j指令采用 26 位相对地址,它会自动为您乘以 4,因此它可以表示 28 位地址范围,从 -2 27到 2 27 -1 ,或-08000000..07fffffc ,以 4 为步长。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM