2-2-2 MIPS Architecture
Review: MIPS Instructions
一般算数指令符号扩展,逻辑指令零扩展
Category | Instr | OPCode | Example | meaning | 备注 |
---|---|---|---|---|---|
Arithmetic (R & I format) | add | 0 & 20 | add s1 ,s2, s3 | s1 = s2 + s3 | |
subtract | 0 & 22 | sub s1, s2, s3 | s1 = s2 - s3 | ||
add immediate | 8 | addi s1, s2, 4 | s1 = s2 + 4 | 做符号扩展 | |
逻辑左移 | shift left logical | 0 & 00 | sll s1, s2, 4 | s1 = s2 « 4 | 在空出的地方补0(零扩展) |
逻辑右移 | shift right logical | 0 & 02 | srl s1, s2, 4 | s1 = s2 » 4 (fill with zeros) | 在空出的地方补0(零扩展) |
算数右移 | shift right arithmetic | 0 & 03 | sra s1, s2, 4 | s1 = s2 » 4 (fill with sign bit) | 在空出的地方做符号扩展 |
and | 0 & 24 | and s1, s2, s3 | s1 = s2 & s3 | ||
or | 0 & 25 | or s1, s2, s3 | s1 = s2|s3 | ||
nor | 0 & 27 | nor s1, s2, s3 | s1 = not (s2|s3) | 或非 | |
and immediate | c | andi s1, s2, ff00 | s1 = s2 & 0xff00 | ||
or immediate | d | ori s1, s2, ff00 | s1 = s2 | 0xff00 | ||
load upper immediate | f | lui s1,0xffff | s1 = 0xffff0000 | 后一步载入低16位时,用无符号数相加指令 addiu 或者 ori 逻辑指令,因为ori是0扩展,而addi是符号扩展 | |
异或 | xor s2,s1,s0 | s2=s1⊕s0 | |||
Data transfer (I format) | load word | 23 | lw s1 100(s2) | s1=Memory(s2+100) | |
store word | 2b | sw s1, 100(s2) | Memory(s2+100)=s1 | ||
load byte | 20 | lb s1, 101(s2) | s1=Memory(s2+101) | 对高位进行符号扩展 | |
store byte | 28 | sb s1, 101(s2) | Memory(s2+101)=s1 | 对高位进行符号扩展 | |
load half | 21 | lh s1, 101(s2) | s1=Memory(s2+102) | 对高位进行符号扩展 | |
store half | 29 | sh s1, 101(s2) | Memory(s2+102)=s1 | 对高位进行符号扩展 | |
Cond. branch (I & R format) | br on equal | 4 | beq s1, s2, L | if (s1==s2) go to L | 1.往前跳为负偏移,往后跳为正偏移;2.跳转目标规定的是相对于它的下⼀条指令的偏移;3.偏移量(加上 2 个低位零,即×4) 符号扩展后与(更新的) PC相加, 得到⽬标地址 |
br on not equal | 5 | bne s1, s2, L | if (s1 !=s2) go to L | 4.跳转范围被限制在离分⽀指令⼤约正负 32K 条指令 (正负 128K 字节(±2^17=±131072 约等于128k))的范围。 | |
set on less than immediate | a | slti s1, s2, 100 | if (s2<100) s1=1; else s1=0 | ||
set on less than | 0 & 2a | slt s1, s2, s3 | if (s2<s3) s1=1; else s1=0 | ||
Uncond. jump | jump | 2 | j 2500 | go to 10000 | 绝对地址的形成 :指令中低26位,后面加两个零把PC+4的高4位与上面的28位合并为32位绝对地址,即跳转范围为距离当前地址高4位2^28≈256M的地方 |
jump register | 0 & 08 | jr t1 | go to t1 | ||
jump and link | 3 | jal 2500 | go to 10000; ra=PC+4 | ==跳转的最远的是jr和jalr,因为他们跳转的地址是寄存器中存的,即有32位,而jal和j只有28位,beq只有17位(因为是16位左移2位变18位,但beq是I型指令,imm中第一位是符号位)== | |
MULT instruction | 0 & 0x18 | mult rs, rt | ⾼ 32 bits 存于 hi 中, 低 32 bits 存于 lo 中 | ||
DIV instruction | 0 & 0x1a | div rs, rt | 商存储在 lo 中, 余数存储在 hi中 | ||
move from hi | 0 & 0x10 | mfhi rd | move the 32-bit half result from hi to rd | ||
move from lo | 0 & 0x12 | mflo rd | move the 32-bit half result from lo to rd |
MIPS指令汇总:
1
https://www.likecs.com/show-203940020.html?sc=3400.3359375
32 个寄存器的使用约定
- 还有2个32位乘、商寄存器
Hi 和L0
; - 乘法时分别存放64位乘积的高、低 32位;除法时分别存放余数和商。
MIPS的模型
MIPS的内存
MIPS 使用字节内存地址. 指令是32位的, 占4个字节(一个字) 地址,指令之间差4个字节地址
32bits = 4Bytes = 1Word
MIPS为大端存储
指令剖析
计算机中指令的表示
MIPS指令格式
MIPS只有3种指令格式, 32位固定⻓度指令格式
- R(Register) 类型指令: 两个寄存器操作数计算, 结果送寄存器
- I(Immediate) 类型指令: 使⽤1个16位⽴即数作操作数;
-
J(Jump) 类型指令: 跳转指令, 26位跳转地址
- 对于Load/Store指令, 单⼀寻址模式: Base+Displacement ➢ 没有间接寻址 ➢ 16位⽴即数 ➢ 简单转移条件(与0⽐较, 或者⽐较两个寄存器是否相等) ➢ ⽆条件码
R型指令
- shamt:此字段包含移位指令的移位数量。 将⼀个 32 位字移位超过 31 是没有意义,所以这个字段只有 5 位(它可以表示数字 0-31)
- 除移位指令外, 该字段均设置为 0
- R型指令的op都是 000 000,所以区别R型指令的是funct部分的编码,即最后六位
1. add rd ,rs ,rt
Reg[rd] = Reg[rs] + Reg[rt] 将 rs 的内容与 rt 的内容相加,将结果存储在 rd中
OP | Rs | Rt | Rd | shamt | funct |
---|---|---|---|---|---|
000000 | Rs | Rt | Rd | 00000 | 0x20 |
add和sub的op是相同的,区分他们的是funct
指令 | 格式 | op | rs | rt | rd | shamt | funct | address |
---|---|---|---|---|---|---|---|---|
add | R | 0 | reg | reg | reg | 0 | 32(10) | n.a. |
sub | R | 0 | reg | reg | reg | 0 | 34(10) | n.a. |
2. sll rd, rt, shamt
Reg[rd] = Reg[rt] « shamt rt内容左移 shamt位; 结果存于rd中
逻辑左/右移:把一个字里所有的位都向左/右移动,并在空出的地方补0
OP | Rs | Rt | Rd | shamt | funct |
---|---|---|---|---|---|
000000 | 00000 | Rt | Rd | 左移位数 | 0x00 |
逻辑左移还有额外的好处,左移i位= 原数 × $2^i$ 如3(00011)左移3位变24(11000),即3×2^3=24
3. sllv rd, rt, rs SLLV (SLL Variable), 左移位数存在寄存器RS中
Reg[rd] = Reg[rt] « Reg[rs] 将 rt 的内容左移 rs 的内容;将结果存储在 rd 中,即移位量不在指令中,而是在寄存器中
注意每个寄存器作用
同时也有右移指令:srl 和 srlv,用法与sll 和sllv相同
还有sll和srl是逻辑移动,还有算数移动,如:sra和sla,区别是补位是符号扩展(算数左移右边多出来的空也是补0)
I型指令
这些常数限定在16位,可表示范围:有符号数[-32768…32767] , 或⽆符号数 [0…65535]
如何存大于16位的常数
Example: 把32-bit 数值0x5678ABCD 存⼊ $5, 怎样处理?
解⼀ :
1
2
3
4
5
6
7
8
9
10
11
把⾼位的16 位 (0x5678) 先存⼊ $5
然后左移$5 内容16 位, (0x5678 0000)
再与低16 位相加“add” (0x5678 0000 + 0xABCD)
//错误
addi $5, $0, 0x5678
sll $5, $5, 16
addi $5, $5, 0xABCD
//正确
addiu $5, $0, 0x5678
sll $5, $5, 16
addiu $5, $5, 0xABCD
⼩问题:
- addi 处理常数为有符号数, 会造成错误,因为算数指令进行符号扩展
- 相加时要用无符号数相加指令 addiu 或者 ori 逻辑指令替代 (零扩展)
解二:
前⾯两步(addi + sll) 组合为lui:装入高16位立即数,16-bit立即数放⼊寄存器的前半部分
上面三行转为下列两行
1
2
lui $5, 0x5678
ori $5, $5, 0xABCD
1. addi rt, rs, imm
Reg[rt] = Reg[rs] + sxt(imm) 将rs的内容与常数相加;将结果存储在 rt中
OP | Rs | Rt | imm |
---|---|---|---|
0x08 | Rs | Rt | 补码,做符号扩展 |
2. addiu rt, rs, imm
Reg[rt] = Reg[rs] + (imm) 寄存器rs内容与⽆符号常数相加;结果存于rt中
OP | Rs | Rt | imm |
---|---|---|---|
0x09 | Rs | Rt | 做符号扩展 |
逻辑操作指令的常数, 是“unsigned”, 符号扩展,总是⽆符号数
ADDI、 ADDIU: 加立即数,区别在于是否检测溢出。 无符号加 不检测溢出 (因为检测溢出需要看符号位有无进位)
ADD、 ADDU: 加寄存器, 区别在于是否检测溢出。 无符号加 不检测溢出 (因为检测溢出需要看符号位有无进位)
即, 在忽略溢出的前提下, ADDI与ADDIU等价, ADD与ADDU等价。 对于CPU来说, 有符号或者⽆符号都不重要。 他们的运算都是⼀样的, 不管有没有符号位, 都是从最低位加, 进位, ⼀直到最⾼位, 区别在于是否具有溢出检测
3. lw rt, imm(rs)
Reg[rt] = Mem[Reg[rs] + sign-ext(imm)] 将内存中数取出存到rt中
tips:load和store都是相对于内存而言
1
2
3
4
5
做如下操作:
• 取出寄存器$rs的值
• 与⽴即数(有符号数)相加
• 相加结果作为内存地址索引
• 相应地址中取出的值存⼊寄存器 $rt 中
#### 4. sw rt, imm(rs)
Mem[Reg[rs] + sign-ext(imm)] = Reg[rt] rt内容取出存到内存中
1
2
3
4
5
做如下操作:
• 取出寄存器$rs的值
• 与⽴即数(有符号数) 相加
• 相加结果作为内存地址索引
• 读出寄存器$rt 的内容写⼊到内存此地址单元中
lw 和 sw 指令读或写的内容是 32-bit ⼀个字, 内存中占4个字节地址,因此, 地址计算必须是4的倍数 ,即 Reg[rs] + sign-ext(imm) ⼆进制数的后两位必须是“00” ,因为不是的话一定不是4的倍数。
1
2
3
4
这类指令也有针对字节访问的指令
•lb (load byte)
•sb (store byte)
• ⼯作⽅式相同, 但它们的地址不必是 4 的倍数
5.lb和sb
1
2
3
lb $t0, 1($s3) #load byte from memory
sb $t0, 6($s3) #store byte to memory
- load指令将内存中读出的字节放在⽬标寄存器的最右边 8 位
- 寄存器中的其它位怎么办? LOAD指令对高位进⾏0扩展。
- store 指令从寄存器中的最右边8位取出⼀个字节,把它写⼊内存字节处
6. 半字操作(16bits)
1
2
lh $t0, 1($s3) #load half word from memory
sh $t0, 6($s3) #store half word to memory
16 bits 内存取和写⼊内存
- load 半字 从内存取出16位半字, 放在⽬标寄存器的最右边, 高位0扩展
- store 半字, 从寄存器的右边取出16位, 写⼊内存半字, 占两个地址内存字的其它部分不变
7.条件跳转指令 MIPS Branch Instructions
OP | Rs | Rt | 16-bits signed constant(offset) |
---|---|---|---|
0x04(beq)/0x05(bne) | Rs | Rt | 往前跳为负偏移,往后跳为正偏移 |
beq rs, rt, label # Branch if equal
1
2
3
4
if (REG[RS] == REG[RT])
{
PC = PC + 4 + 4*offset;
}
bne rs, rt, label # Branch if not equal
1
2
3
4
if (REG[RS] != REG[RT])
{
PC = PC + 4 + 4*offset;
}
- 注意内存地址必须是4的倍数, 目标地址也必须是4的倍数
- 跳转目标规定的是相对于它的下⼀条指令的偏移 (默认取下⼀条指令)。 汇编器计算出相对于⽬标地址(通常⽤lable表示) 的偏移量, 偏移量常数字段16位限定了跳转范围 ==PC = PC + 4 + 4*offset==;
16 位分⽀偏移量通过以下公式转换为 32 位的值
1
16-bit 偏移量后⾯加两个0, 然后==符号扩展==为32-bit
跳转流程:
1
2
在取指bne后, PC 更新为下⼀条指令地址 (PC = PC + 4)
偏移量(加上 2 个低位零,相当于×4) 符号扩展后与(更新的) PC相加, 得到⽬标地址
例子:
1
2
3
4
5
6
7
8
9
0x00001234:beq $s3,$s4,True
sub $s0,$s1,$s2 #这里是PC+4
add #1
sub #2
j Fin #3
True:add #4
Fin....
-------------------------------------------------------------------------------------------------------------------------------
PC=PC+4+sign-ext(4*offset)=0x1234+4+16=0x1248
总结:
- Branch 分⽀指令的跳转范围被限制在离分⽀指令⼤约正负 32K 条指令 (正负 128K 字节($2^{17}$=131072 约等于128k))的范围
- 跳转目标规定的是相对于它的下⼀条指令的偏移 (默认取下⼀条指令)。
字节编址
- Big Endian(⼤端存储) :存储字的最左边字节地址作为字地址
- Little Endian(⼩端存储) :存储字的最右边字节地址作为字地址
J型指令
绝对跳转(无条件跳转)
指令(J型) | OP | 26-bit address |
---|---|---|
j | 2 | 后面会默认补2个0,即imm*4 |
jal | 3 | 后面会默认补2个0,即imm*4 |
指令(R型) | OP | Rs | Rt | Rd | shamt | funct |
---|---|---|---|---|---|---|
jr | 2 | Rs | 0 | 0 | 0 | 8 |
jalr | 3 | Rs | 0 | Rd | 0 | 9 |
1
2
3
4
j label # jump to label (PC = PC[31-28] || CONST[25:0]*4)
jal label # jump to label and store PC+4 in $31
jr $t0 # jump to address specified by register’s contents
jalr $t0, $ra # jump to address specified by first register’s contentsand store PC+4 in second register
1. j label
jump 的目标地址怎样规定 ?绝对地址的形成 :
- 指令中低26位, 后面加两个零
- 把PC+4的高4位与上面的28位合并为32位绝对地址
例子:
2. jal label
jal应该被称为 laj , “链接和跳转”:
- 第 1 步( link链接) : 将下⼀条指令的地址保存到 $ra,ra=pc+4(为什么是下⼀条指令? 为什么不是当前⼀条? )
- 第 2 步( jump跳转) : 跳转到给定的标签
- 没⽤jal指令之前,只能用j指令
1
2
1008 addi ra,zero,1016 #$ra=1016 ,要存储j指令后一条指令的地址,等下才可以返回主程序
1012 j sum #goto sum
- 使用jal指令
1
2
1008 jal sum # $ra=1012,goto sum
1012 ........
#### 3. jr $ra
jr 指令跳转的地址在寄存器$ra中
tips:指令是R型指令
Q:如果 branch 跳转⽬的地址远于它16位能表示的范围怎么办?
A:汇编器来救援 - 它插入⼀个无条件跳转到分⽀目标并反转条件
1
beq $s0, $s1, L1
变成
1
2
3
bne $s0, $s1, L2
j L1
L2:......
乘除指令
1. mult rs, rt
含义: 将寄存器 rs 和 rt 的内容相乘, 并将(64 位结果) 存储在⼀对特殊寄存器中{hi, lo}
t代表”结果临时存储“,也就是需要特殊寄存器hi/lo存储运算结果
- ⾼ 32 bits 存于 hi 中, 低 32 bits 存于 lo 中
要访问结果, 使⽤两个新指令
mfhi: move from hi
1
mfhi rd #move the 32-bit half result from hi to $rd
mflo: move from lo
1
mflo rd #move the 32-bit half result from lo to $rd
2. div rs, rt
含义: 将寄存器 rs 的内容除以 rt, 并将商存储在 lo 中, 余数存储在 hi中
- 访问结果, use mfhi and mflo
有两条对应的⽆符号数乘法和除法指令
- multu
- divu
比较指令
1. slt rd, rs, rt
slt = set-if-less-than 小于则置⼀
rd = (rs < rt) // “1” if true and “0” if false
2.slti rt, rs, imm
slti = set-if-less-than-immediate 小于立即数则置⼀
rt = (rs < sign-ext(imm) ) 做符号扩展
also unsigned flavors 对应两条⽆符号数⽐较指令
- sltu
- sltiu
逻辑操作指令
Boolean operations 布尔操作: 所有32位按位操作
- AND, OR, NOR, XOR
- and, andi
- or, ori
- nor // Note: There is no nori! Why? 或非没有与立即数比较的指令
- xor, xori
无i的都是R型指令,有i的都为i型指令
Review: MIPS Instructions
一般算数指令符号扩展,逻辑指令零扩展
Category | Instr | OPCode | Example | meaning | 备注 |
---|---|---|---|---|---|
Arithmetic (R & I format) | add | 0 & 20 | add s1 ,s2, s3 | s1 = s2 + s3 | |
subtract | 0 & 22 | sub s1, s2, s3 | s1 = s2 - s3 | ||
add immediate | 8 | addi s1, s2, 4 | s1 = s2 + 4 | 做符号扩展 | |
逻辑左移 | shift left logical | 0 & 00 | sll s1, s2, 4 | s1 = s2 « 4 | 在空出的地方补0 |
逻辑右移 | shift right logical | 0 & 02 | srl s1, s2, 4 | s1 = s2 » 4 (fill with zeros) | 在空出的地方补0 |
算数右移 | shift right arithmetic | 0 & 03 | sra s1, s2, 4 | s1 = s2 » 4 (fill with sign bit) | 在空出的地方做符号扩展 |
and | 0 & 24 | and s1, s2, s3 | s1 = s2 & s3 | ||
or | 0 & 25 | or s1, s2, s3 | s1 = s2|s3 | ||
nor | 0 & 27 | nor s1, s2, s3 | s1 = not (s2|s3) | 或非 | |
and immediate | c | and s1, s2, ff00 | s1 = s2 & 0xff00 | ||
or immediate | d | or s1, s2, ff00 | s1 = s2 | 0xff00 | ||
load upper immediate | f | lui s1,0xffff | s1 = 0xffff0000 | 后一步载入低16位时,用无符号数相加指令 addiu 或者 ori 逻辑指令,因为他们时0扩展,而addi是符号扩展 | |
异或 | xor s2,s1,s0 | s2=s1⊕s0 | |||
Data transfer (I format) | load word | 23 | lw s1 100(s2) | s1=Memory(s2+100) | |
store word | 2b | sw s1, 100(s2) | Memory(s2+100)=s1 | ||
load byte | 20 | lb s1, 101(s2) | s1=Memory(s2+101) | 对高位进行符号扩展 | |
store byte | 28 | sb s1, 101(s2) | Memory(s2+101)=s1 | 对高位进行符号扩展 | |
load half | 21 | lh s1, 101(s2) | s1=Memory(s2+102) | 对高位进行符号扩展 | |
store half | 29 | sh s1, 101(s2) | Memory(s2+102)=s1 | 对高位进行符号扩展 | |
Cond. branch (I & R format) | br on equal | 4 | beq s1, s2, L | if (s1==s2) go to L | 1.往前跳为负偏移,往后跳为正偏移;2.跳转目标规定的是相对于它的下⼀条指令的偏移;3.偏移量(加上 2 个低位零) 符号扩展后与(更新的) PC相加, 得到⽬标地址 |
br on not equal | 5 | bne s1, s2, L | if (s1 !=s2) go to L | 4.跳转范围被限制在离分⽀指令⼤约正负 32K 条指令 (正负 128K 字节(±2^17=±131072 约等于128k))的范围。 | |
set on less than immediate | a | slti s1, s2, 100 | if (s2<100) s1=1; else s1=0 | ||
set on less than | 0 & 2a | slt s1, s2, s3 | if (s2<s3) s1=1; else s1=0 | ||
Uncond. jump | jump | 2 | j 2500 | go to 10000 | 绝对地址的形成 :指令中低26位,后面加两个零把PC+4的高4位与上面的28位合并为32位绝对地址,即跳转范围为距离当前地址高4位2^28≈256M的地方 |
jump register | 0 & 08 | jr t1 | go to t1 | ||
jump and link | 3 | jal 2500 | go to 10000; ra=PC+4 | 跳转的最远的是jr和jalr,因为他们跳转的地址是寄存器中存的,即有32位,而jal和j只有28位,beq只有17位(因为是16位左移2位变18位,但beq是I型指令,imm中第一位是符号位) | |
MULT instruction | 0 & 0x18 | mult rs, rt | ⾼ 32 bits 存于 hi 中, 低 32 bits 存于 lo 中 | ||
DIV instruction | 0 & 0x1a | div rs, rt | 商存储在 lo 中, 余数存储在 hi中 | ||
move from hi | 0 & 0x10 | mfhi rd | move the 32-bit half result from hi to rd | ||
move from lo | 0 & 0x12 | mflo rd | move the 32-bit half result from lo to rd |