1.CPU
-
编译器是一个程序,它可以将我们所写的程序翻译成特殊的机器语言结构。通常,每一种类型的CPU都有它自己唯一的机器语言。这是为什么为Mac写的程序不能在IBM类型PC机运行的一个原因。
-
电脑通过使用时钟来同步指令的执行。时钟脉冲在一个固定的频率(称为时钟频率)。当你买了一台1.5GHz的电脑,1.5GHz就是时钟频率,即每秒15亿次的时钟脉冲。时钟并不记录分和秒。它以不变的速率简单跳动。电子计算机通过使用这个跳动来正确执行它们的操作,就像节拍器的跳动如何来帮助你以正确的节奏播放音乐。一个指令需要跳动的次数(或就像他们经常说的执行周期)依赖CPU的产生和模仿。周期的次数取决于它之前的指令和其他因素。
2.程序内存需要分段(以8086CPU为例)
- 内存按访问的方式来看,就像长方形的带子,地址依次升高。内存是一个随机读写设备,即可以访问内部任何一处,不需要从头开始找,只要直接给出实际物理地址即可。
- 而分段是内存访问的机制,是给CPU用的访问内存的方式,只有CPU才会关注段
- 8086CPU有20根地址线,最大可寻址内存空间为1MB,2^20Byte。而8086的寄存器只有16位,指令指针(IP)和变址寄存器(SI、DI)也是16位的。用16位的地址寻址1MB空间是不可能的。所以就要把内存分段,也就是把1MB空间分为若干个段,每段不超过64KB,在8086中设置4个16位的段寄存器,用于管理4
种
段,注意这里是种
,不是个
(内存是分成四种段,这是考虑到程序执行时需要的四个部分。但是,内存不止四个段,只是同时最多只有四个段在工作,其他的在“睡眠”,需要时再“唤醒”。),具体种类为:CS是代码段,DS是数据段,SS是堆栈段,ES是附加段。 - 内存分段后,内存的地址(又称物理地址)就由两部分组成:段地址和段内偏移地址,段寄存器管理的是段地址。把内存分段后,每一个段就有一个段基址,段寄存器保存的就是这个段基址的高16位,这个16位的地址左移四位(后面加上4个0)就可构成20位的段基址。
- 上述便是段地址×16(或者左移四位)+偏移地址=物理地址”的寻址模式的由来。
- 8086CPU有20根地址线,最大可寻址内存空间为1MB,2^20Byte。而8086的寄存器只有16位,指令指针(IP)和变址寄存器(SI、DI)也是16位的。用16位的地址寻址1MB空间是不可能的。所以就要把内存分段,也就是把1MB空间分为若干个段,每段不超过64KB,在8086中设置4个16位的段寄存器,用于管理4
- 第二个是保护模式下,段(segmentation)强调的是分割,用来把内存分成不同的地址空间,每个段一个空间,而后通过CPU的MMU转换成实际物理地址。由于程序运行在不同的段里,根本上保护了CPU保护模式下的各个不相关的代码,所谓进程或者作业。
3.CPU的不同型号
-
8088,8086:
这些CPU从编程的观点来看是完全相同的。它们是用在早期PC机上的CPU。它们提供一些16位的寄存器:AX,BX,CX,DX,SI,DI,BP,SP,CS,DS,SS,ES,IP,FLAGS。它们仅仅支持1M字节的内存,而且只能工作在实模式下。在这种模式下,一个程序可以访问任何内存地址,甚至其它程序的内存!这会使排除故障和保证安全变得非常困难!而且,程序的内存需要分成段。每段不能大
于64K,即2^16Byte(16位的寄存器)。 -
80286:
这种CPU使用在AT系列的PC机中。它在8088/86的基本机器语言中加入了一些新的指令。然而,它主要的新的特征是16位保护模式。在这种模式下,它可以访问16M字节的内存和通过阻止访问其它程序的内存来保护程序。可是,程序依然是分成不能大于64K的段,即2^16Byte(16位的寄存器)。 -
80386:
这种CPU极大地增强了80286的性能。首先,它扩展了许多寄存器来容纳32位数据(EAX,EBX,ECX,EDX,ESI,EDI,EBP,ESP,EIP)而且增加了两个新的16位寄存器(FS,GS)。它同样增加了一个新的32位保护模式。在这种模式下,它可以访问4G字节。程序同样分成段,但是现在每段大小同样可以到4G,即2^32Byte(32位的寄存器) -
还有很多CPU型号这里不在列出,毕竟我们不做CPU的芯片开发,我们学习结构较为简单的CPU,了解其原理即可。
4.十六位寄存器(以8086为例,即x86架构)
寄存器是CPU内部的存储部件与内存空间没有关系,设置寄存器的原因是为了减少CPU与内存交换数据的次数,以提高计算机的工作速度。
8086 CPU 中寄存器总共为 14 个,即 AX,BX,CX,DX,SP,BP,SI,DI,IP,FLAG,CS,DS,SS,ES,且都为16位。
4.1通用寄存器:
通用寄存器有 8 个,分别是 AX,BX,CX,DX,SP,BP,SI,DI 。
4.1.1数据寄存器:
AX,BX,CX,DX 称作为数据寄存器,都可以暂存一般的数据,他们还具有其他的特殊用途,具体的特殊用途如下:
-
AX (Accumulator)
:累加寄存器,也称之为累加器,除了做加法以外,还可以做乘法或除法,当操作的数是16位的时候经常与DX搭配使用,如下:-
做除法(DIV)运算时:
除数可以存放在寄存器中或者是内存单元中
,被除数默认放在AX或(DX和AX)中,如果除数为8位,被除数则为16位,默认放在AX中;如果除数为16位,那么被除数就为32位,存放在DX和AX两个寄存器中,高16位存放在DX,低16位存放在AX。
即除数可以是 8 位或者是 16 位的:- 当除数是 8 位时,被除数一定会是 16 位的,并且默认是放在 AX 寄存器中,如果除数是 8 位的,则在 AL 中会保存此次除法操作的商,而在 AH 中则会保存此次除法操作的余数,
- 而当除数是 16 位时,被除数一定是 32 位的,因为 AX 是 16 位寄存器,自然,放不下 32 位的被除数,所以,在这里还需要使用另一个 16 位寄存器 DX ,其中 DX 存放 32 位的被除数的高 16 位,而 AX 则存放 32 位的被除数的低 16 位,同时,AX 的作用还不仅仅是用来保存被除数的,当除法指令执行完成以后,当然,如果除数是 16 位的话,则 AX 中会保存本次除法操作的商,而 DX 则保存本次除法操作的余数。
-
做乘法(MUL)运算时:
两个相乘的数要么都是 8 位,要么都是 16 位:- 如果两个相乘的数都是 8 位的话,则一个默认是放在 AL 中,而另一个 8 位的乘数则位于其他的寄存器或者说是内存字节单元中,当 MUL 指令执行完毕后,如果是 8 位的乘法运算,则默认乘法运算的结果是保存在 AX 中,
- 而如果两个相乘的数都是 16 位的话,则一个默认存放在 AX 中,另一个 16 位的则是位于 16 的寄存器中或者是某个内存字单元中。
同时,而如果是 16 位的乘法运算的话,则默认乘法运算的结果有 32 位,其中,高位默认保存在 DX 中,而低位则默认保存在 AX 中。
-
-
BX (Base)
:基地址寄存器;
BX 主要还是用于其专属功能 :存储偏移地址(与段寄存器搭配,可以寻址物理地址)。
如果未额外指明寄存器,会默认使用 DS 段寄存器,例如 [BX],相当于DS:[BP]
段地址×16(或者左移四位)+偏移地址=物理地址 -
CX (Count)
:计数器寄存器;
CX 中的 C 被翻译为 Counting 也就是计数器的功能,当在汇编指令中使用循环 LOOP 指令时,可以通过 CX 来指定需要循环的次数,而 CPU 在每一次执行 LOOP 指令的时候,都会做两件事:- 一件就是令 CX = CX – 1,即令 CX 计数器自动减去 1;
- 还有一件就是判断 CX 中的值,如果 CX 中的值为 0 则会跳出循环,而继续执行循环下面的指令,如果 CX 中的值不为 0 ,则会继续执行循环中所指定的指令 。
-
DX (Data)
:数据寄存器;
在做除法(DIV)或者乘法(MUL)运算的时候可以搭配AX使用,请参考上面关于AX详细用法的讲解。
4.1.2指针寄存器:
SP (Stack Pointer)
:堆栈指针寄存器;
段地址使用默认的 SS 寄存器中的值
在任何时刻,SS:SP 都是指向栈顶元素BP (Base Pointer)
:基指针寄存器;
段地址使用默认的 SS 寄存器中的值
bp为基址寄存器,一般在函数中用来保存进入函数时的sp的栈顶基址。
sp会随着带有堆栈操作的指令(比如PUSH、CALL、INT、RETF)产生变化,而BP不会,所以在带参数的子过程中用BP来获取参数和访问设在堆栈里面的临时变量。
每次子函数调用时,系统在开始时都会保存这个两个指针并在函数结束时恢复sp和bp的值。如下
在函数进入时:
push bp // 保存bp指针
mov bp,sp // 将sp指针传给bp,此时bp指向sp的基地址。
// 这个时候,如果该函数有参数,则[bp + 2*4]则是该子函数的第一个参数,[bp+3*4]则是该子函数的 第二个参数,以此类推,有多少个参数则[bp+(n-1)*4]。
.....
.....
函数结束时:
mov sp,bp // 将原sp指针传回给sp
pop bp // 恢复原bp的值。
ret // 退出子函数
只有在寻找堆栈里的数据和使用个别的寻址方式时候才能用到
比如说,堆栈中压入了很多数据或者地址,你肯定想通过SP来访问这些数据或者地址,但SP是要指向栈顶的,是不能随便乱改的,这时候你就需要使用BP,把SP的值传递给BP,通过BP来寻找堆栈里数据或者地址。
他们也是通用寄存器,在许多情况下也可以像通用寄存器一样暂存数据。但是,它们不可以分解成两个8位寄存器,因为只有数据寄存器才可以分解成两个8位寄存器。
4.1.3变址寄存器
SI (Source Index)
:源变址寄存器;
SI会默认使用 DS 段寄存器DI (Destination Index)
:目的变址寄存器;
DI 会默认使用 DS 段寄存器
两个16位指针寄存器:SI 和DI 。通常它们都是当作指针来使用,但是在许多情况下也可以像通用寄存器一样暂存数据。但是,它们不可以分解成两个8位寄存器,因为只有数据寄存器才可以分解成两个8位寄存器。
4.2段寄存器:
CS (Code Segment):代码段寄存器;DS (Data Segment):数据段寄存器;SS (Stack Segment):堆栈段寄存器;ES (Extra Segment):附加段寄存器;
16位CS,DS,SS 和ES 寄存器是段寄存器。它们指出程序不同部分所使用的内存。CS代表代码段,DS 代表数据段,SS 代表堆栈段和ES代表附加段。ES当作一个暂时段寄存器来使用。这些寄存器的细节描述在后面的文章中。
4.3控制寄存器
IP (Instruction Pointer):指令指针寄存器;
指令指针寄存器(IP) 与CS寄存器一起使用来跟踪CPU下一条执行指令的地址。通常,当一条指令执行时,IP提前指向内存里的下一条指令。FLAG:标志寄存器;
FLAGS寄存器储存了前面指令执行结果的重要信息。这些结果在寄存器里以单个的位储存。例如:如果前面指令执行结果是0,Z位为1,反之为0。并不是所有指令都修改FLAGS里的位。
5. 三十二位寄存器(80386 )
80386及以后的处理器扩展了寄存器。例如:16位AX寄存器扩展成了32位。为了向后兼容,AX依然表示16位寄存器而EAX 用来表示扩展的32位寄存器。AX是EAX 的低16位就像AL是AX(EAX)的低8位一样。但是没有直接访问EAX 高16位的方法。其它的扩展寄存器是EBX,ECX,EDX,ESI 和EDI 。许多其它类型的寄存器同样也扩展了。BP变成了EBP;SP 变成了ESP;FLAGS变成了EFLAGSEFLAGS 而IP变成了EIP。但是,不同于指针寄存器和通用寄存器,在32位保护模式下只有这此寄存器的扩展形式被使用。