即使是前面例子中展示的极其简单的微处理器,也拥有一套相当大的可执行指令集。这些指令集合以位模式的形式实现,每个位模式在加载到指令寄存器时都有不同的含义。人类不擅长记忆位模式,因此定义了一组短词来表示不同的位模式。这组词被称为处理器的**汇编语言**。**汇编器**可以非常容易地将这些词翻译成它们的位模式,然后汇编器的输出被放置在内存中供微处理器执行。
以下是设计者可能为我们示例中的简单微处理器创建的汇编语言指令集:
- **LOADA mem** - 从内存地址加载到寄存器A
- **LOADB mem** - 从内存地址加载到寄存器B
- **CONB con** - 将常数值加载到寄存器B
- **SAVEB mem** - 将寄存器B保存到内存地址
- **SAVEC mem** - 将寄存器C保存到内存地址
- **ADD** - 将A和B相加,结果存储在C中
- **SUB** - 将A和B相减,结果存储在C中
- **MUL** - 将A和B相乘,结果存储在C中
- **DIV** - 将A和B相除,结果存储在C中
- **COM** - 比较A和B,结果存储在测试寄存器中
- **JUMP addr** - 跳转到某个地址
- **JEQ addr** - 如果相等,则跳转到某个地址
- **JNEQ** addr - 如果不相等,则跳转到某个地址
- **JG addr** - 如果大于,则跳转到某个地址
- **JGE addr** - 如果大于或等于,则跳转到某个地址
- **JL addr** - 如果小于,则跳转到某个地址
- **JLE addr** - 如果小于或等于,则跳转到某个地址
- **STOP** - 停止执行
如果您阅读过《C语言编程如何工作》,那么您就知道这段简单的C代码将计算5的阶乘(其中5的阶乘 = 5! = 5 * 4 * 3 * 2 * 1 = 120):
a=1;f=1;while (a <= 5){ f = f * a; a = a + 1;}
程序执行结束时,变量**f**包含5的阶乘。
汇编语言
**C编译器**将这段C代码翻译成汇编语言。假设在此处理器中RAM从地址128开始,ROM(包含汇编语言程序)从地址0开始,那么对于我们简单的微处理器,汇编语言可能看起来像这样:
// 假设a在地址128// 假设F在地址1290 CONB 1 // a=1;1 SAVEB 1282 CONB 1 // f=1;3 SAVEB 1294 LOADA 128 // 如果a > 5 则跳转到175 CONB 56 COM7 JG 178 LOADA 129 // f=f*a;9 LOADB 12810 MUL11 SAVEC 12912 LOADA 128 // a=a+1;13 CONB 114 ADD15 SAVEC 12816 JUMP 4 // 循环回if17 STOP
ROM
那么现在的问题是,“所有这些指令在ROM中是什么样子?”这些汇编语言指令中的每一个都必须由一个二进制数表示。为了简单起见,我们假设每条汇编语言指令都被赋予一个唯一的数字,如下所示:
- LOADA - 1
- LOADB - 2
- CONB - 3
- SAVEB - 4
- SAVEC mem - 5
- ADD - 6
- SUB - 7
- MUL - 8
- DIV - 9
- COM - 10
- JUMP addr - 11
- JEQ addr - 12
- JNEQ addr - 13
- JG addr - 14
- JGE addr - 15
- JL addr - 16
- JLE addr - 17
- STOP - 18
这些数字被称为**操作码**。在ROM中,我们的小程序将看起来像这样:
// 假设a在地址128// 假设F在地址129Addr opcode/value0 3 // CONB 11 12 4 // SAVEB 1283 1284 3 // CONB 15 16 4 // SAVEB 1297 1298 1 // LOADA 1289 12810 3 // CONB 511 512 10 // COM13 14 // JG 1714 3115 1 // LOADA 12916 12917 2 // LOADB 12818 12819 8 // MUL20 5 // SAVEC 12921 12922 1 // LOADA 12823 12824 3 // CONB 125 126 6 // ADD27 5 // SAVEC 12828 12829 11 // JUMP 430 831 18 // STOP
您可以看到,七行C代码变成了18行汇编语言,而这又变成了ROM中的32字节。
解码
指令解码器需要将每个操作码转换成一组信号,这些信号驱动微处理器内部的不同组件。我们以ADD指令为例,看看它需要做什么:
- 在第一个时钟周期内,我们需要加载指令。因此,指令解码器需要:
- 激活程序计数器的三态缓冲器
- 激活RD线
- 激活数据输入三态缓冲器
- 将指令锁存到指令寄存器中
- 在第二个时钟周期内,ADD指令被解码。它需要做的很少:
- 将ALU的操作设置为加法
- 将ALU的输出锁存到C寄存器中
- 在第三个时钟周期内,程序计数器递增(理论上这可以与第二个时钟周期重叠)。
每条指令都可以分解为一系列按顺序执行的操作,这些操作以正确的顺序操纵微处理器的组件。有些指令,如这条ADD指令,可能需要两到三个时钟周期。其他的可能需要五到六个时钟周期。