一点一点学汇编

我是一个三流学校的本科生,软件工程专业,马上就要毕业了。回想起来,大学四年在专业方面收获颇为有限,也就是跟着学校的安排做做实验,庸庸碌碌,没做过什么像样的东西出来。

  

去年,和几个同学参加了王爽老师发起的“三个一工程”活动。从汇编开始,学到c语言的基础。感觉很有帮助。但是后来因为各种原因,退出了那个活动。上个学期干脆就没debug过任何程序,程序感已经丧失全无。

  

现在,计划把当时学习汇编,c语言时候的学习心得,方法,思路重新回顾一下,写到这里,和大家分享。先从汇编开始,我用的是王爽老师的《汇编语言第二版》,也是按照书的结构来学习。

  

--------------------------------------------------------------------------------------------------------

 

基础知识

 

我们知道,计算机语言,大概上可以分为三类:机器语言,汇编语言,高级语言。

 

机器语言也就是我们平时常说的0,1代码。不同的0,1代码,对应着不同的机器指令。计算机通过解析这样0,1代码,产生了不同的高低电平,从而驱动计算机中的部件进行相应的运算。对于不同的CPU,由于其内部硬件等设计的不同,就有不同的指令集,从而对应的机器指令也不相同。最早期的程序,都使用机器语言设计,将程序写在纸带上,1打孔,0不打孔。然后将纸带给计算机来处理,得到相应的程序。总的看来,机器语言是最直接接触硬件的语言,他对于程序员对硬件的了解程度颇高;同时程序写出来是大量的0,1代码,晦涩难懂,效率很低,非常容易出错。

 

 

汇编语言的产生正是因为人们看到了机器语言太麻烦。汇编语言和机器语言的主要差别在于指令的表示上。汇编语言用到了助记符这样的东西。汇编指令和机器指令一一对应。

 

比如说这样一条汇编指令:MOV AX,BX 它表示把寄存器BX的内容送到AX寄存器中;而其对应的机器指令是1000100111011000。我们看到,比起机器语言的0,1,汇编语言这样的形式更接近我们的自然语言,更容易记忆。

 

但是计算机只能读懂0,1代码而执行之。我们的汇编语言编好的程序如何执行呢?这就要用到编译程序了。这里,我们是把汇编语言转换成二进制代码。所以,这个编译器的工作就是把我们的汇编指令的字符们解析成对应的机器语言。

汇编转机器_thumb[4]

 

汇编语言中最重要的就是汇编指令,也就是机器码的助记符,比如我们见过的MOV ADD +寄存器内存什么的等等。其中还有伪指令,是由编译器执行的,不翻译成机器语言。还有一些其他的符号,+、-、*、/,这些也没有对应的机器码,由编译器识别。

 

 

高级语言是更接近我们自然语言的一种程序语言。常用的如C,C++,java等。这些语言通常也需要编译器来把他们编译成目标程序。通常目标程序都是汇编程序。相对编译器,还有一个叫做解释器的东西,在概念上和编译器有明显的区别。解释器是源程序的一个执行系统,编译器是一个源程序的转换系统。

编译器是把源程序的每一条语句都编译成机器语言,并保存成二进制文件,这样运行时计算机可以直接以机器语言来运行此程序,速度很快;而解释器则是只在执行程序时,才一条一条的解释成机器语言给计算机来执行,所以运行速度是不如编译后的程序运行的快的。

 

这里有点转向编译原理的知识,我忘得差不多了。java程序的执行就是基于解释器的。

高级语言这些个问题对于汇编学习没什么大用,也不多说了。

 

--------------------------------------------------------------------------------------------------------

汇编和硬件结合很紧,所以接下来就是一些有关硬件神马的概念了。

 

首先,存储器。现代计算机都是以存储器为核心的。CPU要获得指令,获取数据,这些都是要从存储器来得到的。这里的存储器指的就是内存。我们需要了解,CPU如何把内存中的数据读出来,转化为指令或者数据,或者如何向内存中写入数据。

 

一个很老的问题:我们在内存中如何区分指令和数据?哪些算是指令或数据?

 

这里我想到了组成原理中讲到的一些知识,大概是有关指令执行的过程。

 

我们知道,内存中存放的全都是0,1这样的东西,根本没给这些0,1加任何标签说谁谁谁是指令,谁谁谁是数据。例如二进制1000100111011000,计算机可以把他看作89D8H这样的数据来处理,也可以解析成为指令MOV AX,BX来执行。

 

一条机器指令执行分三个阶段:取指令阶段,分析取数阶段和执行阶段。其中的取指令阶段就是把内存中的数据取到CPU中的指令寄存器;然后就是分析取数阶段,分析这个寄存器的数据,看看他是哪个指令,这个指令要从内存还是寄存器中取什么数,接着立马根据其相应的寻址方式,再到内存中取数据,放到相应的寄存器中;最后就是执行阶段,把取回来的数据进行相应的操作和运算。

 

所以,一个内存中的数据,到底最后被当作数据还是指令,全看它是在哪个阶段被读走。

 

当然,按照我们一般的程序装入的方式,我们的程序是在一个连续的内存空间的,指令在一个段,数据占一个段。

 

为了方便存取,存储器被划分成若干个存储单元,存储单元一般是从0开始编号,每个存储单元可以存放8个bit,组成一个byte。大容量的存储器还用到更大的单位,KB,MB,GB,TB。

 

CPU要读或者写一个内存位置的数据,先要给出存储单元的编号,然后才能进行下一步的操作。

比如我要先给出地址0003H,说明我要操作的存储单元的编号;接着CPU发出相应的控制信号,说明我是要读这个单元的信息还是要向这个单元写数据;最后,我要读数据,就给出我要把这个地址数据存储的具体地址给出来,比如给出AX寄存器;或者我要写入,就要给出我要往这个单元写的数据的内容。

 

这里还要提到总线,上面讲到的地址信息,数据信息,控制信号,都是要通过总线,按一定先后顺序,从CPU传到相应的部件。总线也就是一种导线。

 

CPU读数据_thumb[4]

这条指令的顺序是这样:首先,CPU通过地址线,把3号地址的信息传到内存;然后CPU通过控制总线,发出读信号,告诉内存我要做读操作;存储器做出相应的响应,就把3好地址的数据,通过数据总线,打回给CPU。

 

写操作也是类似的。

 

这是说明了CPU是如何执行读写指令的。我们写程序,就是要让CPU做出一系列我们希望的这些操作,完成一个任务。计算机是需要一系列电平的变化来产生相应的硬件反应的。所以我们的程序最后的内容应该就是一系列这种电平的变化,就是相应的0,1代码。

 

例如对于8086CPU,机器码101000000000001100000000对应的操作就是从3号单元读数据到AX寄存器。看到上面的机器码,计算机就会完成对应得操作。但是机器码太难记,于是我们的汇编语言把它简化为MOV AX,[3]。

 

 

现在,我们知道CPU通过地址总线来给出相应的存储单元。也就是说,地址总线可以表示多少个不同的信号,就决定了CPU最多能确定多少个不同的地址。而一根导线,可以传送的稳定状态就是高或者低电平,对应到二进制就是0,1。也就是一根导线可以表示两种状态。如果有10根,就可以表示2的十次方中不同的信号,对应2的10次方个内存单元。地址线的根数,通常叫做地址线宽度

 

CPU通过数据总线,来完成数据的传输。数据总线的宽度决定了数据传输的速率。比如数据总线宽度是8,这样一次可以传输8bit的数据,也就是一个字节。16位宽的数据总线一次可以传输2字节。这里举例来说明,两种总线宽度的CPU在传输数据89D8H时的不同。我们要注意到,8位数据总线宽度的传输顺序是先传低位后高位,这个顺序也会在以后的debug中看到。

 

8088CPU_thumb[4]

8086cpu_thumb[6]

 

控制总线的宽度决定了CPU对于各种部件的控制能力。比如有一根控制线负责读控制信号,低电平有效;有一根控制线来负责输出写信号。

 

内存地址空间是指CPU可以寻址的存储单元的。地址线宽度为10,则可以确定1024个不同的存储单元,这些个存储单元构成了这个CPU的内存地址空间。

 

主板是把各种设备连接在一起的一个大板子。最简单的,上面有CPU,存储器,外围芯片组,还有各种扩展插槽。他们通过总线连接在一块。

 

接口卡可以理解是一个在外设和CPU中间的一个中转站。接口卡直接插在扩展插槽中,扩展插槽和CPU通过总线相连,传输信息。CPU不能直接控制外设(原因主要有这么几个:1.信号匹配问题 2.速度有差距的问题 3.如果直接控制,CPU会花大量时间处理外设,降低效率 4.如果直接控制,外设设计会依赖于CPU的设计),所以通过接口卡,CPU可以方便的进行外围设备的控制。

 

各类存储芯片:从读写性质上,存储芯片可分为随机存储器RAM和只读存储器ROM。这些存储器在功能和连接上又分为随机存储器(我们平时说的内存就是指这个,他可以由主板上的和扩展插槽上的两个地方的RAM一起组成),装有BIOS(Basic Input/Output System)的ROM(可以用来执行最基本的输入输出动作,主板会有主板的ROM,显卡会有显卡的ROM来存BIOS),还有就是接口卡中的RAM,最典型的就是显存了。显卡会将显存中的显示在显示器上。

 

上述这些存储器,物理上是独立元件,但是他们都通过总线和CPU相连,CPU对他们进行读写操作的时候都是发出内存读写命令。这也就是所谓的外设与内存统一编址。也就是说,CPU在访问这些设备的时候,都是通过内存地址进行的,这些存储器一同构成了一个逻辑存储器,而这个逻辑存储器就是我们说的内存地址空间。在汇编中,我们的要面对的就是这个内存地址空间。

逻辑存储器_thumb[6]

 

图中看到,各种存储器被当做一个逻辑存储器,对一个地址的访问,就对应这一个相应的物理存储器。比如,假定:逻辑地址0000H~7FFFH是主存储器的地址;8000H~9FFFH是显存的地址空间;A000H~FFFFH是其他ROM地址空间。这样对于一个地址为1234H的访问就是访问主存;访问8123H就是访问显存的一个存储单元;访问A123H只能是读操作,因为这个区域是ROM.

 

不同的计算机,内存地址空间的分配是不相同的。

 

我们会在以后的编程中感性的了解内存地址空间这个东西~

posted @ 2011-02-28 15:32  Vanie  阅读(4185)  评论(18编辑  收藏  举报