8 | 程序 & 指令
上一次把 RAM 控制单元 ALU 时钟结合在一起,做了简化版的CPU。
给CPU 加指令运行。
CPU 之所以强大是因为它是可编程的,写入不同的指令执行不同的任务。
CPU 是硬件 可以被软件控制。
image.png
上次的RAM 没地地址存8位,前4位是操作码,后四位是内存地址,或寄存器。
地址换成指令方便理解:
image.png
- Load A 14 把3 放入寄存器A
- Load B 15 把14放入寄存器B
- ADD A B 把寄存器A 寄存器B 相加,但顺序很重要,因为结果要4存入第二个寄存器中。
- store a 13 把寄存器A存如 RAM 13
image.png
4个指令也只能做这个了
新加指令:
-
Sub :两个指令相减
-
JUMP :让程序跳转新位置,可以改变指令顺序或跳过指令
jump 0 跳回开头,底层实现方式是把指令后4位代表的内存地址的值。覆盖掉指令地址寄存器的值。 -
JUMP _ NEGATIVE : 特别版的jump
在ALU 的负数标志 为真时进线JUMP
image.png
算数标志为负数才是真,结果不是负数,负数标志为假。
如果是假, JUMP _ NEGATIVE 不会执行,程序继续。
- 停止 指令 HALT
计算机需要知道什么时候该停下来。
image.png
指令和数据都是存才同一个内存里面的
他们在根本层面上没有区别,都是二进制数。
HALT 能区分指令或是数据
使用JUMP ,从CPU视角走一遍程序
image.png
-
把Load A 14 的1 存如寄存器A
-
把Load B 15中的1 存入寄存器B
-
然后Add 把寄存器B和A相加,结果放到寄存器A里面,里面是2 ,就是 0010
-
把 寄存器A的值存入内存地址 13 值是 2 ,0010
-
JUMP 2 ,把指令寄存器的值(现在是4 )改成2,先一步不是HALT ,还是ADD B A 相加。
-
寄存器 A是2 B 是1 相加是3,放入寄存器A ,然后存入 内存地址12 是3,然后有碰到JUMP 2 。。。。叫无限循环
所以 跳转需要加上条件。
JUMP _ NEGATIVE
JUMP IF EQUAL 如果相等
JUMP IF GREATER 如果更大
修改下指令再来一遍。
image.png
执行循序:
Load A 14 是 11 存寄存器A
Load B 15 是5 存寄存器B
SUB B A B- A 11 - 5 = 6 ,结果存入寄存器A
JUMP NEGATIVE 结果是6 假,CPU 不执行跳转。
JUMP 2 没有条件直接执行
回到 SUB B A B- A 6 - 5 = 1 ,结果存入寄存器A。
JUMP NEGATIVE 5 结果是1 假,CPU 不执行跳转。
回到 SUB B A B- A 1 - 5 = -4 ,结果存入寄存器A。
JUMP NEGATIVE 5 结果是-4 真,CPU 执行 指令5
ADD B A - 4 + 5 = 1 存入寄存器 A
STORE A 13 1 存入 内存 13
HALT 停止
虽然只有7个指令但是CPU 执行了13个指令。
这个其实算余数 11 / 5 =1
如果多加几行指令,我们可以跟踪循环了多少次。
软件的好处让任意两个数可以执行,软件让我们做的硬件做不到的事情。
ALU 可以没有除法功能,是程序给了我们这个功能。
别的程序也可以用我们的除法程序,来做其他事情 -意味着一层新 抽象。
我们这里了假设的CPU 很基础,指令都是8位的,前面4个是操作码,最多也就16个指令,后4位内存地址也只有16个值。指令太少都不能jump 17。
因此,真正的现代CPU 两种策略
- 用更多位代表指令,32 64 位 - 指令长度。
- 可变指令长度 ,如 操作码是8位,看到HALT 指令,HALT 指令不需要额外数据,马上会执行。
如果看到jump ,要知道位置值,这个值在jump 的后面 叫立即值。
这样设计指令可以是任意值。
但会让读取阶段复杂一点。
我们拿来举例的CPU 和指令集都是假设的。
是为了展示核心原理。
来看看真的CPU例子。
1971 年 Intel 发布了 4004 处理器,第一次把CPU 做成一个芯片。
给后来的Intel 处理器打下基础。
支持46 个指令。
image.png
有常用的指令,也用8位的 “立即值” 来执行JUMP 标识更多的地址。
从1971 - 至今发展巨大。CPU I7有上千个指令和指令变种,长度从1 - 15个字节。
光 ADD 指令就有很多变种。
指令越多就是给cpu 设计了越来越多的的功能。