指令系统基础
复习
- 除法器:除法拆成逐位试减和移位,并用状态机推动
- 从寄存器到存储器:用地址、译码器和存储矩阵实现容量更大的存储器
- 一台会记事的小计算机:把运算器和存储器组装成能连续计算并保存结果的机器
TL;DR
- 指令是机器能执行的一条命令
- 操作码说明做什么,操作数说明对谁做
- 指令也是一串比特,因此可以和普通数据一起存放
正文
上一章那位忙碌的人类指挥员,嘴里念叨着:“读地址 20”“读地址 21”“做加法”“写到地址 22”。要让机器自己工作,第一步就是把这些话翻译成机器能处理的固定格式——一串比特。
这样的一句机器能听懂的话,叫作指令(instruction)。
一句话的两部分
一条指令至少要回答两个问题:
- 做什么:操作码(opcode)
- 对谁做:操作数(operand)
这其实和日常说话很像:“把盐递给我”里,“递”是动作,“盐”是对象。机器语言也遵循同样的套路,只是把动作和对象都编码成了数字。
例如,给极简机器约定这样一张表:
| 操作码 | 含义 |
|---|---|
0001 | 把某地址的数据读入寄存器 A |
0010 | 把某地址的数据读入寄存器 B |
0011 | A 与 B 相加,结果放进结果寄存器 |
0100 | 把结果写到某地址 |
若后面 8 位用来写地址,那么:
0001 00010100
就可以表示“把地址 20 的数据读入 A”。前 4 位是操作码,后 8 位是操作数。拆开来读,意思清清楚楚。
编号本身没有魔法
为什么 0001 表示读取,而不是加法?没有为什么,这不过是设计者的约定。
这就像字符编码里约定 65 表示字母 A。数字 65 本身并没有“A 性”,只是因为收发双方用了同一张表,它才代表 A。机器世界里的约定,只要你我都照同一张表解释,就成立。
所有可用指令,以及它们的编码规则,合起来叫作指令集(instruction set)。一台机器全部的本事,就写在这张表里。
指令也是数据
指令写成比特以后,外表和数字没有任何区别。它一样可以被存入、读取和搬运。
真正的不同只在于谁来解释它:控制器把这串比特当作命令来执行,ALU 则把另一串比特当作数值来计算。同一堆比特,进了不同的部件,就活成了不同的东西。
从哪里取操作数
“对谁做”里的那个“谁”,通常不是直接写在指令里的数,而是一个地址。那么,这个地址怎样变成真正的数据?这就引出了寻址方式(addressing mode)。
常见的几种:
- 立即寻址:操作数就是指令里直接给出的那个数
- 直接寻址:指令里给的是数据所在的地址
- 间接寻址:指令里给的是一个地址,那个地址里才存着真正的目标地址
- 寄存器寻址:操作数就放在某个寄存器里
- 变址/基址寻址:地址由“基址 + 偏移”算出来,特别适合访问数组和结构体
寻址方式越丰富,一条指令能表达的意思就越多,程序也能写得更紧凑。它是后面理解汇编语言、以及编译后端怎样生成取数指令的基础。
现在,我们可以把许多条指令依次放进存储器了。可机器执行完第一条以后,怎么知道第二条在哪?下一章,我们给它一根始终指向“下一条指令”的手指。
思考题 1
同一串比特为什么既可能是数字,也可能是指令?机器靠什么决定怎样解释它?
思考题 2
如果两台机器用了不同的指令集,同一段程序能在它们上面直接运行吗?为什么?
小结
知识点
- 指令由操作码和操作数组成
- 指令集规定机器能听懂哪些编码
- 指令与普通数据在存储器里都是比特
参考资料
- Wikipedia(zh):指令集架构:处理器能理解的指令与编码规范
- Wikipedia(zh):操作码:指令中说明“做什么”的部分
思考题答案(仅供参考)
思考题 1
比特本身没有自带含义。它来自哪个位置、被送到哪个部件,决定了它的解释方式:进入指令寄存器并交给控制器时被当作命令,进入运算器时则可能被当作数字。所以“含义”是使用方式赋予的,而不是比特自带的。
思考题 2
通常不能。指令集是机器与程序之间的约定,操作码的编号、指令的格式都可能不同。同一串比特在另一台机器上,可能被解释成完全不同的指令,甚至根本没有对应含义。这正是软件兼容性问题的根源。
协议
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
封面图
设计师 | 南国微雪