简化的8088CPU逻辑功能的仿真实现
1.无需注册登录,支付后按照提示操作即可获取该资料.
2.资料以网页介绍的为准,下载后不会有水印.资料仅供学习参考之用.
密 惠 保
项目要求
综合运用以前所学的专业知识,设计开发一个简化的8088CPU功能模拟的演示软件;在设计与开发中,将CPU与存储器整合在一起的模型,1.实现CPU的指令系统 2. 实现CPU的时序逻辑
think58.com
2 VC++6.0的编程
2.1 MFC编程
MFC用类编写Windows应用程序的C++类集,以层次结构组织起来,其中封装了大部分Windows API函数和 Windows控件,而基础的虚拟平台就是在次基础之上而开发出的。使用MFC类库和Visual C++提供的高度可视的应用程序开发工具,可是应用程序开发变的更简单,开发周期极大地缩短,提高代码的可靠性和可重用性。它提供的类库对程序设计的高度抽象,使得程序员不用放在程序设计的具体细节上,而是对功能的扩展上,大大简化了开发工作。在开发过程中,我们所做的就是编写封装器件的程序,由平台调用之来显示出控件,除了编写一些相关消息响应函数外,大部分工作由系统完成。
2.2 DLL相关知识
比较大的应用程序都由很多模块组成,这些模块分别完成相对独立的功能,它们彼此协作来完成整个软件系统的工作。可能存在一些模块的功能较为通用,在构造其它软件系统时仍会被使用。在构造软件系统时,如果将所有模块的源代码都静态编译到整个应用程序 EXE 文件中,会产生一些问题:一个缺点是增加了应用程序的大小,它会占用更多的磁盘空间,程序运行时也会消耗较大的内存空间,造成系统资源的浪费;另一个缺点是,在编写大的 EXE 程序时,在每次修改重建时都必须调整编译所有源代码,增加了编译过程的复杂性,也不利于阶段性的单元测试。Windows 系统平台上提供了一种完全不同的较有效的编程和运行环境,你可以将独立的程序模块创建为较小的 DLL (Dynamic Linkable Library) 文件,并可对它们单独编译和测试。在运行时,只有当 EXE 程序确实要调用这些 DLL 模块的情况下,系统才会将它们装载到内存空间中。这种方式不仅减少了 EXE 文件的大小和对内存空间的需求,而且使这些 DLL 模块可以同时被多个应用程序使用。Windows 自己就将一些主要的系统功能以 DLL 模块的形式实现。 think58
3 8088CPU介绍
3.1. 概述
8088是8080和8085的改进型,像8080和8085一样,它的指令是以字节为基础构成的。它的性能的提高,主要依赖于采取了以下一些特殊措施。
1. 建立4字节的指令预取队列
2. 设立地址段寄存器
3. 在结构上和指令设置方面支持多微处理器系统
一般处理器与8088CPU处理器指令过程见图表1 think58好,好think58 [资料来源:http://think58.com]
内容来自think58 [来源:http://www.think58.com]
内容来自think58
图表 1 一般处理器与8088处理器指令执行过程对比
(a)一般处理器指令执行过程;(b)8088处理器指令执行过程
3.2 8088CPU引线及其功能
8088CPU是一块具有40条引出线的集成电路芯片,其各引出线的定义如图22所示。为了减少芯片的引线,有许多引线具有双重定义和功能,采用分时复用方式工作,即在不同时刻,这些引线上的信号是不相同的,其管脚引线如图表2。
[资料来源:THINK58.com]
[来源:http://think58.com]
think58.com [来源:http://www.think58.com]
[来源:http://think58.com]
copyright think58 [资料来源:http://THINK58.com]
think58.com [版权所有:http://think58.com]
think58好,好think58 [资料来源:http://think58.com]
copyright think58
[资料来源:THINK58.com]
think58好,好think58 [资料来源:http://THINK58.com]
图表 2 8088处理器芯片引线图
3.3 8088CPU的内部结构
8088微处理器内部分为两个部分:执行单元(EU)和总线接口单元(BIU),如图表3所示。
copyright think58
think58.com [资料来源:http://THINK58.com]
[资料来源:www.THINK58.com]think58.com
[资料来源:http://THINK58.com]
[来源:http://think58.com]
think58.com [来源:http://think58.com]
copyright think58 [资料来源:http://www.THINK58.com]
think58 [资料来源:http://www.THINK58.com]
think58好,好think58
图表 3 8088CPU的内部结构
内容来自think58 [来源:http://www.think58.com]
8088处理器中的内部寄存器用户能用指令改变其内容的,主要是一组内部寄存器,其结构如图表4所示。
1)数据寄存器
8088有4个16位的数据寄存器,可以存放16位的操作数。
2) 指针寄存器
8088的指针寄存器有两个:SP和BP。SP是堆栈指针寄存器,由它和堆栈段寄存器一起来确定堆栈在内存中的位置。BP是基数指针寄存器,通常用于存放基地址,以使8088的寻址更加灵活。
3)变址寄存器
SI是源变址寄存器,DI是目的变址寄存器,都用于指令的变址寻址。
4)控制寄存器
8088的控制寄存器有两个:IP,PSW。IP是指令指针寄存器,用来控制CPU的指令执行顺序。
图表 4 8088CPU的内部结构
think58.com [资料来源:www.THINK58.com]
[版权所有:http://think58.com]
3.4 时序
在8088CPU中,CPU与内存或接口间进行通信,如将一个字节写入内存一个单元(或接口),或者从内存某单元(或某接口)读一个字节到CPU,这种读(或)写的过程称为一个总线周期。
copyright think58 [资料来源:http://THINK58.com]
[资料来源:THINK58.com]
3.5 简化的8088CPU模型概述
本设计的设计目标是设计一个简化的8088CPU模型,要求模型体现8088CPU的主要功能,省略掉其很多繁琐的细节。基于此考虑,简化模型主要从如下几个方面对8088CPU作了简化:
1 取消8088里的取指令、执行指令流水线
2 取消8088里的指令预取对列
3 取消8088里管脚的分时复用功能
4 将存储器与CPU融合,简化存储器读写总线周期
5 对8088的管脚也重新进行了设置。
管脚设置如图表5所示,CPU内部功能结构如图表6所示:
图表 5简化8088CPU管脚分布 think58.com [来源:http://www.think58.com]
图表 6 简化8088CPU内部功能结构
3.6移植bochs仿真实现8088CPU功能的基本思路
CPU指令系统和时序逻辑是本设计难点,而我们解决难点采取的办法是移植虚拟机bochs,它包含了8088下所有CPU指令的仿真函数实现。而且还专门预留了一个插槽库供扩展Bochs使用。Bochs很庞杂,也很少文献资料,了解Bochs成为我们前期的工作。
4 Bochs介绍
4.1 Bochs概述
Bochs是一个X86的模拟器,它可以模拟几乎所有类型的X86 CPU,包括16位,32位和64位(X86_64)。一个64位的X86可以看作是32位和16位X86的超集。X86_64的工作方式包括以下两类:
1,IA-32模式:包括实模式,保护模式和虚拟8086模式三个子模式。
2,IA-32e模式:包括长模式和兼容模式。
在Bochs中,用BX_CPU_C类来模拟CPU,它支持上面提到的每一种模式。
4.2 类 BX_CPU_C 介绍
4.2.1 主要的数据成员
char name[64],代表CPU的名字。
unsigned bx_cpuid,CPU的ID号,用于SMP(对称多处理)的机器。
bx_gen_reg_t gen_reg[BX_GENERAL_REGISTERS],bx_gen_reg_t是一个复杂的结构体,它表示一个64位的通用寄存器,其主要成员有:rrx:表示一个完整的64位寄存器。hrx、erx:分别表示64位寄存器的高32位与低32位,这时候64位的寄存器被分割成2个32位寄存器使用。
rx:表示erx的低16位,此时寄存器被当作16位寄存器使用。
rh、rl:分别表示rx的高8位与低8位,此时rx被分割成2个8位寄存器。
gen_reg就是一个代表通用寄存器的数组,BX_GENERAL_REGISTERS是寄存器的个数,如果模拟的是64位处理器,那么BX_GENERAL_REGISTERS值为16,否则值为8。64位时通用寄存器分别是RAX、RCX、RDX、RBX、RSP、RBP、RSI、RDI、R8~R15;32位时没有R8~R15 。
copyright think58
Bit64u rip; Bit32u eip;
分别是64位模式和32位模式下的指令指示器。
bx_segment_reg_t sregs[6];
bx_segment_reg_t是表示段寄存器的结构,sregs[6]是表示了6个段寄存器的数组。分别是ES、CS、SS、DS、FS和GS。 内容来自think58 [版权所有:http://think58.com]
BX_MEM_C *mem;
mem是指向这个CPU所使用的内存的指针 。
bx_local_apic_c local_apic;
bx_local_apic是模拟本地APIC的类,用于SMP系统。
unsigned cpu_mode;
就是前面提到的CPU的工作模式,主要有5种,分别是实模式(16位模式)、8086虚拟模式(32位模式下的虚拟16位模式)、保护模式(32位模式)、兼容模式(64位模式下的虚拟32位,16位模式)和长模式(64位模式)。分别使用宏BX_MODE_IA32_REAL、BX_MODE_IA32_V8086、BX_MODE_IA32_PROTECTED、BX_MODE_LONG_COMPAT和BX_MODE_LONG_64表示。
4.2.2 主要的成员函数
void cpu_loop(Bit32s max_instr_count)
cpu_loop是一个非常重要的函数,所有的指令函数都在这里执行。参数max_instr_count表示cpu_loop执行的最大指令数。
think58.com
[来源:http://think58.com]
[资料来源:THINK58.com]void prefetch(void)预取指令函数,计算指令的物理地址和其他相关信息,为后面的指令译码作准备。
unsigned fetchDecode(Bit8u *iptr, bxInstruction_c *instruction,unsigned remain)
unsigned fetchDecode64(Bit8u *iptr, bxInstruction_c *instruction,unsigned remain)
这两个函数用于对指令进行译码,前者按指令长度32位译码,后者按64位译码,参数意义分别是指令的物理地址,空白指令结构和指令长度的最大字节数 think58
[资料来源:http://THINK58.com]
void boundaryFetch(Bit8u *fetchPtr, unsigned remainingInPage, bxInstruction_c *i)
X86的最长指令可以达到15个字节,因此指令可能出现跨页,此时prefetch()+ fetchDecode()/fetchDecode64()失败,必须调用boundaryFetch()函数进行跨页取指译码。 copyright think58
[资料来源:http://www.THINK58.com]
4.3 BX_CPU_C 类对CPU工作流程的简易描述
CPU工作流程见图表7
copyright think58 [资料来源:http://www.THINK58.com]
[资料来源:THINK58.com]
内容来自think58 [版权所有:http://think58.com]
[版权所有:http://think58.com]
内容来自think58 [版权所有:http://think58.com]
think58好,好think58
[资料来源:http://THINK58.com]
图表 7 CPU工作流程示意图
prefetch()函数(指令预取)
void BX_CPU_C::prefetch(void)
{
计算laddr //指令所在的线性地址
计算paddr //经过地址翻译得到的指令的物理地址
计算laddrPageOffset0 //指令所在的线性页(虚页)的基地址
计算eipPageOffset0
//RIP - (laddr - laddrPageOffset0),指令所在页基 地址相对于CS的偏移
计算eipPageBias // - eipPageOffset0
计算eipPageWindowSize //页大小,值为4096;
计算pAddrA20Page //pAddr & 0xfffff000,指令所在的物理页的基地址
计算eipFetchPtr
//通过getHostMemAddr()计算得到的指令所在页在主机(真实机器)上的基地址
}
所谓指令预取,就是计算指令的物理地址和其他相关信息,为后面的指令译码作准备。表8是其功能流程:
[资料来源:http://www.THINK58.com]
[来源:http://www.think58.com]
[资料来源:www.THINK58.com]
本文来自think58 [资料来源:www.THINK58.com]
[资料来源:http://THINK58.com]think58.com [资料来源:http://www.THINK58.com]
copyright think58 [资料来源:http://think58.com]
图表 8指令预取功能流程图
fetchDecode()函数(指令译码):
指令的结构用一个类指令结构bxInstruction_c 来表示,它的两个主要成员函数下:
void (BX_CPU_C::*ResolveModrm) (bxInstruction_c *) BX_CPP_AttrRegparmN(1);
//获取指令的类型,类型是由bochs定义的
void (BX_CPU_C::*execute)(bxInstruction_c *);
//指令对应的执行函数指针
基本指令放在一个数组中:
static BxOpcodeInfo_t BxOpcodeInfo[512*2]
第0~511项: 16bit mode
第512~1023项: 32bit mode
(其中包括了fpu,x86-64,3DNOW,SSE等指令入口) 内容来自think58
[来源:http://www.think58.com]
boundaryFetch()函数(跨页边界取指)
void boundaryFetch(Bit8u *fetchPtr, unsigned remainingInPage, bxInstruction_c *i)因为指令跨页,不能将指令的首地址作为参数传给fetchDecode()/fetchDecode64(),因此设立临时变量fetchBuffer,用以保存在两页中取出的共15个字节,并将fetchBuffer作为译码的参数。指令的最大长度为15字节,但具体每条指令的长度是不定的,在本次取指过程中为了实现跨页移动了RIP,因此返回之前必须将其复原,函数返回以后,在cpu_loop()里面,会根据实际的指令长度移动RIP。
4.4 基本内存系统
主要文件:Memory.h, Memory.cc,Misc_mem.cc think58好,好think58
主要类:bx_mem_c [资料来源:www.THINK58.com]
内存块基指针:
Bit8u *actual_vector;//实际分配的内存块指针
Bit8u *vector; //经过对齐处理(4K大小整数倍 )
think58好,好think58 [版权所有:http://think58.com]
[资料来源:THINK58.com]
内存大小
size_t len; //以字节为单位
size_t megabytes; //以兆字节为单位
内存空间的分配:
alloc_vector_aligned (size_t bytes, size_t alignment) think58 [资料来源:www.THINK58.com]
内容来自think58 [资料来源:THINK58.com]
[资料来源:www.THINK58.com]
图 4-3 内存空间分配图 本文来自think58
内容来自think58
[资料来源:http://think58.com]
[来源:http://www.think58.com]图表 9内存空间分布图
X86处理器的分页机制(相关寄器):
CR0:当CR0[PG](表示CR0寄存器的PG位,下同)=1时,启用分页机制。
CR3:X86处理器通常使用多级页表,CR3中存放着最高级页表的基地址。
CR4:当CR4[PSE]=1时,使用4M/2M大小的页面,否则使用4K大小的页面;CR4[PAE]=1时,启用PAE模式(支持36位地址空间的物理内存)。 [来源:http://www.think58.com]