MIPS 操作数
名字 | 示例 | 注释 |
---|---|---|
32个寄存器 | $s0-$s7, $t0-$t9, $zero, $a0-$a3, $v0-$v1, $gp, $fp, $sp, $ra, $at |
寄存器用于数据的快速存取。在MIPS中,只能对存放在寄存器中的数据执行算数操作,寄存器$zero的值恒为0,寄存器$at被汇编器保留,用于处理大的常数 |
20^30个存储器字 | Memory[0], Memory[4], …, Memory[4294967292] |
存储器只能通过数据传输指令访问。MIPS使用字节编址,所以连续的字地址相差4。存储器用于保存数据结构、数组和溢出的寄存器。 |
第一部分 MIPS-32概述
指令的组成 MIPS的设计思想
计算机执行任何程序,本质上都是在执行机器语言指令(instruction),每条指令都是一条0-1串
指令首先要指明执行什么操作,通常用0-1串中的前几位来表示,称为操作码 指令还要指出需要操作的数据来自哪里、操作后的结果数据放回哪里 通常用0-1串中的剩余位来表示,称为操作数或地址码大部分操作数都是一个地址编号,告诉CPU从哪里取得数据、向哪里放回数据 所以操作数通常也叫做地址码
操作码 | 地址码 | 地址码 |
---|
MIPS作为RISC指令集,设计力求保证硬件设备的简单性,在我们讲解的32为MIPS汇编语言(MIPS-32)中,所有指令都是32位长
MIPS-32中的通用寄存器
MIPS中运算操作的操作数必须来自寄存器(register)或者指令本身一种位于CPU、比cache更小更快的存储器,用来暂时存放运算的源数据和结果
一些寄存器是专用的,如存放执行中指令的地址的程序计数器(PC)于此相对应,用于暂时存放运算数据的寄存器称为通用寄存器
MIPS中一共有32个32位寄存器,共128B(大部分架构都采用16或32个寄存器)
我们约定:
- 程序中的变量存放在保存寄存器(store reg)中:$s0 ~ $s7 共8个
- 运算的临时变量、中间变量存放在临时寄存器(temp reg)中:$t0 ~ $t7 共8个
- 还有一个零寄存器,永远存放32位的0,写作$zero
第二部分 三类汇编指令
算数运算:加add、减sub
C赋值语句:c = a + b;
加法指令 add c, a, b: 将a和b中的数据相加,并将结果存放在c中
再次强调:MIPS中运算的操作数必须来自寄存器或者指令本身!
假设变量a,b,c分别存放在寄存器$s0,$s1,$s2中,这条指令就应当写为
add $s2, $s0, $s1
加法中两个数可以对换,但减法不行,故c = a - b;必须写作
sub $s2, $s0, $s1
运算的 “原材料” a和b对应的寄存器$s0, $s1
分别称为源操作数1*(src1)和源操作数2(src2)
运算的结果c对应的寄存器$s2称为目的操作数(des)
加减指令的通式:add/sub des, src1, src2
算术运算:加立即数addi
在 i++ 即 i = i + 1;这条赋值语句中,有个确定的常数1
与其采取额外的步骤将1装入某个寄存器,不如让指令本身包含这个1
假设变量i位于寄存器$s0,我们把加法指令的第二个源操作数改为常数1
addi $s0, $s0, 1
就成了加立即数(add immediate)指令
因为addi指令中的立即数可以取负数(对立即数取负后相加)
因此,MIPS中没有subi指令
逻辑按位运算:and、or、nor指令
当两个源寄存器中,对应的位上同时为1时,与and操作结果为1 当两个源寄存器中,对应的位上至少有一个为1时,或or操作结果为1 因此,假设
$t0 = 0000 0000 0000 0000 0000 0000 0000 1001
$t1 = 0000 0000 0000 0000 0000 0000 0000 1100
执行下列两条指令后,$t2中的数据分别变为多少?
and $t2, $t0, $t1
or $t2, $t0, $t1
任何数据与0进行或非nor操作,都会0/1反转 执行下列指令后,$t2中的数据会变为多少?
nor $t2, $t0, $zero
$t2 = 1111 1111 1111 1111 1111 1111 1111 0110
逻辑位移运算:sll 和 srl 指令
比较12和120两个十进制数,通过在对低位的右边添加一个0,变成了10倍
比较11和110两个二进制数,通过在最低位的右边添加一个0,变成了多少倍?1100呢?
逻辑左移(shift left logic)指令让寄存器中的数据整体往左移动指定的位数,并在右边空出来的位上补0。
假设$s2 = 0000 0000 0000 0000 0000 0000 0000 0101
逻辑左移两位后,放到寄存器$s0中:
sll $s0, $s2, 2
这里的2不是addi指令中的立即数,而是告诉计算机移动几位的位移量(shift amount)
通过这样一条指令,我们实际上完成了x4的运算!
x2、x8、x128时,移位量分别是多少?
x2移位量为1,x8移位量为3,x128移位量为7
srl 指令可以实现/2运算,使用场景不多,不额外讨论
综合练习1:变量运算与赋值
翻译以下C语句:
result = a - 10 + (b + c * 5);其中 result 为 $s3,a 为 $s0,b 为 $s1,c 为 $s2
1 | c * 5 -> sll $t0, $s2, 2 |
寄存器—存储器数据传送:lw指令
运算指令的操作数必须来自于寄存器/指令本身,但是,通用寄存器一共只有128B
数组元素却可以占据成千上万个字节,只能存放在内存中
这时,我们把数组第一个元素(a[0])的32位地址,称为数组的基址,放在寄存器中
基址加上要找的元素的下标,就组成了这个元素的地址
如果源操作数在内存中,是数组a的5号元素(第六个元素),数组a的基址存放在$s1中。那么,a[5]的地址就表示为5($s1)
计算机会自动计算$s1中的基址和偏移量5的和,找到a[5]的地址
将a[5]从内存传送到寄存器$s0,使用取字指令(load word)
lw $s0, 5($s1)
寄存器—存储器数据传送:字与sw指令
MIPS的通用寄存器都是32位长,这个长度就是MIPS体系结构的字长,通常代表了参与运算的数据的长度,因此我们约定:整门课程中,1字=32b=4B(字节)
a[5]相对于a[0],在内存中的距离是5个字,而不是5个字节。又因为内存按字节编址,即:内存每个字节都有一个特定的编号。
所以偏移量应该是 5x4=20个字节,a[5]的地址应该表示成20($s1),于是取数指令变为lw $s0, 20($s1)
如果我们要把$t0中运算结果送回内存中的a[2],需要用到存字指令(store word): sw $t0, 8($s1)
寄存器间数据传送 装载立即数到寄存器
如果我们需要把数从$t0保存到存放某变量的$s1中,怎么实现?
MIPS没有专门的寄存器间移动数据的指令。但是,通过把源寄存器中的数据加上0再保存到目标寄存器中,可以实现相同的功能
addi $s1, $t0, 0
或 add $s1, $t0, $zero
这个功能可以用move伪指令来代替 move $s1, $t0
假如我们要把一个常数10装入寄存器$s2,同样可以采用addi指令
addi $s2, $zero, 10
或使用取立即数(load immediate)伪指令 li $s2, 10
程序设计题中能否使用伪指令,请咨询老师!
装载32位立即数到寄存器
我们说可以用addi指令向寄存器装载立即数:addi $s2, $zero, 10
但是,addi指令中的立即数10只能占用32位指令中的一部分(16位,稍后介绍指令格式)
16位只能表示2E16,即六万多个数,寄存器却能容纳2E32即40多亿个数
addi 指令只能作用于 -2^15 ~ 2^15-1 个立即数中(即-32768 ~ 32767)
二进制与十六进制的转化在此不作介绍
假设我们要向寄存器$s2装载一个32位的立即数:10A2 7FFF(16)
我们必须先用取高位立即数(load upper immediate)指令,把10A2放入$s2的高16位
lui $s2,4258
#十六进制的10A2等于十进制的4258
再让$s2与低16位的立即数7FFF进行或运算
ori $s2, $s2, 32767
#7FFF(16)=32767(10)
这样,就分两步把32位立即数装载到了32的寄存器中
不能使用addi代替ori指令,如果低16位的最高位是1,addi会把它理解为负数
综合练习2:数组元素运算与赋值
a[i] = a[0] + 100000;
假设数组 a 的基址位于 $s0,变量 i 位于 $s1
100000(10) = 186A0(16),1(16) = 1(10),86A0(16) = 34464
1 | 1.将a[0]传到临时寄存器中 |
决策:条件分支beq和bne
计算机和一般计算器的区别在于何处?
在于决策能力
即,根据一定的条件选择执行何种运算的能力
最基础的判断条件是相等关系
假设 $s0 = 0,$s1 = 0,$s2 = 1
相等则分支(branch if equal)指令在两个源操作数寄存器中的值相同时分支,分支以分支标签表示
beq $s0,$s1,Label
与此相对应,不等则分支(branch if not equal)指令在值不同时分支到标签
bne $s0,s2,Label
如果 不发生分支,则继续执行内存中相邻的下一条指令
综合练习3:if-else语句(无条件跳转 j 和条件分支)
If (i == j) f = g + h;
else f = g - h;
假设f、g、h、i、j 分别存放在$s0 - $s4中
1 | bne $s3,$s4,Else |
结论:判定相等 == 使用bne,判断不等 != 使用beq
决策:小于则置位slt
除了相等、不等关系,我们还经常比较两个数的大小
MIPS有一条小于则置位(set on less than)指令slt
置位:将一位设置为1;复位:将一位设置为0
还是假设$s0 = 0,$s1 = 0,$s2 = 1
slt $t0,$s0,$s2
源操作数1 < 源操作数2 吗? Yes
此时把目的炒作输寄存器$t0置位为1
slt $t0,$s0,$s1
源操作数1 < 源操作数2 吗?No!
此时把目的操作数寄存器$t0复位为0
在比较中经常使用常数操作数,所以有立即数版本的小于则置位指令。
slti $t0, $s2, 10 # $t0 = 1 if $s2 < 10
6种条件判断及其伪指令
通过slt、beq、bne(严格来说还有小于立即数则置位slti指令,不作讨论)指令的各种组合,我们就能够实现全部六种比较指令,即六种值为真或假的布尔表达式
1 | if (i < j) f = g + h; |
1 | slt $t0, i, j # 当 i<j 时,把$t0置为1,否则为0 |
结论:判断大于 > 或小于 < 使用 slt 和 beq,判定大于等于 >= 或小于等于 <= 使用 slt 和 bne
对于比大小的四种比较条件,可以使用伪指令:
- 小于则分支 blt
- 大于则分支 bgt
- 小于等于则分支 ble
- 大于等于则分支 bge
综合练习4:while循环
while(a[i] == k) i++;
假设i,k分别存放在$s3和$s5中,a的基址放在$s6中
1 | Loop: sll $t0, $s3, 2 # 4i |
MIPS汇编指令小结
指令格式:R型
指令中含三个寄存器的运算指令都属于R型(register type)指令
add/sub
des,src1,src2
and/or/nor
des,src1,src2
slt
des,src1,src2
32位的MIPS指令一共分为6个字段:
op | rs | rt | rd | shamt | funct |
---|---|---|---|---|---|
6位 | 5位 | 5位 | 5位 | 5位 | 6位 |
- op:operation code,源操作码
- rs:register source,源操作数寄存器 -> rt:s后面是t,表示第二个源操作数寄存器
- rd:register destination,目的寄存器
- shamt:shift amount,位移量
- funct:function code,功能码
指令格式:R型
R型指令的操作码op都是6个0,由6位功能码funct进一步指定执行什么操作
以add指令为例
$t0 ~ $t7分别为8~15号寄存器
$s0 ~ $s7分别为16~23号寄存器
将下列机器码,翻译成MIPS-32指令
000000 | 10001 | 10010 | 01000 | 00000 | 100000 |
---|---|---|---|---|---|
6位 | 5位 | 5位 | 5位 | 5位 | 6位 |
1 | 操作码 op 为0,功能码为32,即为add指令 |
sub指令仅仅是功能码funct字段从32变为了34,sub $s1, $s1, $s0的32位机器码是多少?
需要记忆add、sub指令的操作码(都是0)和功能码(分别为32、34)
1 | sub 的操作码 op 为 000000 |
op | rs | rt | rd | shamt | funct |
---|---|---|---|---|---|
000000 | 10001 | 10001 | 10000 | 00000 | 100100 |
此外,使用移位量的两条逻辑移位指令
sll/srl
des, src1, shamt
也属于R型指令,因为没有第二个源操作数寄存器,rt被置为0
指令格式:I型(立即数)
有两条“目的reg + 源reg + 立即数” 格式的指令
addi
des,src1,i
ori
des,src1,i
通过把R型指令中的后桑格字段拼接成一个16为的里结束字段,让指令本身包含常数。这样的指令属于I型(immediate type)指令
op | rs | rt | rd | shamt | funct |
---|---|---|---|---|---|
6位 | 5位 | 5位 | 5位 | 5位 | 6位 |
op | rs | rt | constant or address |
---|---|---|---|
6位 | 5位 | 5位 | 16位 |
以 addi 指令为例
其操作码为8
,由于 rd 字段被合并了,现在 rt 就成了目的寄存器
通过 I 型指令格式也可以看出,立即数最大只有 16位,再加上一位的正负号,只有15位可用(即-2^15 ~ 2^15-1 -> -32768 ~ 32767)
练习1
将此指令翻译成机器指令:addi $t1, $t0, 15
1 | addi 指令的操作码 op 为8,即001000 |
op | rs | rt | constant or address |
---|---|---|---|
001000 | 01000 | 01001 | 0000000000001111 |
指令格式:I型(偏移量)
lw/sw reg, num(reg)
两条数据传送指令也包含两个寄存器和一个常数
同样属于I型指令
此时,16位立即数字段的含义发生了改变,表示数组元素相对于数组基址的地址偏移量
op | rs | rt | constant or address |
---|---|---|---|
6位 | 5位 | 5位 | 16位 |
无论是lw还是sw指令
都是由 rs 字段表示的寄存器值与 address 字段相加,得到存储器单元地址
rt 字段表示与存储器单元交换数据的寄存器
lw、sw 指令操作码分别为 35 和 43
练习1
将此指令翻译成机器指令:lw $t0, 8($s1)
1 | lw 指令的操作码 op 为35,所以换成二进制为10011 |
op | rs | rt | constant or address |
---|---|---|---|
10011 | 10001 | 01000 | 0000000000001000 |
练习2
将此指令翻译成机器指令:sw $t2, 0($s4)
1 | sw 指令的操作码 op 为43,所以换成二进制为11101 |
op | rs | rt | constant or address |
---|---|---|---|
11101 | 10010 | 10100 | 0000000000000000 |
指令格式:I 型(标签)
beq/bne src1, src2, Label
在这两条条件分支指令中,同样是使用了两个寄存器
还有一个分支标签的地址,用16位立即数字段表示(也就变成了 Address 字段)
也属于 I 型指令
例如,当 i($s0) 和 j($s1) 相等时分支到地址为 10000 的标签 Else
beq $s0, $s1, Else
翻译为机器语言为
op | rs | rt | constant or address |
---|---|---|---|
6位 | 5位 | 5位 | 16位 |
这里的 10000 实际上并不是 Else 标签指向指令的地址,讲寻址方式时再具体说明
机器语言指令格式小结
名字 | 格式 | 举例 | 注释 | |||||
---|---|---|---|---|---|---|---|---|
add | R | 0 | 18 | 19 | 17 | 0 | 32 | add $s1, $s2, $s3 |
sub | R | 0 | 18 | 19 | 17 | 0 | 34 | sub $s1, $s2, $s3 |
addi | I | 8 | 18 | 17 | 100 | addi $s1, $s2, 100 | ||
lw | I | 35 | 18 | 17 | 100 | lw $s1, 100($s2) | ||
sw | I | 43 | 18 | 17 | 100 | sw $s1, 100($s2) | ||
字段宽度 | 6位 | 5位 | 5位 | 5位 | 5位 | 6位 | 所有 MIPS 指令均为32位 | |
R型 | R | op | rs | rt | rd | shamt | funct | 算数指令格式 |
I型 | I | op | rs | rt | address | 数据传送指令格式 |
lui 指令的指令格式不作讨论
五条伪指令本身不是真正的指令,程序运行时会被替换成为真正的指令,不讨论指令格式
j 指令的指令格式稍后讲解
复习题
1、指令通常由那两个部分组成?MIPS-32 指令长度均为多少?
- 操作码和操作数(地址码)
- MIPS-32 指令长度为32位
2、8个临时寄存器、8个保存寄存器分别是什么编号?零寄存器存储什么?
- 8个临时寄存器($t0 ~ $t7)的编号为 8-15
- 8个保存寄存器($s0 ~ $s7)的编号为 16-23
3、回顾综合联系1 ~ 4,掌握运算、数据传输、决策三类汇编指令。(注意字和字节的区别)
4、练习上面的五条指令(add、sub、addi、lw、sw)汇编语言和机器语言的转化
转自:B站翼云