CPU 看似神秘,其实每时每刻只做三件事:从哪里取数、做什么运算、把结果放回哪里。而「放回哪里」的这个地方,就叫寄存器(register)——它是 CPU 自己的工作台,容量小得可怜,但速度是全机最快的。这一讲我们把工作台认全,然后写下你的第一条汇编指令。

32 个通用寄存器

RV32I 一共有 32 个通用寄存器,编号 x0 到 x31,每个 32 位宽(这就是 RV32 里的 32)。听起来很少?x86-64 有 16 个,8086 只有 8 个——RISC 设计哲学就是「寄存器多而快,编译器随便用」。

不过日常读汇编时,你几乎看不到 x5、x10 这种编号,看到的全是 ABI 名字——按用途起的外号:

编号 ABI 名 用途 谁保存
x0 zero 恒为零,写不进去 ——
x1 ra 函数返回地址 调用者破坏
x2 sp 栈指针 专用
x3 gp 全局指针 专用
x4 tp 线程指针 专用
x5–x7, x28–x31 t0–t6 临时值 调用者保存
x8, x9, x18–x27 s0–s11 需要跨函数存活 被调者保存
x10–x17 a0–a7 函数参数/返回值 调用者保存

t 是 temporary,s 是 saved,a 是 argument——先混个脸熟,第 05 讲函数调用时你会真正用到这张表。

x0:一个故意「坏了」的寄存器

zero 是整个指令集里最有意思的设计:读它永远是 0,写它等于扔进黑洞。

为什么要故意造一个坏掉的寄存器?因为 0 在程序里出现得太频繁了:清零、比较、置初值……有了恒零寄存器,这些操作全部不需要专门的指令:

addi a0, zero, 42     # a0 = 0 + 42,把常数装进寄存器
add   a0, zero, a1    # 等价于「把 a1 复制到 a0」
add   zero, zero, zero# 著名的 nop(什么都不做)

一套加法指令,顺手解决了「装常数」「复制」「空转」三件事——这是精简指令集「用规则换取简洁」的典型体现。

第一条指令:addi

汇编指令的通用形状是「做什么,操作数,操作数」。我们写第一条:

addi a0, zero, 42     # a0 = 0 + 42
  • addi = add immediate(加立即数):把一个常数加到寄存器上
  • 格式:addi rd, rs1, imm——结果放 rd,源是 rs1 加常数 imm
  • 立即数是 12 位有符号数,范围 -2048 到 2047,超了要分两步(第 07 讲教)

再来两条热热身:

addi t0, zero, 10     # t0 = 10
addi t0, t0, -1       # t0 = t0 - 1 ——减法就是加负数

注意第二行:RV32I 里有 add 却没有 subi。想要「减常数」,加个负数就行——少一条指令,硬件就少一套电路。

动手验证

读十遍不如跑一遍。打开 HI RISC-V App 的模拟器沙盒,敲下这三行:

addi a0, zero, 42     # a0 = 42
addi a1, zero, 8      # a1 = 8
add   a2, a0, a1      # a2 = 50

单步执行,盯住寄存器面板:你会看到 a2 在第三条指令后变成 50(十六进制 0x32)。改改立即数、把 zero 换成 a0,观察每条指令如何改写工作台。

免费下载 HI RISC-V App,前 5 关完全免费。

本讲三句话

  1. RV32I 有 32 个 32 位寄存器,汇编里用 ABI 名(zero/ra/sp/t0/a0…)
  2. zero 恒为零:装常数、复制、空转全靠它
  3. addi rd, rs1, imm 是你的第一条指令,立即数范围 ±2048

下一讲我们把加减法做完,顺便回答一个有趣的问题:RISC-V 为什么没有 flags 寄存器?