计算机CPU工作原理及汇编语言简介

     原文发表于我的微信公众号"涛歌依旧",可以点击如下红色文字链接阅读:

   计算机CPU工作原理及汇编语言简介(链接)
 

     从微信公众号把原文直接复制过来,发现图片、表格经常丢失,只有纯文字,所以建议直接点击上述链接阅读。

     复制过来的纯文字内容如下(图片、表格丢失,格式错乱):
 


      在之前的文章中, 基于“冯诺依曼结构”,我们制作了一台简易计算机,如下图所示:

      其中有CPU的影子:
       a. U3加法器对应CPU运算器。
      b. U1计数器、U4触发器、时钟信号、U5非门,这四个部件对应CPU控制器。
      深入理解这个简易计算机的工作流程,有助于理解CPU的工作原理(本文会讲述)。

      我们可以继续用proteus来画更复杂的电路图,并自定义CPU指令集,实现更强大的功能。
      然而,无论我们怎么设计电路来制作CPU,它永远只是冰山一角的一个小小冰点, 离Intel或AMD的商用CPU有太远太远的距离。我们不可能也没必要画出那么复杂的CPU(几十亿个晶体管器件)。
      因此,我们需要从底层复杂的电路图设计中解脱出来,站在更高的层次和更高的抽象度上来看待CPU.

      CPU是Central Processing Unit的缩写, 翻译过来就是中央处理器,看这名字,就知道它是很重要的东西。本文在叙述时,有的地方用CPU,有的地方用微处理,它们都是一个意思。


       本文主要分为三个部分:

CPU发展历史

CPU工作原理及8086CPU简介

汇编语言简介

一. CPU发展历史
      1947年,那是一个冬天,有三位牛人,在贝尔实验室发明了晶体管,神话般的传说......
      1948年,他们申请了专利,并于1956年获得了诺贝尔物理学奖。肖克利和仙童“八叛逆”的事情,我们之前已经讲过,故不再展开介绍。
      1968年,“八叛逆”中的罗伯特诺依斯和戈登摩尔,以及安迪格鲁夫共同创立了鼎鼎大名的Intel公司。大家应该对这三个人略有耳闻:罗伯特诺依斯曾陪乔布斯解惑伤心往事;戈登摩尔发明了沿用至今的摩尔定律;安迪格鲁夫写过一本书叫《只有偏执狂才能生存》。
      1971年,又是一个冬天,Intel工程师特德霍夫,发明了世界上第一款商用微处理器4004,它是一个通用的微处理器,内部集成了2250个晶体管,有45条指令,每秒运算6万次。特德霍夫被英国《经济学家》杂志称为“二战以来最有影响力的科学家之一”。Intel CEO戈登摩尔将4004称为"人类历史上最具革新性的产品之一"。
       4004微处理器外观图如下:


       后来,Intel又研发了经典的8086微处理器,本文会对8086微处理器进行具体介绍,故暂且不表。

       事实上,在那个年代, 有很多公司都参与了微处理器的研发,Intel只是其中的典型代表之一,我们不对所有的微处理器进行介绍,仅来看一下Intel微处理器的发展历程,如下表所示:

       我此刻写公众号文章,正在使用的电脑的CPU芯片,就是Intel公司的Pentium(奔腾)型号:

二. CPU工作原理及8086CPU简介

       我们来审视一下自制的简易计算机,其根本部件就是CPU和存储器,我们来看看CPU如何与内存进行交互:

        a. 地址线:指定数据在内存中所在的地址

        b. 数据线:指定数据的值具体是多少

        c. 控制线:指定控制信号(控制信号的种类非常多),比如指定究竟是从内存中读出数据,还是向内存中写入数据。

       来看一下CPU从内存中读取数据的过程。例如:从地址为3的内存单元中读出数据,读出的数据是8,如下图所示:

       再看CPU向内存中写入数据,也是类似的逻辑。例如,把数据8写入到地址为3的内存单元中,如下图所示:

        来看CPU如何传递地址信息。下图中CPU有10根地址线,因此可以对应1024个内存单元(每个内存单元是1B),所以,我们可以说这个CPU的寻址能力是1KB.

       来看一下CPU如何传递数据信息。对于8088CPU而言,有8根数据线。所以,传送16位二进制数据时,需要操作两次,如下图所示:

      

      而8086CPU中有16跟数据线,所以,传送16位二进制数据,只需要一次即可,如下图所示:

      下图是CPU和各类存储器的连接逻辑图:

      从CPU的角度看,CPU将系统各类存储器看作是一个逻辑存储器,如下图所示:

       以8086CPU为例,它的地址线有20根,故8086CPU的寻址能力是1MB, 其对应的地址空间就是00000H~FFFFFH, 总共能对应2^20个内存单元,如下图所示:

       我们来具体看看CPU是如何与内存交互的,如下图所示:

        CPU的主要工作流程如下:

        a. 读取指令:控制器的PC中,存储了待执行指令的地址,并根据这个地址,从内存的代码段读出指令内容,存放到IR中。

       b. 指令译码:控制器中译码电路,对IR中的指令内容进行译码,确定究竟该执行什么操作。注意,在指令译码后,还需要更新PC, 使得PC指向下一条指令,便于让CPU不知疲倦周而复始地工作。

        c.  执行指令:根据指令译码的结果,可能会去内存的数据段读取数据,传到寄存器中,进而传递到运算器,进行运算。运算之后,可能需要把数据结果写回到内存的数据段。

       CPU三“步”曲如下:

       海可枯,石可烂,天可崩,地可裂,但这种循环永不改变。CPU完全按照人写的程序指令,来执行任务,它本身没有任何智能和思维。

       而且,我们应该注意到:在“冯诺依曼结构”中,指令和数据都存放在内存中,具有同等的地位。对于内存而言,里面存放的无非就是二进制数字,至于这些数字表示的是指令还是数据,内存自身并不能区分。这些数字的含义,取决于读取者CPU怎么理解。比如,CPU将程序计数器PC指向的内容理解为指令,而在其它场景,可能就理解为数据。

       所以,在数据线上传输的内容,可能是数据(程序数据),也可能是指令(程序指令)。它们都是广义上的数据。

       思考一下这个问题:如果强制让PC指向内存的数据段,会怎样呢?显然,CPU仍然把数据当做指令来处理,当然这个指令也有可能超出CPU指令集的范围,无法正常执行。


      我们已经介绍了通用CPU的工作原理,若要更深入理解并操作CPU, 还是得针对具体型号的CPU. 以经典的8086CPU为例,  其外观图如下:

       8086CPU外部引脚图如下:

       8086CPU内部结构图如下:


        简单总结一下8086CPU:

       a. 8086CPU包括控制器和运算器,还有14个寄存器。部分寄存器起到了控制的作用,所以,可以归纳为属于控制器。这14个寄存器都是16位的,它们是:

       b. 8086CPU有20根地址线,因此,可以对应2^20个存储单元(1个存储单元是1B), 所以,8086的寻址能力是1MB.  我们知道,8086CPU的寄存器是16位的,那如何给20根地址线提供数据的呢?很显然,可以考虑用两个寄存器(比如CS和IP)。在叙述通用CPU时,一般用PC来表示程序计数器,8086CPU是一种具体的CPU, 其实现PC的方式是CS:IP, 即有PC = CS * 16 + IP, 如下所示:

        c. 8086CPU有16根数据线,一次就可以传送16位二进制数据,如下:

       d. 字长是CPU一次能处理的数据的位数,通常与寄存器位数有关。前面已经说过,8086CPU的寄存器都是16位的,所以,8086CPU的字长就是16位。来看看AX如何存储16位二进制数据:

      我们大致熟悉了8086CPU的结构,现在问题是,怎么操作和使用它呢?钻到里面去用手掰弄开关吗?显然不行,因为你我根本钻不进去。
      还是得请机器语言0和1出场了,机器语言完全由一系列的0和1组成,非常不好懂,而且容易出错,也很难排查错误,烦人得很,看一眼就头晕:


       如果对机器语言还心存幻想, 不妨看看下面这段机器语言程序,它的功能是在屏幕上输出"welcome to masm":


       其中0和1不能有一丁点错误。一个正常的人,是没法忍受机器语言程序的。而且,如今这个年代,已经几乎没有人用机器语言来编写程序了。机器语言繁琐,需要用汇编语言来简化编程。

      在之前的文章中,我们说过,人写了汇编语言后,需要经过汇编器工具的转换,自动生成天书般的机器语言,过程如下:


      所以,我们还是用汇编语言来辅助描述CPU的执行流程吧。
 
       在了解8086CPU是如何执行指令前,我来提一个问题:CPU的指令集存放在哪里?
      有人说,CPU指令集存放在CPU中,有人说,存放在内存中。这有点搞笑了
      CPU指令集存放在CPU的使用手册中。所谓的CPU指令集,就是操作CPU的方法,这些操作方法,当然是存在于CPU使用手册中。而这些操作方法,则具体是由CPU的电路来实现的。

      我们来看8086CPU是如何执行程序指令的,以如下汇编指令为例:
mov ax, 0123h
mov bx, 0003h
mov ax, bx
add ax, bx
       这些汇编指令,与机制语言指令是对应的,它们以二进制的方式存储在内存中。为了方便阅读,我们以十六进制的方式进行展示,如下图所示:


       下面,来看看8086CPU执行这段指令的过程。
       初始状态如下(CS = 2000H, 图中标注的是十六进制):


       执行地址加法(注意PC = CS * 16 + IP, 这里的16就是10H):


       于是,PC =20000H,如下:


        用地址总线来传递PC的值:


      从内存中读出数据(指令),对应的机器语言指令的十六进制是B8 23 01,其汇编指令是mov ax, 0123H , 如下:


        把这条指令存储到指令缓冲器(即我们前面提到的指令寄存器IR),  然后,译码电路完成译码功能。指令缓冲器中的指令内容如下:


      然后IP会变化,PC也就随之变化了,指向下一条指令,如下:


       现在开始执行IR中的指令(mov ax, 0123H),如下:


       执行后的结果是:

       到此为止, CPU已经完整地执行了第一条指令:mov ax, 0123H,  可以看到,CPU似乎又回到了初始状态,不同之处是,现在IP = 0003H, 也就是说,PC指向了下一条指令。

      接下来的CPU执行流程,就很俗套了,无非就是重复再重复而已。我们不详细介绍每个细节,直接给出重要步骤的结果:


      终于,我们得到了结果,如此简单而清晰:


      通过上面的分析,我们很容易理解8086CPU的具体工作过程。可以看到:
      CPU周而复始地执行人设定的指令(程序),CPU并不智能,也不具备思考能力,它就是个干蛮力、干苦力的家伙,一根筋干到底。这种枯燥无味的繁重工作,就应该让CPU去做,而且,它还几乎不会犯错。可以说,CPU解放了生产力,解放了全人类。

三. 汇编语言简介
      我们已经讲述了8086CPU执行指令的具体过程, 但仍然会有这样一个疑问:

     在各种高级语言琳琅满目的21世纪,很少有人用汇编语言进行编程,汇编语言开发效率低,而且还不可移植。那么,我们为什么还需要了解和学习汇编语言呢?

      其实,我们学习汇编语言,不是为了用汇编语言去编写大型的程序和软件,而是因为:

      a. 汇编语言是直接操作计算机硬件的语言,通过汇编语言编程,可以获取底层编程的感觉。直接指挥控制计算机,这种感觉很奇妙。通过汇编语言,可以深入理解计算机的工作原理,对后续学习计算机的其它课程(比如操作系统)很有好处。熟悉汇编语言,也能提升自信,偶尔还可以在闲谈聊天时吹水。
      b. 我们在用高级语言编写软件程序时,经常需要调试,有时候需要跟踪到汇编语言这一层,才能明白问题之所在,汇编语言是最后的一根救命稻草。
      c. 在进行性能优化时,需要用到汇编语言。对于多数程序员而言,是不需要用汇编语言进行性能优化的。但是,当我们看操作系统或优秀开源组件的源码时,会经常看到汇编语言代码,如果我们熟悉汇编语言,至少能读懂,那么也就不害怕了。

      在学习汇编语言之前,我们先来看一下与汇编语言有关的几个基本问题:
      a. 同一CPU的汇编语言和机器语言是一一对应的吗?
      由于汇编规范可以有多种(比如Intel汇编规范和AT&T汇编规范),所以,机器语言可能有不同的汇编语言形式。可以这么说,当汇编规范确定后,机器语言和汇编语言是一一对应的。
      b. 汇编语言是CPU厂商规定的吗?
      CPU厂商只负责设计和制作CPU的电路硬件部分,并提供手册告诉使用方如何来操作CPU. 这个手册中,必须提供操作CPU的机器语言, 一般也会提供其对应的简记方式(汇编助记符), 编译器厂商拿到这个汇编助记符手册,定义汇编语言规范,开发对应的汇编器,把汇编语言程序转化为CPU厂商要求的机器语言程序。所以,汇编语言是编译器厂商根据CPU厂商的指令集说明书来规定的。需要补充说明一点,CPU厂商通常也会提供SDK,方便使用CPU.
      c. 汇编语言为什么不具有可移植性?
      我们自制简易计算机的CPU和8086CPU的电路结构不一样, 所以,操作方式不同(指令集不同),机器语言也不同,汇编语言也不同。因此,汇编语言不具备移植性。你辛辛苦苦地写好汇编语言程序后,只能在某款CPU上运行,没法在其它类型指令集的CPU上运行,挺尴尬的,这也是汇编语言的局限性之一。

      学习汇编语言,一定要动手实际操作。可以采用masm(Windows平台)或者nasm(Linux平台)作为汇编器工具。如果是在Windows平台上,可以在dos中进行debug,嗯,就是那个命令行黑框框:


       这些命令行式的东西,不直观,不好用。直到遇到Windows平台上的emu8086界面工具, 才知道什么叫相见恨晚,如下:


       工欲善其事,必先利其器。建议在学习汇编语言时,使用emu8086这种界面工具,它是所见即所得的。工具的安装和使用很简单,基本都是傻瓜式的操作,故无需介绍。

       现在开始用emu8086工具来介绍汇编语言。
      之前用自制简易计算机实现了连续加法(1+2+3+4+5),那该如何针对8086CPU编写汇编语言,来实现这个连续加法呢?如下:
assume cs:code

code segment
  mov ax, 0  
  add ax, 1
  add ax, 2
  add ax, 3
  add ax, 4
  add ax, 5
  
  mov ax, 4c00h
  int 21h    
code ends

end
      我们来看下emu8086的初始状态,盯住CS和IP, 它是程序执行的起点,如下图:

      可以看到, CS = 0100H, IP = 0000H, 所以,即将执行的汇编指令是:
mov ax, 0

       我们利用emu8086工具来做单步调试,能看到CPU内部每一步操作的详细信息,当add ax, 5 执行完毕后,可以看到,ax的值是000FH, 也就是十进制的15,如下所示:

      这个程序中的语句,比较简单,所以,我们不做详细解释。

      1加到5很简单,如果要计算1加到100呢?难道要把add ax重复写100遍吗?显然不是,直接用循环指令吧,汇编语言程序如下:
assume cs:code

code segment
  mov ax, 0  
  mov bx, 0   
  mov cx, 100
  
  label:  
  inc bx
  add ax, bx
  loop label
  
  mov ax, 4c00h
  int 21h    
code ends

end
      执行完毕后,ax = 13BAH, 也就是十进制的5050, 和高斯小朋友当年算出的结果完全一致,如下:

      这个程序中的每条语句,也比较简单,毕竟都是语法层面的玩意儿,所以,就不一一解读了。


      至此,我们基本了解了汇编语言,至于汇编语言进阶的学习,那也不难,有emu8086在手,还担心什么呢?建议有兴趣的朋友,多多演练和调试。

       我们来总结一下本文内容:
       首先,我们简要介绍了CPU的发展历史。
       然后,我们介绍了CPU的工作原理,并对8086CPU执行程序指令做了详细讲解。
       最后,我们讲了汇编语言的重要性和必要性,并用简单的示例,进行汇编语言入门级的演练。

      还是那句话,我们了解和学习汇编语言,绝不是为了将来用汇编语言去编写软件程序,而是为了深入理解计算机的工作原理。
      从电路操作开始,我们的关注点在电路层面,当电路复杂后,感觉控制电路已经力不从心了。
      于是,我们开始用机器语言来控制电路, 一串串的0和1组成的机器语言,可以控制电路,但天书般的机器语言,已经让我们眼花缭乱。
      于是,我们开始用汇编语言来控制电路, 我们需要操作CPU的每个部件,告诉CPU电路如何去执行指令, 这种过程,依然是心力交瘁的感觉。看下图来感受一下对比,那么多杂乱的机器语言和汇编语言, 被高级语言(C语言)一行简单代码就搞定了:


  
      从直接掰弄开关来操作电路,到机器语言,到汇编语言,再到高级语言(比如C语言),这是一个不断远离具体细节、不断进行抽象、不断接近事物本质的过程。在这种不断的抽象过程中,编写程序的人,从繁琐中得以解脱,编写程序的效率得到了提升。那些繁琐的步骤,还是交给编译器工具和汇编器工具吧。


      我们已经详细介绍了8086CPU, 然而,它只是微处理器,不包含RAM和ROM, 因此,8086CPU不是一台完整的计算机。在后续文章中,我们会介绍一台完整的计算机(8051单片机),并用高级语言(C语言)来编写程序,达到控制单片机的目的。

       单片机和C语言,不见不散

       

发布了2213 篇原创文章 · 获赞 4564 · 访问量 1977万+

猜你喜欢

转载自blog.csdn.net/stpeace/article/details/103706218