深入理解计算机系统(4.1)---X86的孪生兄弟,Y86指令体系结构

时间:2021-11-23 01:22:55

引言

  各位猿友们好,计算机系统系列很久没更新了,实在是抱歉之极。新的一年,为了给计算机系统系列添加一些新的元素,LZ将其更改为书的原名《深入理解计算机系统》。这本书非常厚,而且难度较高,LZ看了很久才看了四章。当然,这跟LZ最近很久没翻书有关系,最近公司的事情比较多,可让LZ愁了个愁,尤其是招人的事一直不太顺利,很多工作无法展开,也让LZ的心中一直压着一块大石。

  不过事情多了,就意味着责任大了,因此LZ最近经常回家自己研究公司所用的框架,以期了如指掌,可以应付各种随即事件。这耽误了LZ不少功夫,最近看书的激情在慢慢下降,这绝对不是个好事,要好好调整,毕竟艺多不压身,尽管计算机系统的这些知识在平时的用处不大,但是对自身的积累还是有很大好处的,因此建议各位猿友千万不要落下。不过耽误了这么久,说不定已经有猿友将LZ远远抛在身后了。

  好了,回归正题,来看看我们的Y86吧。

Y86指令体系结构

  Y86是一个指令体系结构(ISA),它是计算机系统这本书的作者YY出的指令集。目的是为了让我们更加清晰的了解ISA,就像你读编译原理,作者会教你做个编译器,是一样的道理。

  我们学这个并不是为了设计指令集,因为这种工作几乎不可能发生在我们的身上。还是和编译原理一样,你的工作也不太可能是去写一个编译器,创造一种语言,最多就是写个小例子拿来唬唬你身边的妹子或基友。我们的目的是为了了解CPU处理指令的流程以及它的工作原理,学习它的思想,这对你以后的技术之路说不定会有一些启发。很多时候,这种启发是很重要并且难得的,非神人不能达到。

  对于Y86,LZ也没有太多可介绍的,它就是一个ISA的例子,让各位通过它来了解ISA的设计。这一章的内容比较轻松,因为我们已经在第三章了解过X86的汇编指令,所以这里很多内容LZ只是一图代过,更多精彩内容还是要在后面再展现出来。

猿友们看的见的东西

  计算机是一个神秘的家伙,它的很多秘密我们都看不到,不过有了ISA的帮忙,我们就可以了解到很多计算机的秘密。比如LZ现在写博文的时候,CPU到底在干什么呢?

  理论上来讲,我们在编写一个程序的时候,我们是可以知道CPU的状态的。因为在你观察程序的汇编指令时,你可以知道当程序执行到某个地方,寄存器、存储器以及条件码寄存器等等的状态是如何的。说到底,无论是寄存器,存储器还是条件码寄存器等等,都是汇编指令可以访问的处理器状态。在设计和实现一个处理器的时候,只要我们能保证机器级程序(比如汇编程序)可以正常的访问程序猿可见状态(比如寄存器、存储器),那么就不太需要非得按照ISA真正的方式来表示我们的处理器状态。

  对于Y86来说,它的程序猿可见状态就是这几种:寄存器、存储器、条件码、PC、程序状态。

  在Y86当中,寄存器依旧是8个,每一个寄存器可以存储一个字,也就是一个32位二进制。条件码是一个一位二进制的寄存器,保存着最近的算术或逻辑运算所造成的影响的信息。PC则是程序计数器,记录当前正在执行的指令的地址。

  存储器则是一个很大的字节数组,Y86的程序可以使用虚拟地址(类似于数组的下标)来访问存储器,硬件和操作系统会将虚拟地址翻译为实际的地址。最后一个程序状态(stat),它则代表着程序的运行情况。

  以上便是程序猿可见的状态,或者说机器级程序可访问的CPU状态,我们在设计和实现一个处理器的时候,就是设计一系列指令去操作这些状态。

Y86指令集

  接下来我们就看看Y86的指令集,这里LZ就直接上图了,这些指令其实都是从X86的指令集YY而来。

深入理解计算机系统(4.1)---X86的孪生兄弟,Y86指令体系结构

  上面的指令相信大家都不会太陌生,LZ这里就不仔细的解释了,下面我们只简单的把每个指令的作用过一遍。

  halt:这个指令将会终止指令的执行。

  nop:这是一个占位指令,它不做任何事情,后续为了实现流水线,它有一定的作用。

  xxmovl:这是一系列的数据传送指令,其中r代表寄存器,m代表存储器,i代表立即数。比如rrmovl指令,则代表将一个寄存器的值,赋给另外一个寄存器。

  opl:操作指令,比如加法,减法等等。

  jxx:条件跳转指令,根据后面的条件进行跳转。

  cmovxx:条件传送指令,后面的xx代表的是条件。特别的是,条件传送只发生在两个寄存器之间,不会将数据传送到存储器。

  call与ret:方法的调用和返回指令。一个将返回地址入栈,并跳到目标地址。一个将返回地址入PC,并跳到返回地址。

  push与pop:入栈和出栈操作。

指令编码

  这里LZ还要说的一点是,在图的右边,是指令所占的字节数或者说编码。一般两个寄存器占用一个字节,存储器则占用四个字节,指令的编码和功能占用一个字节。因此可以看到,比如rrmovl指令,它的字节长度是2,其中第一个字节代表了指令rrmovl,第二个字节代表了两个寄存器。

  对于opl、jxx、cmovxx指令来说,都有一个fn标识,占用4个二进制位(半个字节)。这个便是指令的功能部分,这个是由于它们的指令编码一样,但功能有所不同所造成的。比如对于opl,就有加、减、与、异或等操作,那么它们的指令编码第一个字节就分别为十六进制的60、61、62、63。

  对于寄存器的表示,是使用4个二进制位表示的,这是一个ID标识。所有的寄存器可以看做是一个寄存器文件,其中的ID标识就类似于它们的地址。对于一些只需要一个寄存器的指令来说,另一个寄存器标识位使用0xF表示。

  还有的指令需要一个字的常数,比如irmovl指令,call指令等等。这种指令,将把常数放在最后的四个字节当中,顺序按照大端法或小端法表示(与机器和OS有关)。对于call指令来说,这四个字节就是一个地址,这个地址就是绝对地址,指向了存储器当中的某一个位置,这个位置存储着代码。采用绝对地址是为了描述简单,真实当中,是采取的基于PC的相对地址。

  

Y86异常

  对于Y86来说,程序猿可见的状态中就有stat状态码,它标识了程序执行的状态。Y86需要有能力根据stat去做一些处理。不过为了简单起见,这里除了正常执行之外,都将停止指令的执行。真实当中,会有专门的异常处理程序。

  Y86有四种不同的状态码,AOK(正常)、HLT(执行halt指令)、ADR(非法地址)和INS(非法指令)。

Y86程序

  书中给出了一个示例程序,来说明X86和Y86的区别,这里LZ就不详细分析这些汇编指令了,这种事情在第三章已经做的很多了,各位猿友可以私底下自己分析一下。其实两者是非常相似的,毕竟Y86就是根据X86的结构YY出来的。区别就在于,有的时候Y86需要两条指令来达到X86一条指令就可以达成的目的。

  比如对于X86指令中的 addl $4,%ecx 这样的指令,由于Y86当中的addl指令中不包含立即数,所以Y86需要先将立即数存入寄存器,即使用irmovl指令,然后再使用addl来处理加法运算。

  总的来说,Y86就是一个X86的缩减版,它的目的是以简单的结构实现一个处理器,帮助我们了解处理器的设计和实现。有兴趣的猿友可以去观摩一下Y86程序生成的汇编代码,并进行逐一的分析,实际上,这与X86是十分类似的。

文章小结

  本文的难度并不高,只是简单的介绍了一个类X86的指令集结构。接下来的内容需要我们了解一下具体的设计是如何进行的,以及如何使用硬件控制语言HCL。后面的内容相对来说会比较难理解,LZ在读的时候也是有点一知半解,尽管现在已经基本摸清了套路,但还是希望各位猿友在看的过程当中最好有自己的理解。