指令系统基础
复习
- 第十八章:设计完成了算术逻辑单元(ALU),它可以完成多种运算
- 第二十七章:做出了累加器,它是运算部件的雏形
- 第二十八章:了解了时序逻辑电路与状态机
TL;DR
- 硬件电路是“死“的,只能做固定的运算;要让机器做不同的事,就得引入“指令“
- 一条指令 = 操作码(做什么)+ 操作数(对谁做)
- 指令按功能分为:数据传送、算术运算、逻辑运算、控制转移
- 指令格式有定长与变长两种,各有取舍
正文
从电路到指令
回头看,我们造的电路都很“死“:加法器只会加,减法器只会减。如果想让机器做一件新事,就得重新接一次线,这显然不现实。
就像钢琴,琴键按下去只能发出固定音高;真正让它能弹奏无数曲子的,是“乐谱“。我们也可以给计算机配一份“乐谱“——一串事先约定好的操作命令,让它照着做。
这串命令,就叫指令(Instruction)。
一条指令长什么样
一条指令至少要说清两件事:
- 做什么?——这部分叫操作码(Opcode),比如“加““减”“跳转”
- 对谁做?——这部分叫操作数(Operand),可以是立即数、寄存器编号或内存地址
举例来说,ADD R1, R2, R3 的意思就是“把 R2 和 R3 相加,结果放进 R1“。其中 ADD 是操作码,R1/R2/R3 是操作数。
冯·诺依曼体系结构最天才的一点,就是把指令和数据一起放在存储器里。指令不再需要额外接线,而是和普通数据一样被读取、被处理。程序因此变成了可以存储在磁盘上、随时加载的东西。
常见的指令类型
现实中的指令成百上千,但按功能大致分四类:
- 数据传送:搬运数据
MOV R1, R2:把 R2 的值复制到 R1LOAD R1, [addr]:从内存读进寄存器STORE [addr], R1:把寄存器写回内存
- 算术运算:加减乘除
ADD、SUB、MUL、DIV
- 逻辑运算:与或非、移位
AND、OR、NOT、LSL(逻辑左移)等
- 控制转移:改变执行顺序
JMP:无条件跳转BEQ、BNE、BGT:满足条件才跳转
前几类靠 ALU 就能完成,第四类则决定了程序能不能“拐弯“。
定长还是变长
指令在机器里就是一串二进制码,怎么划分这些码,有两种设计:
- 定长指令:所有指令一样长。译码简单、流水线友好,但可能浪费空间
- 变长指令:指令长度不一。更省空间、表达能力更强,但译码复杂
这就像快递盒:统一尺寸好装箱、好分拣,但不一定贴着货物;定制尺寸省材料,但分拣起来麻烦。两种路线各有代表(RISC 偏向定长,CISC 偏向变长),至今仍在竞争。
思考题
为什么冯·诺依曼体系结构要把“指令“和“数据“放在同一个存储器里?这样做有什么好处,又可能带来什么问题?
小结
知识点
- 指令与操作码、操作数
- 存储程序思想
- 指令的四大类型
- 定长与变长指令格式
参考资料
- Wikipedia(zh):指令集架构:指令集的基本概念
- Wikipedia(zh):汇编语言:低级编程语言
- Wikipedia(zh):RISC:精简指令集计算机
思考题答案(仅供参考)
好处:指令像数据一样存在内存里,程序可以被灵活加载、修改,硬件只需一套取指-译码-执行的机制,通用性大大增强。问题:指令和数据混在一起,如果程序出错、跳转到数据区去执行,就会把数据当指令执行,这既是“程序可以自我修改“的灵活之处,也是很多漏洞(如缓冲区溢出攻击)的根源。
协议
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
封面图
设计师 | 南国微雪