整数运算指令

格式为:[指令名] [目标], [源], [操作数]

例如:

add t0, t1, t2
意为将t1 + t2存放入t0

xori t0, t1, 0x7EF
意为将t1 ^ 0x7EF存放入t0

注意: RV32I指令长度为32bit,带立即数的运算指令(I型指令)的立即数只有12bit,因此带符号数只能取-0x8000x7FF之间的数

具体地,运算指令有以下种类:

  • 算术运算:add sub
  • 逻辑运算:and or xor
  • 移位运算:sll srl sra

访存指令

格式为:

[指令名(读)] [目的寄存器] [偏移量]([地址寄存器])

[指令名(写)] [源寄存器] [偏移量]([地址寄存器])

例如:

lb t0, (0x8)t1
意为将t1寄存器中的地址加0x8后得到的地址处的1个字节存入t0

具体地,有:

  • 读指令:lw lh lb
  • 无符号读指令:lhu lbu
  • 写指令:sw sh sb

比较指令

slt a0, a1, a2
意为若a1 < a2则将a01,否则为0sltu为对应的无符号数比较版本;slti sltiu为对应的立即数版本

例如以下代码段:

1
2
3
4
addi a0, a0, 0x123
addi a1, a1, -0x234
slt a2, a0, a1
sltu a3, a0, a1

运行后a20a31

长立即数操作指令

lui a4 0x4daf1
a4的高20位设为0x4daf1

auipc a5 0x6789a
a5设为PC + 0x6789a000

条件分支指令

beq a0, a1, equal
a0 == a1则跳转至equal标号处

还有bne bge bgeu blt bltu

无条件跳转

jal s9, target
跳转至target,将当前的PC + 4(即下一条指令地址)存入s9

使用jal x0, target则只会进行跳转,因为x0不可改变

jalr x0, x1, 0可以实现ret,即跳转到x1 + 0x1用来存储返回地址)

类似地,jal x1, func可以实现过程调用(注意:这里没有维护栈帧)

杂项指令

看不懂,下一个 :(

伪指令

RV32I使用了一些方便用户的伪指令,其本质使用其他指令实现,例如ret的真实实现是jalr x0, x1, 0

一定要善用伪指令标号~

RV32I函数使用惯例

寄存器使用

a开头为函数参数寄存器,调用者保存
s开头为被调用者保存寄存器
t开头为临时寄存器,调用者保存

函数格式

函数入口:

1
2
3
entry_label:
addi sp, sp, -framesize # 拉栈帧
sw ra, framesize-4(sp) # 返回地址存入栈帧

函数结尾:

1
2
3
lw ra, framesize-4(sp)  # 从栈帧读取返回地址
addi sp, sp, framesize # 收栈帧
ret