2. lc-2k 指令集架构
硬件结构
- 32 位 处理器: 指令长度是 32 位 , 寄存器大小是 32 位
- 32 位处理器的关键特征是其寄存器、数据总线和地址总线的宽度为 32 位。这意味着它可以处理 32 位的整数,并且通常可以寻址的最大内存空间为 4 GB。
- 在 32 位处理器中,寄存器和数据路径通常也是 32 位的,所以处理器可以在一次操作中处理 32 位的数据
- 其指令长度也是 32 位(这里的32 没有特指王楚钦)
- 8 个寄存器,其中 reg 0 一般只会被赋值 0
- 内存寻址范围 4GB 即 65536 words
指令形式
寄存器型指令结构
| 31 - 25 | 24 - 22 | 21 - 19 | 18 - 16 | 15 - 3 | 2 - 0 |
|---|---|---|---|---|---|
| unused | opcode | regA | regB | unused | destR |
立即数型指令结构
| 31 - 25 | 24 - 22 | 21 - 19 | 18 - 16 | 15 - 0 |
|---|---|---|---|---|
| unused | opcode | regA | regB | offset |
J 类型指令 (jalr)
| 31 - 25 | 24 - 22 | 21 - 19 | 18 - 16 | 15 - 0 |
|---|---|---|---|---|
| unused | opcode | regA | regB | unused |
O 类型指令 halt, noop
| 31 - 25 | 24 - 22 | 21 - 0 |
|---|---|---|
| unused | opcode | unused |
指令分析
算术类
add: add 1 2 3 表示将 1 + 2 的结果存入 3
nor: nor 1 2 3 表示将 ~(r1|r2) 的结果存入 3
访存类
lw: lw 1 2 3 表示将 mem[r1 + 3] 的值存入 r2
sw: sw 1 2 3 表示将 r2 的值存入 mem[r1 + 3]
我们称 r1 为 base register, 3 为 offset 或者 displacement
如果第一个参数选择 0 说明我们这里用的是 0 寄存器,存储地址是 mem[0], 因此后面任何的 displacement 就等于mem[displacement]
如果第一个参数不是 0, 那么就要找到该寄存器存储的值(这里一般就是地址)然后加上 displacement 进行偏移计算
注
这里的两条指令指的是将数据在 内存 和 寄存器之间进行加载存储,如果要想将某个寄存器的值移动到其他寄存器,可以考虑 用 add r0 实现
控制类
beq: beq 1 2 3 表示如果 r1 == r2, 则跳转到 3, 这里 PC = PC + 1 + 3
如果上面的3是一个label, 那么在解析的时候,我们事实上会跳转到那一行,但是这个地址被解析成的offset 遵循 offset = label_address - 1 - PC
而且注意:这里的跳转到达的位置不是 label 地址的val 值,只是 label 的地址
jalr: jalr reg1 reg2 表示 将 pc + 1 存储到 reg2的值中,然后将当前的计数器地址指向寄存器 1 存储的值
填充
.fill: .fill 5 表示将 5 填充到当前位置 注意这里的覆盖是 val 而不是地址,这里的做法是将 5 填充到当前PC 坐标指向的内存里面
如何访问这个fill 的值呢?可以通过 lw 0 0 3 来访问这个值,这里的 3 是 fill 的地址 (行数),因为 reg0 一般为 0
注
这里的 .fill 并不是一个指令,并没有对应的 opcode, 在汇编的时候一般会用于初始化变量
提问
请问 .fill 7 和 add 0 0 7 有什么共同点?
写成2进制代码,可以发现二者完全相同,因此我们很多时候不能对 r0 进行赋值操作,否则难以辨别
内存空间 memory space
我们很容易混淆寄存器空间和内存空间的概念,在我们运行程序的时候,代码存储的地方是 内存空间, 我们的每一行都是对应的内存空间的一个相邻地址(相差1)
我们如果要用到立即数作为 地址偏移量 的时候,就是在内存的地址部分进行加减偏移。如果还会用到 base, 一般就是某个寄存器存储的地址数值,而不是寄存器本身的地址
寄存器本身地址不在内存空间内,二者不可混淆
例
例如在代码的第12行, 我们有 index .fill 14 的指令,那么我们首先在 内存空间 mem[13] 处填充 14,并且当前在 R1 的地方保存了 11,那么我们如果要调用 lw 1 2 index, 那么根据定义,我们有自然语言指令 R2 = mem[R1 + index], 这里的 [] 表示内存空间地址,所以这里面的数字都表示地址, 使用其存的值表示地址, 使用其地址 (而不是 val)进行地址的偏移计算(可以 想象一下 会得到其一般的地址而不是其值,只有解 [] 的时候会用到其值)
