CPU是如何执行程序的? 1、我们先来看下两位全加器 A = 01B = 1H B = 11B = 3H A + B = 1 + 3 = 4 = 100B
为了方便更多比特位的运算,我们使用内部集成8个全加器芯片74283来运算 DATA1 = 0000 0101B = 5H DATA2 = 0000 1010B = 10H SUM = 0000 1111B = 15H
2、那么数字电路是如何进行加减乘除运算的? 注意:我们这里不考虑一些特殊的情况,我们8位数最大能表示的数为255,所以只考虑数值小于255的情况,更深入的探讨大家可以去寻找更专业的资源学习。 加法运算: 假设我们要进行8+13运算: 十进制运算:8+13= 21; 二进制运算:0000 1000 + 0000 1101 = 00010101; 我们通过8位全加器来验证下
减法运算: 加法是进位,减法需要考虑的则是借位,小学时对加减法的经验是这样的,但是计算机不是这么处理的。计算机只有加法,没有减法。那么 int a = b - c 是怎么得出来结果的呢?首先要了解一个概念——补码。 计算机中对于有符号数,用最高位作为符号位,“0” 代表 “+” ,“1” 代表 “负号-” ;其余数位用作数值位,代表数值。比如 Byte 类型的取值范围为 -128 ~ 127。其中,表示数值的只有 7 位,首位表示正负。 补码规定,正数和 0 的补码就是其原码(原码、反码的定义这里就不多赘述),负数的补码是其正数的原码取反再加 1 。 举个例子,求负 -10 的补码:十进制 10 的原码(按 8 位举例)为 0000 1010,其反码为 1111 0101,取反后再加 1 即为其补码1111 0110。因此,负10 的补码为 1111 0110。 不知道写到这里,大家有没有发现什么端倪?我们再回到减法计算来,a = b - c 实际上等同于a = b + ( -c )。 情形1,减数>被减数 - 12 - 5= 0000 1100 + 1111 1011= (1)0000 0111= 7 括号里为进位,因为只有 8 位,所以高于 8 位的进位要去掉。
- (-5 )二进制: 000 0101;
- 反码:1111 1010;
- 补码 = 反码+1:1111 1011
- 最终通过加入反相电路和加1电路得到的最终结果,进位去掉后为0000 0111换算成10近制就是7
Proteus仿真文件:
情形2,减数>被减数 - 再来计算下7 - 9= 0000 0111 + 1111 0111= 1111 1110= -2
- (-9)二进制:0000 1001,
- 反码:1111 0110,
- 补码:1111 0111
- 然后得到的是负数,我们又要把它反回来
- 补码-1:1111 1110-1 = 1111 1101
- 反码反过来:0000 0010
- 0000 0111 + 1111 1101
通过这两个例子是不是就清楚了计算机是如何计算减法的了? 乘法运算: 通过说减法,我们是不是对乘法也有一定的启发了呢?乘法其实就是循环的加法。比如 5 * 3 实际上就是 5 + 5 + 5。貌似就说完了。实际上不仅仅如此的。现在有一个电子器件叫做乘法器,其可以实现二进制的乘法、除法等运算。我们同样以 5 * 3 做为例子,讲解一下乘法器计算乘法的流程。 - 5 * 3 = 0000 0101 * 0000 0011
- 第一步:5 + 5 = 10
- 00000 0101 + 0000 0101 = 0000 1010
- 第二步:10 + 5 = 15
- 0000 0101 + 0000 1010 = 0000 1111
小提示:当然也可以通过移位电路来运输也是可以的,这里就不举例子了 虽然CPU中有乘法器,但是我们发现实际的最终操作流程还是加法和位移操作计算的乘法运算。我们写的代码中的乘法到底是用乘法器运算还是转化成加法运算,我们也并不太确定,有些编译器编译的时候会对代码进行优化,选取最优的一种算法来计算结果。 除法运算:除法可以通过减法来实现,比如 10 / 3 等价于 10 一直减 3 直到被减数小于 3 ,减了 3 次,那么 10 / 3 的结果就为 3 了,余数为减完剩下的值 1 。 其实上面已经提到了乘法器,除法的原理同样也类似(这里不说浮点数的除法,只说整数的除法),但是稍微复杂一点。 同样我们举个例子来说明一下。 - 10 / 3 = 000 1010 / 0000 0011
- 第一步:10 - 3 = 7
- 000 1010 - 0000 0011 = 0000 0111
- 第二步:7 - 3 = 4
- 0000 0111 - 0000 0011 = 0000 0100
- 第三步:4 - 3 = 1,通过比较大小,不能再继续减下去。
- 0000 0100 - 0000 0011 = 0000 0001
小提示:这里只是展示其中一种除法运算,大家可以去思考其他方式的除法运算,比如说使用移位方式。 然后我们再来看一个完整的CPU计算电路 这里面包含有完整的8位与运算电路,或运算电路,右移运算电路,左移运算电路,还有加法运算电路等,我们实际运算的时候就通过取到的指令通过内部解码来决定当前操作是进行加法操作,还是移位操作,又或是对外部I/O进行锁存操作。 然后这里面我们发现复杂的运算会产生中间数据,比如说乘法,除法,要计算好几次才能得到结果,所以我们就需要寄存器来临时存储这些数据,这样我们就可以计算了. CPU执行整个程序流程分解
我们以这个代码为例来详细讲解下。 #include <reg52.h>sbit LED = P0^0;void delay( unsigned int nTime){ unsigned char i; while(nTime--) for(i=10; i>0; i--);}void main(){ while(1) { LED = 0; delay(10); LED = 1; delay(10); }}
我们再来看下汇编指令,我们的程序从main函数开始执行 C:0x0000 02002D LJMP C:002D5: void delay( unsigned int nTime)6: {7: unsigned char i;8: while(nTime--)C:0x0003 EF MOV A,R7C:0x0004 1F DEC R7C:0x0005 AA06 MOV R2,0x06C:0x0007 7001 JNZ C:000AC:0x0009 1E DEC R6C:0x000A 4A ORL A,R2C:0x000B 600B JZ C:00189: for(i=10; i>0; i--);C:0x000D 7D0A MOV R5,#0x0AC:0x000F ED MOV A,R5C:0x0010 D3 SETB CC:0x0011 9400 SUBB A,#0x00C:0x0013 40EE JC delay(C:0003)C:0x0015 1D DEC R5C:0x0016 80F7 SJMP C:000F10: }11:C:0x0018 22 RET12: void main()13: {14: while(1)15: {16: LED = 0;C:0x0019 C280 CLR LED(0x80.0)17: delay(10);C:0x001B 7F0A MOV R7,#0x0AC:0x001D 7E00 MOV R6,#0x00C:0x001F 120003 LCALL delay(C:0003)18: LED = 1;C:0x0022 D280 SETB LED(0x80.0)19: delay(10);C:0x0024 7F0A MOV R7,#0x0AC:0x0026 7E00 MOV R6,#0x00C:0x0028 120003 LCALL delay(C:0003)20: }C:0x002B 80EC SJMP main(C:0019)C:0x002D 787F MOV R0,#0x7FC:0x002F E4 CLR AC:0x0030 F6 MOV @R0,AC:0x0031 D8FD DJNZ R0,C:0030C:0x0033 758107 MOV SP(0x81),#0x07C:0x0036 020019 LJMP main(C:0019)
小提示:我们的单片机默认都是跳转到main函数处执行
第一步:取指令
C:0x0019 C280 PC计数器指向读取指令的地址,然后存储到指令寄存,通过指令解码器解码后输送给运算单元,执行相应操作. 示例:我们的单片机程序一般都是直接跳转到main函数,这时PC = 0x0019地址里面的指令是C280 第二步:解码 CLR LED(0x80.0) 指令寄存器中的指令通过译码电路进行译码,然后CPU根据译码的结果来执行加减乘除,移位等操作,示例:通过译码器电路我们得知C280代表的意思是C2表示清除位指令 第三步:执行 在执行阶段会根据指令的类型,将算数/逻辑单元(ALU)用于不同的目的。对其他指令,它会作为一个加法器来计算增加或减少栈指针,或者计算有效地址,或者只是简单地加0,将一个输入传递到输出 示例:这里相当于是清除0x80的第0位,也就是将P0.0置低,然后PC更新PC的值,没有对PC值进行操作的话,它就自动累加这里自动累加后是0x001B,然后继续第一步循环操作就完成了我们整个程序的一个执行。 这里只是给出大致的程序执行方式,省略了很多细节操作,大家可以进一步参考更详细的程序执行过程。 晶振在单片机中是起什么作用? 上面的运算,我们都是通过手动的方式来进行的, 我们知道数字电路本身是不工作的,它也不能工作,否则电路就会出乱子,它必须依赖其它电路产生的高低电平才能驱动它工作,我们把这种有规律的高低电平定为脉冲,单片机也是数字电路,它又是怎么工作的?我们必须要有一个能源源不断产生脉冲的电路,它才能持续不断的工作下去,这也就是晶振在单片机电路中为什么被称作为“心脏”的原因,我们把这个“心脏”去掉,相应的整个单片机系统也会**。 为什么有些单片机没有晶振?我们知道单片机工作只要有持续不断的脉冲输入就行,我们有些场合对单片机没有精确的时间要求,所以一般使用内部自带的振荡器为单片机提的时钟脉冲也能确保单片机正常工作,所以在有些地方我们就看不到晶振。那是因为内部自带了RC振荡电路。 复位:CPU执行程序需要有一个初始状态,通过复位可以进入到一个初始状态。这样单片机知道自己的起始位置,它才能按照既定的方式运行程序。
|