和其他RISC体系结构一样,RISC-V体系结构也基于加载和存储的体系结构设计理念。在这种体系结构下,所有的数据处理都需要在通用寄存器中完成,而不能直接在内存中完成。因此,要先把待处理数据从内存加载到通用寄存器,然后进行数据处理,最后把结果写回内存中。
加载指令的格式如下。
l{d|w|h|b}{u} rd, offset(rs1)
其中,相关选项的含义如下。
● {d|w|h|b}:表示加载的数据宽度。加载指令如表3.1所示。
● {u}:可选项,表示加载的数据为无符号数,即采用零扩展方式。如果没有这个选项,则加载的数据为有符号数,即采用符号扩展方式。
● rd:表示目标寄存器。
● rs1:表示源寄存器1。
● (rs1):表示以rs1寄存器的值为基地址进行寻址,简称rs1地址。
● offset:表示以源寄存器的值为基地址的偏移量。offset是12位有符号数,取值范围为[−2048, 2047]。
表3.1 加载指令
上述加载指令的编码如图3.3所示,其中opcode字段都是一样的,唯一不同的是funct3字段。
图3.3 加载指令的编码
【例3-2】 下面的代码使用了加载指令。
1 li t0, 0x80000000
2
3 lb t1, (t0)
4 lb t1, 4(t0)
5 lbu t1, 4(t0)
6 lb t1, -4(t0)
7 ld t1, (t0)
8 ld t1, 16(t0)
第1行是一条伪指令,表示把立即数加载到t0寄存器中。
在第3行中,从以t0寄存器的值为基地址的内存中加载1字节的数据到t1寄存器,并对这1字节的数据进行符号扩展。符号扩展是计算机系统把小字节数据转换成大字节数据的规则之一,它将符号位扩展至所需要的位数。例如,一个8位的有符号数为0x8A,它的最高位(第7位)为1,因此在做符号扩展的过程中,高字节部分需要填充为0xFF,如图3.4所示,符号扩展到64位的结果为0xFFFF FFFF FFFF FF8A。
图3.4 符号扩展
在第4行中,以t0寄存器的值为基地址加上4字节的偏移量为内存地址(0x8000 0004),再从这个内存地址中加载1字节的数据到t1寄存器中,并对该字节的数据进行符号扩展。
第5行中的指令与第4行中的指令基本类似,不同之处在于该字节的数据不会做符号扩展,即按照无符号数处理,因此高字节部分填充为0,称为零扩展,如图3.5所示。
图3.5 零扩展
在第6行中,以t0寄存器的值为基地址减去4字节的偏移量为内存地址(0x7FFF FFFC),再从这个内存地址中加载1字节的数据到t1寄存器中,并对该字节的数据进行符号扩展。
在第7行中,从以t0寄存器的值为基地址的内存中加载8字节的数据到t1寄存器中。
在第8行中,以t0寄存器的值为基地址加上16字节的偏移量为内存地址(0x8000 0010),再从这个内存地址中加载8字节的数据到t1寄存器中。
【例3-3】 下面的代码使用LUI加载立即数。
lui t0, 0x80200
lui t1, 0x40200
在第1行中,首先把0x80200左移12位得到0x8020 0000,然后进行符号扩展,最后结果为0xFFFF FFFF 8020 0000。
在第2行中,首先把0x40200左移12位得到0x4020 0000,然后进行符号扩展,因为最高位为0,所以最后结果为0x4020 0000。
【例3-4】 下面的代码有错误。
lb a1, 2048(a0)
lb a1,-2049(a0)
上述指令的偏移量超过了取值范围,汇编器会报错。
AS build_src/boot_s.o
src/boot.S: Assembler messages:
src/boot.S:6: Error: illegal operands 'lb a1,-2049(a0)'
src/boot.S:7: Error: illegal operands 'lb a1,2048(a0)'
make: *** [Makefile:28: build_src/boot_s.o] Error 1
存储指令的格式如下。
s{d|w|h|b} rs2, offset(rs1)
其中,相关选项的含义如下。
● {d|w|h|b}:表示存储的数据宽度。根据数据的位宽,存储指令的分类如表3.2所示。
● rs1:表示源寄存器1,用来表示基地址。
● (rs1):表示以rs1寄存器的值为基地址进行寻址,简称 rs1地址 。
● rs2:表示源寄存器2,用来表示源操作数。
● offset:表示以源寄存器的值为基地址的偏移量。offset是12位有符号数,取值范围为[−2048, 2047]。
表3.2 存储指令的分类