【发布时间】:2021-11-02 09:33:57
【问题描述】:
我想将 3*2^16 + 9 存储在 $t0 中。我做了lui $t0,3,我做了addi $t0,$t0,3。顺便说一句,在教科书中,值是在lui之后通过ori操作创建的。我这样做的方式有问题吗?两者有什么区别?
【问题讨论】:
我想将 3*2^16 + 9 存储在 $t0 中。我做了lui $t0,3,我做了addi $t0,$t0,3。顺便说一句,在教科书中,值是在lui之后通过ori操作创建的。我这样做的方式有问题吗?两者有什么区别?
【问题讨论】:
两者有什么区别?
ori 和 addi 都允许 16 位立即数。
但是,ori 将零扩展立即数,因此换句话说,立即数始终被视为正数,因此在 32 位中,扩展立即数的高 16 位将为零。
而 addi 将符号扩展立即数,因此它被视为 16 位有符号值,可以是正数或负数。
两者都可以,但是在使用addi时,我们必须注意以下几点:如果低16位设置了符号位,那么根据符号扩展和加法的性质,它会产生递减高位的效果部分,所以需要在上面的立即数上加一个。例如,假设我们要加载一个常量 0x23458765。 lui 应该被赋予 0x2346 而不是 0x2345,然后addi 得到 0x8765,作为 16 位有符号立即数为负数,因此将从 lui 提供的 0x23460000 中减去 1,使其成为 0x23458765。
另一个区别是addi 将捕获溢出,这将是一个非常不受欢迎的副作用。 ori 不会在溢出时陷入陷阱。但是,我们可以改用addiu,因为它也不会在溢出时陷入陷阱,并且使用与addi 相同的16 位有符号立即数。
注意:汇编程序通常会按照您的意图执行,而不是完全按照您指定的方式执行。在某些汇编程序中将 0x8765 与 addi 或 addiu 一起使用将生成多条指令以将该数字作为正数。例如,如果您想用 MARS 测试上述序列,我们将不得不使用 -30875(10) 而不是 0x8765 来让汇编器生成立即数 0x8765,否则它将使用多个构建 +34661(10) 的说明。 (将 -30875 与 ori 一起使用也会生成多条指令,因此我们必须使用 0x8765 或 34661 才能在一条指令中立即获得该指令。)
我的做法有问题吗?
总之,您可以使用任何您喜欢的代码序列来构建常量。虽然有些文本只会显示一种方式,但其他文本肯定是可能的,而且没有“正确”的方式。
话虽如此,但是,如果您使用汇编程序伪函数 %hi 和 %lo,它们是为与 addiu 一起使用而设计的——这意味着如果低 16 位 %hi 将执行 +1显示为否定立即数。这些函数的使用如下: lui $t0, %hi(label); addiu $t0, $t0, %lo(label) — 其中label 是(代码或数据)标签,但也可以是 32 位常量。 (MARS 不支持这些功能,所以我们使用 MARS 使用li 或la 伪指令代替,它会根据需要生成一两条指令。)
%hi 和 %lo 以这种方式工作的原因是有时序列使用负载(例如 lw)或存储(例如 sw)而不是 addiu,以及所有负载& 存储也适用于相同的 16 位有符号立即数(因此,当低 16 位变为负数时,也会产生递减上部的潜在影响)。 (例如,如果意图访问全局变量,这些序列 (lui;lw/sw) 可以在这里和那里保存指令。)
【讨论】:
lw $t0, label 作为 lui / lw 的伪指令,但如果您有多个附近的负载/存储可以共享相同的上半部分,这将无济于事。
lw label($reg) 形式的伤害大于帮助,因为它们对学习者隐藏了有关真实处理器的信息。