指令和程序
本集重点:一步步带你运行一遍程序。回顾上集的例子程序,一步步讲解。介绍“指令集”的概念。
带条件跳转,JUMP NEGATIVE 是负数才跳转,还有其他类型的JUMP。
我们把ALU,控制单元,RAM,时钟结合在一起做了个基本,但可用的“中央处理单元”,简称CPU,它是计算机的核心。
CPU之所以强大,是因为它是可编程的,如果写入不同指令,就会执行不同任务,CPU是一块硬件,可以被软件控制!
四个简单的指令
在内存中每个地址可以存8位数据,因为我们的CPU是假设的,这里前4位是“操作码”,后4位指定一个内存地址,或寄存器。

内存地址0是00101110,前4位代表LOAD_A,意思是:把后4位指定的内存地址的值,放入寄存器A,后4位是1110,十进制的14,我们来把00101110看成“LOAD_A 14”指令,这样更好理解!

我们把对内存里剩下的数也这样转换:

这里,我们的程序只有4个指令,还有数字3和14,现在一步一步看,“LOAD_A 14”是从地址14中拿到数字3,放入寄存器A;“LOAD_B 15”是从地址15中拿到数字14,放入寄存器B;“ADD B A”告诉ALU把寄存器B和寄存器A里的数字加起来,B和A的顺序很重要,因为结果会存在第二个寄存器,也就是寄存器A ,最后一条指令是“STORE_A 13”把寄存器A的值存入内存地址 13。
新的指令

“SUB”是减法指令;JUMP是跳转指令,让程序跳转到新位置,如果想改变指令顺序,或者跳过一些指令,这个很实用,举例,JUMP 0可以跳回开头,JUMP在底层的实现方式是把指令后4位代表的内存地址的值,覆盖掉“指令地址寄存器”里的值;还有一个特别版的JUMP叫JUMP_NEGATIVE,它只在ALU的“负数标志”为真时,进行JUMP,算数结果为负,“负数标志”才是真,结果不是负数时,“负数标志”为假,如果是假,JUMP_NEGATIVE就不会执行,程序照常进行。
最后,计算机还需要知道什么时候该停下来,所以有HALT指令(停止),我们之前的例子程序,其实应该是这样,才能正确工作,否则跑完STORE_A 13之后,CPU会不停运行下去,处理后面的0,因为0不是操作码,所以电脑会崩溃!还需指出的一点是,指令和数据都是存在同一个内存里的,它们在根本层面上毫无区别-都是二进制数,HALT很重要,能区分指令和数据。
JUMP指令

如上图所示,我们已经执行完前4个指令,该进行第5个指令JUMP,遇到JUMP,CPU会把“指令地址寄存器”的值,现在是4,改成2,就会执行地址2“ADD B A”,所以寄存器A变成3,然后存入地址13,又碰到JUMP 2,又回到ADD B A,寄存器A变成4,然后存入地址13,每次循环都+1,不断地循环下去,永远不会碰到HALT,总会碰到JUMP,这叫无限循环。
为了停下来,我们需要有条件的JUMP,就是JUMP_NEGATIVE

对于图中的程序来说,当最开始的寄存器A为11,寄存器B为5,在JUMP 2指令前面加上了指令JUMP_NEG 5和SUB B A,当程序执行地址2的指令时,就会进行A-B,把结果存入寄存器A,JUMP_NEG 5的执行条件是ALU的“负数标志”是真,当运算到1-5=-4时,ALU的“负数标志”是真,就是执行地址3,跳转到地址5,从而跳过执行JUMP 2,到程序遇到地址7的HALT时程序结束。实际上这些程序和指令是进行余数运算的。
指令长度
我们这里假设的CPU很基础,所有指令都是8位,操作码只占了前面4位,即便用尽4位,也只能代表16个指令,而且我们有几条指令,是用后4位指定内存地址,因为4位最多能表示16个值,所以我们只能操作16个地址,这可不多。因此,真正的现代CPU用两种策略,最直接的方法是用更多位来代表指令,比如32位或64位,这叫指令长度。
第二个策略是“可变指令长度”,举个例子,比如某个CPU用8位长度的操作码,如果看到HALT指令,HALT不需要额外数据,那么会马上执行。如果看到JUMP,它得知道位置值,这个值在JUMP的后面,这叫“立即值”。一般来说 指令=(操作码+操作值地址) 当指令=(操作码+操作值)时,这个操作值就是立即值,比如JUMP ADD SUB LOAD,它用8位的“立即值”来执行JUMP,以表示更多内存地址。




