[ASM] 基础概念
1. 汇编语言的产生
计算机从本质上来讲,就是中央处理器(CPU)连接一堆外设。CPU是计算机的核心部件,它控制整个计算机的运作并进行运算。要想CPU工作就必须提供指令和数据。这些指令和数据在存储器(严格来说应该是逻辑存储器)中存放,也就是平台我们所说的内存。指令和数据是应用上的概念,在内存或磁盘上,它们没有区别都是二进制信息。CPU在工作的时候把有的信息看作指令,有的信息看作数据,为同样的信息赋予了不同的意义。
只要CPU一加电,它就从预设的地址开始一直执行下去。在执行程序的时候CPU是从CS:IP指向的某个地址开始,自动向下读取指令执行。CPU读取的指令就是机器语言。
机器语言是机器指令的集合。机器指令就是一台机器可以正确执行的命令。电子计算机的机器指令是一列二进制数字。计算机将之转变成一列高低电平,从而使计算机的电子器件受到驱动,进行运算。由于机器指令难于辨别和记忆,给整个产业的发展带来了障碍,于是汇编语言产生了。汇编语言的主体是汇编指令,汇编指令和机器指令是一一对应的,汇编指令是机器指令便于记忆的书写格式。
汇编语言的核心是汇编指令,它决定了汇编语言的特性。
汇编语言主要由3类指令组成。
- 汇编指令:机器码的助记符,有对应的机器码。
- 伪指令:没有对应的机器码,由编译器执行,计算机并不执行。
- 其他符号:如+、-、*、/等,由编译器识别,没有对应的机器码。
2. 存储单元和总线
存储器被化分成若干个存储单元,每个存储单元从0开始编号。电子计算机的最小信息单位是bit,也就是一个二进制位,8个bit组成一个Byte字节,微机存储器的容量是以字节为最小单位计算的。
CPU要从编号的存储单元中读取或写入数据,必须进行下面3类信息的交互。
- 存储单元的地址(地址信息);
- 器件的选择,读或写的命令(控制信息);
- 读或写的数据(数据信息);
电子计算机处理、传输的信息都是电讯号,电信号是用导线传送的,专门连接CPU和其他芯片的导线,称为总线。从物理上来讲,总线就是一根根导线的集合。根据传送信息的不同,总线从逻辑上分为:地址总线、控制总线、数据总线。
如CPU 从3号单元中读取数据的过程
- CPU通过地址总线将地址信息3发出
- CPU 通过控制线发出内存读命令,选中存储器芯片,并通知它将要从中读取数据
- 存储器将3号单元中的数据,通过数据总线传入CPU
-
地址总线和寻址能力
CPU 是通过地址总线来指定存储器单元的,可见地址总线上能传送多少个不同的信息,CPU就可以对多少个存储单元进行寻址。在计算机中一根导线可以传送和稳定状态只有两种:高电平或是低电平。用二进制表示就是1或0。
所以一个CPU有N要地址线,则可以说这个CPU的地址总线的宽度为N, 最多可以寻找2的N次方个内存单元。
-
数据总线
CPU与内存或其他器件之间的数据传送是通过数据总线传送的。数据总线的宽度决定了CPU和外界的数据传送速度。8根数据总线一次可传送一个8位二进制数,16根数据总线则可传两个字节。
-
控制总线
CPU对外部器件的控制是通过控制总线进行的。所以有多少要控制总线,就意味着CPU提供了对外部器件的多少种控制。控制总线决定了CPU对外部器件的控制能力。
3. 内存地址空间
一台PC机中,装有多个存储器芯片,从读写属性上分为:随机存储器(RAM)和只读存储器(ROM). 如主板上的RAM和插在扩展槽上的RAM、显卡、网卡等都有相应的存储器芯片。这些芯片都和CPU 总线相连,CPU对它们进行读或写的时候都是通过控制线发出的命令。
可以说,CPU 把它们全当作内存来对待,把它们总的看作一个由若干存储单元组成的逻辑存储器,这个逻辑存储器就是我们所说的内存地址空间。于是,每个物理存储器在这个逻辑存储器中占有一个地址段,既一段地址空间。CPU对这段地址空间中读写数据,实际上就是在对相应的物理存储器中读写数据。
内存地址空间的大小受CPU地址总线宽度的限制,若地址总线宽度为20,则可以定位2的20次方个内存单元,内存地址空间大小为1M, 若地址总线宽度为32,则内存地址空间最大为4GB。现在明白为什么XP 系统不能完成利用4GB内存空间了吧。
4. 物理地址与寄存器
存储器被化分成若干个存储单元,每个存储单元从0开始编号,所用的内存单元构成的存储空间是一个一维的线性空间,每一个内存单元在这个这间中都有唯一的地址,这个地址称为物理地址。
CPU 通过地址总线送入存储器的,必须是一个内存单元的物理地址。所以CPU在发出物理地址之前必须先在内部形成这个物理地址,不同的CPU 可以有不同的形成方式。对于16位结构的CPU来说,若地址总线为20位,可以传送20位地址,达到1MB寻址能力。但16CPU表现出的寻址能力只有64KB。为了得到更大的寻址能力,有的CPU 在内部用两个16位地址合成的方法来形成一个20位的物理地址。
比如8086CPU要读写内存时:
- CPU中的相关部件提供两个16位地址,一个称为段地址,另一个称为偏移地址,
- 段地址和偏移地址通过内部总线送入一个称为地址加法器的部件;
- 地址加法器将两个16位地址合成为一个20位的物理地址;
- 地址加法器通过内部总线装20位物理地址送入输入输出控制电路;
- 输入输出控制电路将20位物理地址送上地址总线;
- 20位物理地址被地址总线传送到存储器。
地址加法器采用 物理地址=段地址 * 16 + 偏移地址 的方法用段地址和偏移地址合成物理地址。
注意段地址并不是说内存被分成许多小段了,这里段地址是对于CPU来说的,由于地址加法器采用 物理地址=段地址 * 16 + 偏移地址的方法来表示物理地址,我们在编程的时候可以,将若干地址连续的内存单元看作一个段,用段地址*16定位段的起始地址,用偏移地址定位段中的内存单元。
注意:
1、段地址*16肯定是16的倍数,所以一个段的起始地址也一定是16的倍数;
2、偏移地址为16位,16位寻址能力为2的16次方为64KB,所以一个段的长度最大为64KB.
现在你可以想象,程序在内存中是如何布局的?为什么好多工具分析时会有程序基地址、偏移地址等?
CPU是怎么区分数据和指令的呢?CPU内部有好多寄存器。
比如:
- CS代码段寄存器,IP为指令指针寄存器,任意时刻,CPU 将CS:IP指向的内容当作指令执行。
- DS数据段寄存器,DS:[0]指向DS*16+0的内存单元。
- SS栈段寄存器,SP存栈偏移地址,任意时刻SS:SP指向栈顶元素。
5. 栈
栈空间也是内空间的一部分,它只是一段可以以一种特殊方式进行访问的内存空间。其特殊性在于,最后进入这个空间的数据,最选出去。栈有两个基本操作:入栈和出栈。栈的操作规则被称为:LIFO(Last In First Out,后进先出).
现今的CPU都有栈的设计,8086CPU也提供相关的指令来以栈的方式访问内存空间。我们编程的时候,可以将一段内存当作栈来使用。基本操作为PUSH和POP。那么CPU怎么会知道我们想把哪段空间当栈使用呢?CPU中有两个寄存器,段寄存器SS和寄存器SP,栈顶的段地址放在SS中,偏移地址放在SP中。任意时刻,SS:SP指向栈顶元素。那么PUSH 和POP执行进自然是从SS:SP得到栈顶的地址。
8086CPU 只提供了寄存器来存放栈顶位置,没有提供寄存器存放栈空间的大小,所以8086CPU不会保证对栈的操作不会越界。我们在编程的时候要自己操作栈顶越界的问题,合理的安排栈空间大小,出栈和入栈时注意不要越界。