《汇编语言(王爽 著)》读书笔记
最早期出现的计算机,是名副其实的“计算”机。这个机器可以执行一系列特定的指令,即机器指令。而由机器指令构成的集合被称为指令集,也就是我们说的机器语言。机器指令是由一系列的二进制数字0和1构成的。计算机将这些二进制的数字转变为一系列的高低电平,驱动计算机内的各种电子器件,进行运算。
其实严格来说,读取指令并且执行计算和控制功能的,只是计算机里面的一个模块,叫做CPU(Central Processing Unit)。计算机发展到现在,CPU已经被集成到一块芯片当中了,被称为微处理器(microprocessor)。每一种微处理器,由于其硬件设计和内部构造的不同,就需要不同的电平脉冲来控制它工作。所以每一种微处理器也都有自己的机器指令集,即机器语言。
早期的程序设计均使用机器语言,但是这种即难辨识又难以记忆的机器语言给行业发展带来了障碍。汇编语言应运而生。
汇编语言实际上就是用简短的英文缩写来表示机器语言中的相应指令。由汇编器和链接器处理后,汇编语言就被翻译为机器语言,可由机器来执行。虽然只做了简单的抽象,这已经大大提高了程序的可读性。编语言由三种指令构成:
汇编指令:机器码的助记符
伪指令:如assume cs:code,由编译器执行
其他符号:如+、-、*、/,由编译器识别
其中只有汇编指令有对应的机器码,构成了汇编语言的核心。
大多数计算机采用了冯·诺伊曼体系结构,由输入设备,输出设备,主存储器和中央单元构成,如图1.1。
IAS是冯·诺伊曼设计的首台应用了储存程序思想(stored-program concept)的计算机,并且成为了通用型计算机的鼻祖。所谓的储存程序思想就是将指令和数据一同储存在储存器中,计算机自己从储存器中读取指令并执行,程序就能够通过修改存储器中的值来得到改变。这一想法由冯·诺伊曼和艾伦·图灵在几乎同一时期产生,最终由冯·诺伊曼为建造EDVAC (Electronic Discrete Variable Computer)首次公开提出。在冯氏结构中,CPU能也只能从主存储器(main memory,也即内存)中读取指令。
从机器指令的角度来看,一个计算机程序实际做的事情就是不停的操纵存储器和CPU内部的寄存器,控制二进制数据的流动。
要理解这句话,我们先看一下CPU是如何控制计算机的各个部件的。
内存全称为内部存储器(internal memory),储存着指令和数据,没有内存的CPU就如同没有记忆的大脑一样,是根本无法工作的。一般来讲,存储器被划分若干个,存储单元从0开始依次编号,每个存储单元可以存储1个字节(byte),即8个比特(bit)。我们之前提到的指令和数据,在存储时是没有任何区别的。引用王爽在《汇编语言》中的形象的类比:“就像围棋中的棋子,在棋盒里的时候没有任何区别”。关键在于CPU对于他们的解释,这是后话。
CPU在操纵内存中的数据时,需要指明三点:操纵何处的数据,做何种操作,需要的附加数据(如,执行写入时的写入数据)。实际上,除了内存以外,CPU还需要和很多种存储器进行交互,它们(包括内存)可以分为两种:ROM(Read Only Memory)和RAM(Random Access Memory)。CPU在与它们交互时,均涉及到下面三类数据的交互:
地址信息:存储单元的地址
控制信息:器件的选择,读或写的命令
数据信息:读或写的数据
这些信息的交互是通过三组导线传递的,如图1.2。
这些导线被称为总线,连接着CPU以及其他芯片。总线从物理上看就是一根根导线的集合,从逻辑上分为三类,地址总线,控制总线和数据总线。
计算机有很多外部设备,直接控制这些外设的是插在主板的扩展插槽上的接口卡。CPU通过总线与接口卡相连,向接口卡发送命令进而控制外设。接口卡上也带有装有BIOS的ROM,某些需要暂存数据的接口卡上也有自己的RAM存储器。CPU操纵接口卡的方式就是操纵里面的存储器。实际上,CPU在读写这些存储器时,通过控制总线发出的都是内存的读写命令。也就是说,CPU在操纵它们的时候,把它们总的看作一个由若干个存储单元组成的逻辑存储器,这个逻辑存储器被称为内存地址空间。另一种对内存地址空间的定义是根据地址总线的寻址范围得来的。在汇编语言中,我们面对的就是内存地址空间。
在特定物理存储器的地址空间中读写数据,实际上就是在相对应的物理存储器中读写数据。CPU就这样控制了各种接口卡,从而控制了各种外部设备。举个例子,在8086PC的内存空间中,显存对应的地址空间为B8000H~BFFFFH的32KB的空间,在这个地址空间写入数据,写入的内容被解释后会立即显示在显示器上。
从最底层来看,CPU的确是在控制各种二进制数据的流动。
从宏观认识了整个计算机的构造和原理后,现在来看看CPU。一个典型的CPU主要由运算器、寄存器、控制器和内部总线构成。但在汇编语言中我们关心的只是其中的“存储器”——寄存器(register)。不同CPU寄存器的个数和结构是不同的,以下全部以Intel 8086CPU(以下简称8086CPU)为例展开讨论。
8086CPU共有14个寄存器,且均为16位。其名称和主要功能如图1.4所示。由于8086CPU的上一代CPU中的寄存器都是8位的,为了保证兼容,8086CPU的AX,BX,CX和DX这四个寄存器都可以分为两个独立的8位寄存器来使用,分别为AH,AL,BH,BL,CH,CL,DH,DL。所以8086CPU可以一次性处理两种尺寸的数据:字节(byte,由8个bit组成)和字(word,由2个byte构成)。这样看来,二进制数据在计算机中至少是以8个为一个基本单元出现的。为了在表示上更加直观,我们经常使用16进制,并且在16进制数据后面加H,在二进制数据后面加B以区别。
当使用汇编语言编程时,我们使用汇编指令不断地在与和存储器和寄存器打交道。因为寄存器的数目有限,用法也有要求,所以我们不仅要明确各个寄存器的使用方法,还需要对任何时候各个寄存器的状态了然于胸。这样在编程时显得十分的麻烦,这时候,高级的语言的优势就体现出来了,高级语言通过编译器来管理寄存器的使用,从而使程序员从中脱身,能专注于业务逻辑的实现。而且汇编程序中的确有一些语句是机械性的重复。但是编译器也有效率不高甚至出bug的时候,这时候凭借汇编知识,我们就能自己做一些优化。而且通过汇编语言来了解计算机的构造和运行原理也是不错的切入点。窃以为对于“学习XXX还有没有必要”这种问题的正确解答可能比学习本身还要困难:P。