教育改变生活

标题: CPU 是怎么认识代码的?(六) [打印本页]

作者: 李玮    时间: 2020-4-7 15:54
标题: CPU 是怎么认识代码的?(六)
只不过这个问题还可以从栈和硬件的关系进行拓展,这里就再多说几句。
其实栈是一种数据结构,跟CPU无关。只不过栈这个数据结构实在太常用了,以至于CPU会针对性的进行优化。为了能让我们的CPU也能用栈,我们给它增加几个组件。
第一,增加一组寄存器。现在有两组寄存器了,我们分别成为A和B。
第二,增加两个指令,RDA/RDB和WRA/WRB,分别为把指定内存地址的数据读到寄存器A/B,和把寄存器A/B的内容写到指定地址。
顺便再说下内存,内存有个地址总线,有个数据总线。比如你要把1100这个数字存到0011这个地址,就把1100接到数据总线,0011接到地址总线,都准备好了啪嚓一按开关(对,就是我们前面提到的那个开关),就算是存进去了。
什么叫DDR内存呢,就是你按这个开关的时候存进去一个数字,抬起来之前你把地址和数据都更新一下,然后一松手,啪!又进去一个。也就是正常的内存你按一下进去1个数据,现在你按一下进去俩数据,这就叫双倍速率(Double Data Rate,简称DDR)
加了这几个命令之后我们发现按原来的设计,CPU每个指令针脚控制一个模块的方式的话针脚不够用了。所以我们就需要加一个解码器了(decoder)。
于是我们选择用第二个位作为是否选择寄存器的针脚。如果为0,则第三第四位可以正常激活位移器和加法器;如果为1则只激活寄存器而不激活位移和加法器,然后用第四位来决定是寄存器A还是B
这样变成了
0100,数据读入寄存器A
0101,数据读入寄存器B (我们把汇编指令定义为MOVB)
0001,数据与寄存器A相加,结果保存到寄存器A
0011,数据与寄存器B相加,结果保存到寄存器B(我们把汇编指令定义为ADDB)
0010,寄存器A数据向左位移一位(乘2)
最后我们可以用第一位来控制是不是进行内存操作。如果第一位为1则也不激活位移和加法器模块,然后用第三个针脚来控制是读还是写。这样就有了
1100,把寄存器B的地址数据读入寄存器A(我们把汇编指令定义为RD)
1110,寄存器A的数据写到寄存器B指定的地址(我们把汇编指令定义为WR)
我们加了个解码器之后,加法器的激活条件从
p4
变成了
(NOT (p1 OR p2)) AND p4
加法器的输入则由第三个针脚判断,0则为寄存器A,1为寄存器B
这就是简单的指令解码啦。
当然我们也可以选择不向下兼容,另外设计一套指令。不过放到现实世界恐怕就要出大乱子了,所以你也可以想象我们平常用的x86背了个多大的历史包袱。

这个时候我们用栈的话,先栈地址初始化
0101 1000 ; MOVB 16; 把栈底地址定义为1000
之后入栈的话,比如把数字3,4入栈
1111 0011 ; WR   03; 把3写到内存,地址为10000011 0001 ; ADDB 01; 栈地址+1 1111 0100 ; WR   04; 把3写到内存,地址为10010011 0001 ; ADDB 01; 栈地址+1
这样就把3,4都保存到栈里了。
出栈的话反过来
0011 1111 ; ADDB -1; 栈地址-11101 0000 ; RD   00; 把内容读入寄存器A,00是占位0011 1111 ; ADDB -1; 栈地址-11101 0000 ; RD   00; 把内容读入寄存器A,00是占位
这样就依次得到4,3两个值。
所以,入栈出栈其实就是把数据写道指定的内存位置,CPU其实不知道你是在干啥。
当然我们也可以让CPU知道。
接下来我们再改进一下,给CPU再加一个寄存器SP,并定义两个指令:一个PUSH,一个POP。动作分别是把数据写入SP的地址,然后SP=SP+1,POP的话反过来。
这样有什么好处呢?好处在于PUSH/POP这样的指令消耗特别少,速度特别快。而栈这种数据结构在各种程序里用的又特别频繁,设计成专用的指令则可以很大程度上提升效率。
当然前提是编译器知道这个指令,并且做了优化,所以同样的程序(c语言写的),编译参数不一样(打开/关闭某些特性),编译出来的东西也就不一样,在不同硬件上的运行的效率也就会不一样。
比如上古时代的mmx,今天的SSE4.2,AVX-512,给力不给力?特别给力,但你平常用的程序支不支持是另一码事,要支持怎么办?重新编译呗。
这个时候开源的优势就显示出来了,重新编译很方便。闭源的话你就要指望作者开恩啦。








欢迎光临 教育改变生活 (http://bbs.goldoar.com/) Powered by Discuz! X3.2