首页 > 解决方案 > 了解教授对装配作业的回答

问题描述

我的教授将此作为家庭作业问题的答案之一发布。谁能帮我分解一下?我不明白他在用 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

标签: assemblyriscv

解决方案


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

使用 32 位 RISC-V (RV32G),可以使用加载上立即数( lui) 和添加立即数( addi) 指令加载更大的常量。的立即操作数lui为 20 位宽,而addi允许立即操作数为 12 位。因此,它们足以加载最多使用 32 位的常量。

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

因此,对于 RV32G,要加载一个更大的常数,lui后跟一个必须采用高 20 位,将它们逻辑右移 12 位,以便抵消addi12 位左移。lui然后屏蔽低 12 位以获取addi.

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

假设我们用 表示常量的高部分,x用表示h低部分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

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

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

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

其中CON1 & 0x0800屏蔽了 12 位,即addi立即操作数的符号位。如果它被设置,则((CON1 & 0x0800)>>11)评估为 1,从而抵消由以下addi指令添加的多余符号位。CON1&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)

可以说,这也比代码段中的混乱更具可读性。

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

.set CON2, 6245

li    a1, 6245

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

li    a3, CON2

# => a1 == a2 == a3

推荐阅读