8086处理器是划时代的产品。本文以8086处理器为例,进行说明。
.bin为后缀的文件是处理器指令文件,在电脑上双击它并不能直接执行。原因是中间有个操作系统,就是在具有操作系统的电脑上执行的可执行程序(exe)需要有固定的格式要求才能正确执行,这是Windows操作系统所要求的,而.bin文件不符合这种exe格式要求,所以不能直接通过双击执行它。
那想要执行自己的处理器指令文件怎么办呢?
想要执行自己的处理器指令两种方式:
计算机上电启动时,处理器都做了什么?
每当开机,处理器会执行硬件初始化以及一个可选的内部测试,然后将内部寄存器的内容都初始化到一个预制的内部的状态,在我们的计算机上都有一个预制的热启动按钮,热启动按钮和处理器的RESET引脚相连,当我们按下热启动按钮的时候,就会向RESET引脚发送一个信号,这同样导致处理器执行硬件初始化和内部自测试,并将内部所有寄存器内容初始化到一个预制的状态。
对于Intel8086来说,复位操作将使代码段寄存器CS的内容是FFFF,而对于其他寄存器来说,所有内容都会被初始化为0,包括所有的通用寄存器、段寄存器和指令指针寄存器。两点说明,第一,这只是8086上电后的状态。第二,8086有三个段寄存器,ES是附加段寄存器,也是数据段寄存器,不够用。
取指令就要发出地址,当处理器加电或者复位之后,第一个地址是什么呢?加电或复位后CS的第一个地址是FFFF,IP的内容是0,8086将CS内容FFFF左移4位和IP内容相加,结果是20位的FFFF0。然后把地址送到地址线上去到内存取第一条指令。FFFF0位于内存的高端,距离内存的最顶端只有16个字节,8086就是从这里开始取指令执行指令的。
问题是刚刚加电或复位,整个内存中有指令可以执行吗?
复位或上电后,处理器立刻开始取指令和执行指令,这是工程师赋予它的本能。
被访问的内存不是一个整体,00000-FFFFF的连续地址范围会被地址分配电路分为几个独立的部分。
如上,00000-9FFFF的范围是可读可写的内存(内存条)。内存由字节组成,字节由比特组成,为节约成本提高容量和集成度,每个比特的存储是靠及其微小的晶体管和及其微小的电容来完成的。电容可以充电和放电,因此可以用充电和放电表示比特0和比特1。但是我们也知道电容很容易泄露电荷,当电容充电后放一边不管他,它就会慢慢放电,直至存储的电荷完全消失。可以想象组成这种内存的微小电容泄露电荷的速度是非常快的,通常会在几个微秒之内漏个一干二净。所以,这种内存需要定期补充电荷来维持原先内容,这个过程叫做动态刷新。因此这种存储器也叫动态随机访问存储器,也叫DRAM。随机访问是访问任何内存单元的时间和它的位置(地址)无关。
在内存刷新期间,处理器是无法访问内存的,不过,对于内存访问来说,刷新的时间是微乎其微的。可以错开进行。这种内存原理决定了在它断电之后所有保存的内容都会统统消失,同时当计算机开机的时候,这部分内存里也没有任何有效的数据和指令。处理器不能从这里取指令和执行指令,因为其中没有有效指令和数据。
如果8086发出地址在A0000-EFFFF地址区间,那么这些地址空间来自一些外部接口电路或者接口卡。如声卡、网卡等接口卡。计算机可以连接各种设备,但是他们不能直接接入,需要通过各自的接口电路或者接口卡接入。以显卡为例,显示器的工作方式和处理器不同,信号的规程的也一样,所以需要一个接口卡来做一个信号的匹配和转换,这个接口卡叫做显卡。显卡通过电缆连接显示器,给显示器发送视频信号,另外它还要插在计算机的主板上。显卡上有存储器,处理器可以把要显示的内容写入显卡上的存储器,然后显卡负责将它转换为视频信号发送给显示器,这样就能在显示器上看见文本了。
最后,如果8086发出地址在F0000-FFFFF地址范围,那么,8086访问的是一个特殊的存储芯片,叫 ROM BIOS。这种芯片只能用来读取而不能写入,所以叫做只读存储器,简称ROM,ROM的内容是用特殊手段预先写入的,如下图,是几种不同的ROM芯片。和内存条(DRAM)不同,只读存储器不需要刷新,它的内容是预先写入的,即使掉电也不会消失,但是也很难改变。有些ROM的内容在写入后永远无法擦除和重写,但是有些ROM可以用紫外线和加电来擦除和重写。ROM的内容可以预先写入,即使是关掉电源也不会消失,这个特点非常重要,比如可以将一些指令和数据固化在ROM中。
从8086处理器的视角来看,ROM占据着整个内存空间的顶端,地址范围是,所占用空间是64KB。内存条(DRAM)占据着较低端的640KB,范围是00000-9FFFF。中间还有一部分分给了一些外围设备,如鼠标、键盘、声卡等,地址范围是A0000-EFFFF,范围大小是320KB。
8086的第一条指令地址FFFF0在ROMBIOS中,正好固化了开机时需要执行的指令。这块ROM中,它的内容是可执行的程序,用来对组成计算机的基本模块进行诊断、检测、初始化。除此之外,有些硬件对于正常使用计算机来说是必不可少的,如:键盘、硬盘、显示器,要使用这些硬件需要编写复杂的程序,为了方便程序员使用这些硬件,这些ROM里还固化了一些程序供我们调用,我们可以通过这些现成的程序来读取输入或者向设备输出,至于如何调用这些程序后续课程讲解。无论如何,这些程序是对最基本的硬件编写的,所以这块芯片又叫做基本输入输出系统,简称BIOS。
问题在于FFFF0的内存单元距离顶端FFFFF只有16个字节,处理器取指令的方向是从内存的低端向高端推进,这是处理器取指令和执行指令的方向。当处理器从FFFF0取指令和执行指令时,很快就会执行到内存的顶端, 这16个字节放不了几条指令,也做不了什么事情。所以,这一小块内存,到底放的是什么指令呢?
FFFF0处存放的跳转指令,占用5字节,如下:
除非遇到跳转指令,否则指令执行一般都是由低地址端向高地址端推进。处理器的跳转动作是由跳转指令触发的。
在这条指令中,第一个字节EA是这条指令的操作码,它包含了几个方面的信息,第一,这是一条跳转指令,第二,跳转的位置紧跟在操作码后面, 分别是16位的偏移地址和16位的段地址。
那么,这条跳转指令如何使用汇编书写?JMP 0xF000:0xE05B(跳转到逻辑地址F000:E05B处取指令并执行)。JMP是英文单词 jump 的缩写。
当加电或复位后,执行第一条指令跳转指令。这条指令的功能是改变段寄存器CS和IP的内容。
执行后,CS和IP改变
紧接着, 和往常一样,将段寄存器CS内容左移4位,和指令指针寄存器IP内容相加,得到FE05B的物理地址并通过地址线送往内存取指令。
到FE05B向上取指令,这个部分依然是ROM BIOS范围,里面固化了开机诊断、 检测、初始化的程序。BIOS容量不大,存储不了太多东西。
因此,ROM BIOS里固化了一些指令,用来从外部的辅助存储设备读取指令到内存,然后让处理器加以执行,通俗的说,就是从U盘或者硬盘中读取程序,然后放入内存,然后继续执行。通俗来说,在硬盘或者U盘存放着操作系统的代码,所以是从这些存储设备里边读取操作系统。然后,执行操作系统的代码从而启动操作系统,于是就看到了Linux和Windows了。
如今,有了多种多样的外部存储设备(相对于内存来说的)。
处理器从内存取指令和操作数,外部存储设备是内存的后备存储设备。外部存储设备的内容只有被加载到内部存储设备中后才能被访问。所以外部存储设备是相对内部存储设备(内存)来说的。外部存储设备的典型例子是U盘和硬盘。
每个盘片有两个磁头,它们附着在盘片表面,上下各一个。通常我们用磁头指示盘面,因为每个盘面都有一个磁头。磁头都有编号,从第一个盘面的第一个磁头开始编号(如下图,依次01234…)。每个磁头并不是单独移动的,相反,它们都是通过磁头臂固定在一个支架上。磁头臂的支架用来固定磁头臂和磁头。所有的磁头都是通过磁头臂固定在一个磁头支架上,然后由步进电机带着一起在盘片中心和边缘移动。也就是说,它们是同进退的。
可以想象,当盘片高速旋转时,磁头每步进一次,都会从它所在的位置开始,绕着圆心画一个看不见的圆圈,这就是磁道。下图中画出了,其实磁道是看不见的。
磁道是数据记录的轨迹,因为所有磁头都是联动的,所以每个磁道的盘面上的同一条磁道又可以形成一个虚拟的圆柱,叫做柱面。如下图。
磁道或者柱面也要编号,编号是从盘面最边缘开始,向着圆心的方向从0开始编号,比如
对于其他盘面来说也是一样的。
柱面是一个用来优化数据读写的概念。初看呢用硬盘记录数据好像是先填写一个盘面,填满后再填写下一个盘面。实际上,移动磁头是一个机械动作,看似很快,但对于处理器来说非常漫长,这就是寻道时间。为了加速数据再硬盘上的读写,最好的办法就是保持磁头不动,如当这条磁道写满了,就写下一条磁道上,如果这条磁道还写满了,就继续把剩余数据写在二面的磁道上。这是大数据量的情况,话句话说,数据的访问是以柱面来组织的,移动磁头时间长,所以尽量把数据记录在同一柱面的相同磁道上。
实际上,磁道还不是硬盘数据的最小记录单位,磁道还能进一步划分位扇区。如下图。每一条磁道能划分多少扇区取决于制造商。但通常都是63个扇区,而且每个扇区都有一个编号,扇区和磁头的编号不同,扇区的编号是从1开始的,磁头和磁道的编号都从0开始。需要注意,每个磁道上,第一个扇区编号为1,最后一个扇区编号是63。
扇区之间是以空白(或称为间隙)间隔的。每个扇区以扇区头开始,然后有512字节的数据区。扇区头包含了每个扇区自己的(位置)信息,主要包含本扇区的磁道号、磁头号、本扇区自己的扇区号。这些信息用来供硬盘的定位记录使用。现在的扇区还会再扇区头包括一个指示扇区是否健康的标志,因为每个扇区在反复读写之后可能会损坏,这时可以指示这个扇区是否可以继续使用。
当ROM BIOS完成自己的使命之前,最后要做的一件事是从外部存储设备读取更多的指令,来交给处理器执行。现实情况是硬盘是首选的外部存储设备,下图中黄色部分是BIOS中执行的最后一段指令。
黄色部分代码是处理器离开ROM BIOS之前所执行的最后一段代码,这段代码要从主引导扇区读到07C00处,然后用一个跳转指令,跳到那里执行。
那么什么是主引导扇区呢?
在硬盘上,最上面的盘面是0面,或者叫0头(因为编号为0的磁头负责这个盘面)。在这个盘面最外侧的磁道是编号为0的磁道。在这条磁道上,编号为1的扇区(第一个扇区)是主引导扇区。换句话说,在硬盘上0面0道1扇区或者说0头0柱一扇区叫做主引导扇区。
如果计算机的设置是如硬盘启动,那么ROM BIOS将读取主引导扇区的内容,将它加载到内存地址07C00处,然后用一个跳转指令跳到该处继续执行。这个跳转指令是处理器离开BIOS时执行的最后一条指令,跳转指令的汇编语言形式是JMP 0x0000:0x7C00
通常主引导扇区的功能是继续从硬盘的其他部分读取更多内容加以执行,像Windows就是采用这种“接力”方式一步一步把自己运行起来的。
就是说在主引导扇区有一部分代码用来从硬盘的其他地方读取内容“接力”执行,这样的话,操作系统在硬盘的其他部分,而主引导扇区负责从其他部分来读取操作系统的代码,把它读到内存然后一步一步的“接力”执行,执行的最终结果就是操作系统已启动。
说到这,如果把自己写的编译好的程序写到主引导扇区,不也能让处理器执行吗?这是可以的。这是不依赖操作系统的让我们程序可以执行的唯一方法。不过坏消息是如果你改写了硬盘的主引导扇区,你正在使用的任何操作系统都会瘫痪,无法启动。怎么办呢?另一种方法,使用虚拟机,让其互不干扰。
虚拟机仅仅是一个软件,运行在各种主流的操作系统上,它以自己运行的真实计算机为模板,虚拟出另一套处理器、内存、外部设备,它的处理能力完全来自背后的真实计算机。尤其重要的是,针对某种真实处理器所写的任何指令代码,通常都可以正常无误的在该处理器的虚拟机上执行,实际上,这也是虚拟机具有广泛应用价值的原因之所在。在过去的若干年里,虚拟机得到了广泛的应用,为了研制防病毒软件,测试最新的操作系统或者软件产品,软件公司通常需要多台用于做实验的计算机,采用虚拟机就可以避免反复重装软件系统的麻烦,当这些软件系统崩溃时,崩溃的只是虚拟机,而真实的计算机丝毫不受影响。虚拟机利用软件来模拟完整的计算机系统,无需添加任何新的设备,而且与主计算机系统是隔离的,在虚拟计算机上的任何操作都不会影响物理计算机上的操作系统和软件。