【问题标题】:Understand prof's answer to Assembly homework了解教授对装配作业的回答
【发布时间】:2020-01-22 22:13:07
【问题描述】:

我的教授将此作为家庭作业问题的答案之一发布。谁能帮我分解一下?我不明白他在用 CON1 - CON4 做什么以及 >> 和 0x0FFF 是什么意思。

CON1:   EQU 6000
CON2:   EQU 6245
CON3:   EQU 10000
CON4:   EQU 10245
A:  DM 4                         ; DM is Define Memory

    addi    t1,  x0, A           ; t1 = &A

    lui t0,  (CON1>>12) + ((CON1 & 0x0800)>>11)
    addi    t0,  t0, CON1&0xFFF
    sd  t0,  0(t1)            // Cut and paste from last question of Quiz1
                                      // Blank line between groups of statements
    lui t0,  (CON2>>12) + ((CON2 & 0x0800)>>11)
    addi    t0,  t0, CON2&0xFFF
    sd  t0,  8(t1)

    lui t0,  (CON3>>12) + ((CON3 & 0x0800)>>11)
    addi    t0,  t0, CON3&0xFFF
    sd  t0,  16(t1)

    lui t0,  (CON4>>12) + ((CON4 & 0x0800)>>11)
    addi    t0,  t0, CON4&0xFFF
    sd  t0,  24(t1)
                                      // We need this to avoid the NO INSTRUCTION error
    ebreak x0, x0, 0              ; Suspend program.

任何帮助将不胜感激,谢谢。我们正在使用 RISC-V

【问题讨论】:

  • >> 是右移。您的教授将 CON 地址分成两部分(低 12 位,高 24 位),因为 luiaddi 的范围有限。通常这是由汇编程序自动处理的。
  • @Jester 那么 >>11 在哪里播放?
  • 这是因为 addi 符号扩展了立即数,因此如果设置了位 0x800,则该值将被解释为负数。添加位将弥补这一点。
  • 我很好奇,这是一个什么样的RISC-V汇编器?
  • @DanielIlie,你有链接吗?我用谷歌找不到。

标签: assembly riscv


【解决方案1】:

在 RISC-V 基本指令集中,每条指令都以 32 位编码。这意味着立即操作数的空间仅限于几位。因此,要将更大的常数放入寄存器(RV32G/RV64G 也是 32 位或 64 位宽),您需要将其拆分并使用多条指令移动部件,即 RV32G 为 2 个,RV64G 为最多 8 个。

使用 32 位 RISC-V (RV32G),可以使用 load upper immediate (lui) 和 add immediate (addi) 加载更大的常量操作说明。 lui 的立即操作数是 20 位宽,而 addi 允许立即操作数为 12 位。因此,它们足以加载使用最多 32 位的常量。

lui 对其直接操作数进行符号扩展并将其左移 12 位并将结果加载到目标寄存器中。因此得名。 addi 还在添加前对其直接操作数进行符号扩展。

因此,对于 RV32G,要使用 lui 后跟 addi 加载更大的常数,必须取高 20 位,将它们逻辑右移 12 位,这样 12 位左移 @ 987654329@ 被取消。再对低 12 位进行掩码,得到addi 的操作数。

如果addi 不对其直接操作数进行符号扩展,这就足够了。如果是因为最高位设置为 1,我们必须增加 lui 操作数,以便在加法中再次将多余的符号位清零。

假设我们用h 表示常量x 的高部分,用l 表示低部分,因为RISC-V 在寄存器溢出时实现了二进制补码和算术换行,我们可以使用模运算来看到:

     h + l = x                             # taking register widths into account:
 => (h + l) % 2**32  = x % 2**32           # in case l is sign extended:
 => (h + l + e + c) % 2**32  = x % 2**32   # replace e with the additional sign bits:
<=> (h + l + 4294963200 + c) % 2**32  = x % 2**32     # eliminate c:
<=> (h + l + 4294963200 + 4096) % 2**32  = x % 2**32
<=> (h + l) % 2**32  + (4294963200 + 4096) % 2**32  = x % 2**32
<=> (h + l) % 2**32  + 0  = x % 2**32

因此,当且仅当 addi 的立即操作数是符号扩展时,我们必须将 1 加到 lui 立即操作数(左移 12 位后等于 4096)。

在您的汇编示例中,&gt;&gt; 表示右移,&lt;&lt; 表示左移,&amp; 表示逻辑与。它们用于实现所描述的拆分和算术,例如在

 lui t0,  (CON1>>12) + ((CON1 & 0x0800)>>11)
 addi    t0,  t0, CON1&0xFFF

其中CON1 &amp; 0x0800 屏蔽了 12 位,即addi 立即操作数的符号位。如果已设置,则((CON1 &amp; 0x0800)&gt;&gt;11) 的计算结果为 1,从而抵消了由以下addi 指令添加的多余符号位。 CON1&amp;0xFFF 屏蔽最低 12 位。

在标准 RISC-V 汇编中,只需使用 load immediate (li) 伪指令即可避免所有这些繁琐的位管理,例如:

li     t1, 6245

汇编器自动翻译成最优指令序列(例如使用 objdump 进行检查):

lui    t1, 0x2
addi   t1, t1,-1947

或者,使用 GNU 作为汇编器,也有将操作数分成上下两部分的指令:

lui    a1, %hi(6245)
addi   a1, a1, %lo(6245)

可以说,这也比你的 sn-p 中的混乱更具可读性。

这也适用于 GNU 中的符号,例如:

.set CON2, 6245

li    a1, 6245

lui   a2, %hi(CON2)
addi  a2, a2, %lo(CON2)

li    a3, CON2

# => a1 == a2 == a3

【讨论】:

  • 谢谢!真的很感激。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-16
  • 1970-01-01
  • 2016-06-06
  • 2016-05-29
相关资源
最近更新 更多