第五章:指令系统

第五章:指令系统

记得在第一章写的层次结构吗?

image-20251117153817438

ISA是一种规定和结构规范,是软件和硬件之间的接口。

目前分为Intel x86(支配着计算机的PC时代)

arm(最流行的嵌入式设备指令系统)

MIPS和RISC-V(新兴的,能够使得计算机系统构建变得简单)

指令集又分为复杂指令集精简指令集,Intel x86属于复杂指令集,其他属于后者。

MIPS和X86两种ISA,都有32位和64位两种版本,也就是说寄存器可以是32位和64位

image-20251125221854356

本章的学习流程会配合C语言,需要你根据一段C语言代码找到对应的MIPS代码。使用GodBolt能够将C语音代码转化为其他指令系统的代码。

image-20251126111028860

MIPS

MIPS是“Microprocessor without Interlocked Pipeline Stages”的缩写,即“无互锁流水级的微处理器”

MIPS规定了在CPU内部有32个通用寄存器,每个寄存器的大小是32位,32位为一个字,也就是一个字等于四个字节。

MIPS是精简指令集

image-20251201144921739

寄存器寻址

image-20251201193928997

注意看,addu是无符号加指令,subu是无符号减指令(上面没有) ,li是加载指令,jal是程序跳转指令,该指令意味着程序需要暂时离开当前位置去执行一个子任务(函数或者子程序)

首先,将25加载到7号寄存器,将12加载到6号寄存器,直到将-10加载到4号寄存器,jal指令跳转到函数add中继续执行,将4号和5号相加,结果存到4号寄存器,随即将4和6相加放到4号,再将4和7相加,结果放到2号寄存器返回给res

基址寻址

image-20251201200613540

栈指针是29号寄存器,里面存有一个存储地址这里是偏移值8,sw是存数指令,将一个字的数据存到指定的内存单元中。

这里将1存入2号寄存器,随后将2号寄存器的值存入到24+$sp(8) = 32号主存中,循环往复直到主存被写入A数组的6个元素,随后给5号寄存器写入10,addiu执行将8+24=32存放到4号寄存器中,随后jal跳转到add_3

add_3中有三条指令

1
2
3
lw $2,12($4)
nop
addu $2,$2,$5

lw是取数,从指定内存地址取得一个字,这是取得4号寄存器的值32+12(索引是3,一个字是4,那就是3*4 = 12)= 44存入2号寄存器中,nop指令是空操作指令,addu将2和5相加,结果存入2号寄存器

数据传送指令:在主存储器和寄存器之间传送数据指令,比如sw,lw。

$sp也可以叫基址寄存器,又名栈指针寄存器,写在前面的值是偏移值offset,目标地址为$sp+offset。

CPU寄存器是主体,主存是客体

初次之外还有立即寻址,也就是操作数就在指令内,这样的操作数称为立即数。

整体指令执行流程

image-20251202144247923两种寻址方式讲的有点细,我们需要跳出来看整体的执行流程。指令也一样是存在主存中的,一开始PC是0,CU向PC和MAR发送信号,将PC的值发送到MAR,通过地址总线读取0号内存的指令,CU通过控制总线发送信号,主存将指令发送到CPU的MDR中,PC自动加4(每一条指令4个字节),MDR中的指令会移交到IR,CU对IR做译码操作,ALU将4号寄存器里的值和偏移值12做加法,得到44送到MAR,CU,MAR配合读出44号内存的数字存到寄存器中。

R型指令格式

R型指令可以表示算数,逻辑,移位运算指令。

一条指令对应的二进制位长度就是指令字长。

精简指令集比如RISC或者MIPS是定长的。

复杂指令集比如CISC是变长的。

MIPS中所有的指令都是32位的,32位分布结构如下:

image-20251202145924526

前六位op是用来区分指令格式的,可以分为R型(寄存器型),I型(立即数型),J型(跳转型)等等。

比如指令add $2,$4,$6$4是rs(第一源),$6是rt(第二源),$2是rd(目的寄存器)。

指令送入IR中,译码器根据前6位操作码确定格式,CU再根据最后6位功能码进行译码分析其操作,转交ALU或者其他模块执行。

寄存器地址是5位,因为2^5=32,正好能够表示32个寄存器。

而位移量是5位,5位二进制数恰好可以表示0到31(2^5=32个值),而这足以覆盖一个32位寄存器所有有意义的移位操作。

你只需要知道原理,不需要知道具体怎么写,我们不可能用汇编语言或者机器语言来写程序。

乘除指令

记得之前说过的吗?有两个32位的乘商寄存器:Hi和Lo是专用寄存器。

image-20251202160008452

mult是将两个操作数存到专用寄存器中,mflo再将结果存到2号寄存器中

除法也是差不多,在这里就不赘述了。

R型指令格式的总结

image-20251202160632925

因为指令字长是一定的,所以即使nop只有一个操作字段,也不得不用32位来表示,虽然这样看起来确实很规整。

I型指令格式

image-20251202192910943

其中rs是源操作数寄存器,rt是目标操作数寄存器,im是立即数,就是包含在指令中的操作数,范围是-32768到+32767。

I型指令没有独立的功能码,因为从根本上就不需要。

比如指令lw $2,1200($4),所以$4是rs,1200是立即数,$2是目标操作数寄存器。

总结

image-20251202193834837

MIPS执行if语句

image-20251202195009184

image-20251202202342495

image-20251202202356984

MIPS中执行循环结构

image-20251203134516104

将同一段高级语言代码能够转化为不同的汇编语言代码,不同汇编代码之间性能差别很大,挑选并采用最佳性能的过程叫优化。

image-20251203135415382

image-20251203135444575

指令设计

指令长度:字面意思,就是指令的长度,就是指令的二进制位数。

越短越好,降低空间开销。

可以设置为**定长指令集(MIPS),或者是变长指令集(x86)**。

指令格式:

一般指令都需要两个部分,一个是操作码一个是地址码,地址码可以是立即数或者寄存器编号,也可以有多个地址码。

image-20251209143216922

但是我们会使用PC存放下一个指令地址

image-20251209142906162

有些操作不需要输出到某个寄存器

image-20251209143256102

部分操作是单目的。

image-20251209143311733

随地址码位数扩大,那么能表示的地址范围就越来越大。

指令寻址

分为立即数寻址,直接寻址,间接寻址,相对寻址,基址寻址,变址寻址,隐含寻址

其中基址寻址,变址寻址,隐含寻址统称偏移寻址。

立即数寻址就是在指令中直接给出操作数本身。

直接寻址分为寄存器直接寻址和主存直接寻址。

寄存器直接寻址就是操作数存放在CPU寄存器中,主存直接寻址就是操作数放在主存中,指令给出寄存器编号或者主存的有效地址来寻找并调用操作数。

因为寄存器数量远小于主存单元数,所以寄存器编号比主存短很多,所以寄存器寻址方式的指令比较短,访问也比主存更快。

间接寻址也分为寄存器间接寻址和主存间接寻址。

寄存器间接寻址就是:指令中的地址码是一个寄存器编号,寄存器中存放的是操作数的有效地址,常用于指针。

image-20251209144854536

主存间接寻址就是:在指令中给出的地址码是存放操作数有效地址的主存单元地址,用的比较少。

image-20251209145031647

基址寻址:指令中给出形式地址R和偏移量offset

操作数的有效地址等于给出的形式地址R加上某个偏移量offset

image-20251209145309835

相对寻址:操作数有效地址等于PC寄存器的值加上给出的偏移值offset

image-20251209145521340

变址寻址:

操作数地址等于一个基地址和一个动态可变偏移量相加。

可变偏移量往往是一个内容可改变的寄存器

image-20251209145947932

隐含寻址是一种特别的指令设计技巧,它的核心在于指令中并不直接写明某个操作数的地址,而是通过操作码或约定俗成的规则隐含地指定

为了让CPU知道寻址方式,指令往往是这种结构:

image-20251209150127712