汇编语言那些事儿

来源:互联网 发布:mac隐藏照片 编辑:程序博客网 时间:2024/06/01 10:06

相信有很多计算机爱好者或是计算机及相关专业的学生,计算机相关职业的工作者都曾一度对于计算机底层实现原理十分着迷,从而一度去学习《计算机组成原理》《编译原理》《汇编语言》等课程。在这些课程中,《汇编语言》是基础,也是初学者了解计算机如何执行程序命令的首选课程。但对于像我一样的小白来说,初学汇编语言一定会产生很多这样那样的疑惑。如果选择的学习教材不当,老师的授课方式不当,那么这门课程的思维便无法贯通,也无法联系起计算机组成原理等其他课程的相关知识,越到后来越迷茫。

为此,笔者结合自己自学汇编语言的经历,谈谈汇编语言中的那些一度令我们感到疑惑的事儿。

首先要为大家推荐一下一本十分优秀的教材:《汇编语言》(第三版) 王爽著。王爽老师是著名的计算机教育专家,这本书是她最著名的著作,在业界好评如潮。其特点是讲解详细,语言浅显且生动,读来不会感到疲惫,往往能抓住我们可能会感到迷茫的关键点做详细讲解。笔者先前跟着学校学习汇编语言,垃圾教材加上教师的不知如何评价的授课方式,学完还不知道什么是汇编语言。但自从偶遇这本书,瞬间打通任督二脉,豁然开朗。

1. 首先我们要来谈谈单位问题。相信很多读者在学习汇编语言的时候都碰到几个让人很头疼的单位概念,字,字节,比特。

有些基础的人可能会比较熟悉字节,比特这些单位。

1字节 = 8比特(1Byte = 8bit)

1千字节 = 1024字节(1KB = 1024B)

1兆 = 1024千字节(1MB = 1024KB)

1季 = 1024兆(1G = 1024MB)

有必要说明的是每个比特也就是二进制的一位,比如10011011就是八位二进制数据,也就是8bit,即1Byte。


这里有一个问题,在十进制里都是换算都是10,100,这里为什么是1024?

因为计算机都是以二进制为基础的,1024也就是2的10次方。


看起来,计算机单位已经很完备了,为什么还要提出字的概念?

1字 = 2字节

在8086CPU中,数据和指令的存储都是以十六进制的形式进行的,每个单元存放十六进制的两位,且低位数据存放在低地址,高位数据存放在高地址,江湖人称小端存储。

比如要存放2345H,那么从上至下,会先放45H,再放23H。

这里我们做一个计算:每个单元存放十六进制的两位,十六进制的每位代表四位二进制,共8bit,即1Byte。也就是说,8086CPU中,每个单元存放1字节信息。


那么为什么要用字,而不用字节呢?

在汇编语言中,程序员只能通过代码控制各寄存器,进行信息的存取,而寄存器是以4位十六进制为单位进行存取的。比如指令mov ax,2345H,就是一次存取一字,用两个单元来存储。伪指令里dw也是表示字。那么读者要问了,为什么要以4位十六进制为单位进行存取?


2. 8086CPU中,CPU要想进行数据的读写,就必须和外部器件(芯片)进行信息交互,而交互是通过总线来完成的。根据交互信息的不同,总线分为三类

(1) 存储单元的地址——地址信息——地址总线

(2) 器件的选择,读或写的命令——控制信息——控制总线

(3) 读或写的数据——数据信息——数据总线

一般来说,CPU执行汇编指令的过程为,先通过地址总线在内存中找到地址并传回,再根据地址通过数据总线找到对应的数据并传回,再通过控制总线执行具体的命令。


这里有必要提一下我们是如何驱动计算机或微处理器工作的呢?

我们知道,程序员所编写的代码大多为高级编程语言,编译器会将其编译为汇编语言,再转化为机器语言,最终计算机执行的是二进制机器语言。二进制只有两种形式,即0和1,它们分别控制低电平和高电平。也就是说,真正驱动计算机工作的是机器语言所代表的电平信息。


8086CPU的数据总线宽度为16,意味着我们一次可以并行传输16位二进制信息,即一个字大小的信息,这也就解释了为什么我们要以4位十六进制为单位进行存取。


3. 说了那么多,读者可能会疑惑,前面反复提到了8086CPU,这到底是什么鬼?

大伙知道,微处理器芯片是有一个几十年发展过程的,早期我们所使用的CPU诸如8080,8085等很多是8位芯片。而8086是1978Intel公司研发出来的16位芯片,地址总线20条,可一次寻找1MB的地址空间。目前,使用较多的正是8086的CPU以及其汇编指令。


4. 在最初学习汇编语言的时候,对于一个概念始终混淆不清——内存。这个概念说起来容易,就是内存储器,但是和CPU联系起来就麻烦了,我曾经还一度认为内存是CPU的一部分。

内存的作用是暂存CPU中的数据,同时和硬盘以及外存进行信息交换。后者请读者参考计算机组成原理相关资料,这里我仅就与CPU相关部分介绍。

我们运行一个程序,必然需要一系列的指令和数据,这些东西放在哪里?内存。内存可以理解成是计算机内部的一个小储物间,计算机把它要用的指令和数据都储存在这里,并随时读取和储存新的信息。

内存是以单元划分的,每个单元存放一字节的信息,且各单元是线性顺序放置的,也就是说,各单元是按照123456789这样顺序排列。每个单元都对应着一个地址,若要存取单元中的信息,则首先要找到该单元的地址,这一点就很好的解释了C++中指针的意义。


在这里,读者可能要提一个问题,前面说到内存单元中存放的都是二进制信息,而且其中有的是数据,有的是指令。那么计算机是如何知道哪些数据?哪些是指令的呢?

首先我要告诉大家,即便是同一个信息,同一段代码,它在不同情况下代表的东西都可能不一样。比如在A情况下是指令,到了B情况下就成了数据。那么计算机是如何分辨的呢?对于这个问题,首先我们要说明计算机的寻址方式,请您接着往下看。


5. 在计算机组成原理中介绍过一大堆寻址方式,什么直接寻址,间接寻址,基址寻址,立即寻址等。在这里我要说的是,CPU是如何确定物理地址的。有过一些基础的读者都知道,物理地址 = 段地址*16 + 偏移地址。其具体过程如下:

段寄存器和偏移地址分别向地址加法器提供段地址和偏移地址——》地址加法器计算出物理地址——》物理地址传输给输入输出控制电路——》输入输出控制电路通过地址总线将物理地址传输给内存,找到对应信息后传回——》输入输出控制电路将传回的信息传给指令缓冲器——》指令缓冲器传给执行控制器——》执行


8086CPU中,段寄存器有多个,例如CS、DS、SS,它们虽然同样提供段地址,但是种类却不同。CS提供的是指令段地址,DS是数据段地址,SS是栈段地址。意味着通过CS和其偏移地址所找到的信息必定是指令,DS则是数据。故而在计算机中,机器通过段寄存器和偏移地址寄存器找到要执行的信息的地址,根据段寄存器的种类确定找寻的信息的种类。


6. 读者可能又要问了,通过多个段地址寻址,这样不就相当于将内存划分为一段一段的了吗,但前面我们说过内存是线性连续的呀?

实际上,段的概念是我们人为确定的,实际并不存在,内存本身是线性连续的,但如果我们现在要使用其中的00:00~00:50的部分,那么我们就可以将这部分划分为一个段。用什么划分?段寄存器和偏移地址寄存器。最明显的就是栈,栈的段地址是栈底,偏移地址是栈顶。确定SS和SP寄存器的值后,栈顶到栈底的一段正是栈段。

根据段寄存器的不同,我们可以将内存分为

存放数据的数据段

存放代码的代码段

当作栈的栈段

注意,栈所用的空间仍是内存,只是我们人为地把内存中的某部分用作栈,并以栈的方式来存取。

以上也就是为什么汇编程序中会有code、stack、data三段的缘故了。

同时,8086寄存器的设计决定,每个段最大只有64KB。

此外,每次进行一次地址的读取后,偏移地址都要自动改变为下一地址,做好下次读取的准备。


7. 各寄存器中,大多数寄存器都有特定的含义与作用,只有四个基本没有,供我们灵活使用,即AX,BX,CX,DX。

这四个寄存器很有意思,每个都可以划分为两段,各是一字节的空间。以AX为例,高地址的为AH,低地址的为AL。

为什么要说明这个呢?

因为当我们对其中一个进行操作时并不会影响另一个。

例如,执行指令add al,2345H,假如计算的结果为12345H,高出的一位1并不会插入到AH中,AL的最终结果就是2345H。


8. 段寄存器和偏移地址寄存器无法直接使用传送指令,例如mov去传送数据。但是可以使用jmp修改CS和IP寄存器,例如jump ax表示将ax中的值传给IP。jump 1000H:2345H表示1000H传给CS,2345H传给IP。

通常我们给段寄存器传值是借助其他寄存器,例如

mov ax,1000H

mov cs,ax

这样就将1000H传入了cs中。

原创粉丝点击