〖2005-8-28〗〖翻译〗如何编写模拟器
原名: How To Write a Computer Emulator
作者: Marat Fayzullin
翻译: LuciferWon
注释: ⒈ 重译的原因是因为原来 Nelson Chou 的译文为繁体版不符合大陆人的习惯,而且,他翻译的有错误。
⒉ 不想向巴哈姆特网站申请转载,因为此站竟然将大陆的身份证号归到外国类中!反感!所以,重新翻译,而后会联系 Marat Fayzullin
本人。
⒊ 未经过作者和幻梦飞翔社区同意,不得转载本文!
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
译文如下
大量的想编写模拟器而又不知道如何编写的人给我发来了邮件,为此,我写了这篇文章。当然,以下文章中的各种想法和意见都是我个人的意见,不应被当作编写模拟器的唯一途径。文章主要以 "解释型(直译型)" 模拟器("Interpreting" Emulators) 为主对象,而与其相对的 "汇编型(编译型)" 模拟器("Compiling" Emulators),因为我没有足够的相关的汇编技巧的经验,所以未涉及。当然,我也在文章中放置了一两个你能找到相关信息的链接。
如果,你认为这篇文章遗漏了什么,或是你想要更正,请发邮件通知我。但是,我不回答争论性的、愚蠢的和向我索要 Rom 镜像的问题。也许,这篇文章的资源列表中我错过了一些重要的 FTP/WWW,如果你知道一些站点有重要的资料,请通知我,如果还有其他的 FAQ 不包括在本文的范围内,也请告诉我。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
前言
如何,你是否决定好要写一个模拟器软件?其实这很好,当然,也许这篇文章会帮到你,它包涵了编写模拟器时会遇到的常规性问题的解答。而且,他会为你描绘一幅模拟器内部运作的 "蓝图" ,让你在一定程度上有所遵循!
概要
什么才能够被模拟?
什么是 " Emulation " ?和 " Simulation " 有何不同?
模拟有专利的硬件合法吗?
什么是 "解释型(直译型)" 模拟器("Interpreting" Emulators) ?和 "汇编型(编译型)" 模拟器("Compiling" Emulators) 有何不同?
我要编写一个模拟器,我应该从什么地方开始呢?
我应该使用哪种程序语言?
我应该在何处找将要模拟的硬件的资料呢?
编写步骤
我应该如何模拟 CPU 的工作原理?
我应该如何处理被模拟的内存区的访问?
周期性: 这是什么?
程序技巧
如何优化 C 语言代码?
什么是 Low/High - Endiness?
如何是程序具有可移植性?
为什么我应该使程序模块化?
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
什么才能够被模拟?
基本上,只要内部具有微处理器的就可以。当然,仅仅一些或多或少有些弹性的程序才是我们感兴趣的,将要被模拟的。他们包括:
电脑
计算器
家用游戏机
大型街机
其他
注释你能模拟的任一计算机系统是十分必要的,即使它十分的复杂(例如: Commodore Amiga Computer),而且模拟效果也许不好。
〖翻译〗如何编写模拟器 »
传说中的'HOW TO MAKE AN EMULATOR"
--------------------------------------------------------------------------------
作者: zchou (Nelson Chou) 看板: Emulator
标题: 传说中的'HOW TO MAKE AN EMULATOR"
时间: Sun Aug 16 13:05:15 1998
********************************************************************
** **
** 好象有很多人看过英文版的,趁着有点时间,顺手翻一下.不过还没有 **
** 写给原作者,有点像盗版的,实在不太好! **
** 也许有地方翻的不好,主要是以意译为主,所以删改了一些句子,如果 **
** 有搞错原意的地方,还请各位多多辅助,为需要知道如何写模拟器的 **
** 人提供信息. **
** NELSON CHOU **
** **
********************************************************************
HOWTO:写一个计算机模拟器
禁止未授权散布.可以链接到这文件,但是不要复制它.
如何写一个计算机模拟器 - Marat Fayzullin
(How To Write a Computer Emulator by Marat Fayzullin)
我收到一大堆E-MAIL,来自想写模拟器但是不晓得从哪里开始着手的朋友,所
以我写了这个文件.以下的任何意见及建议都只是我自己的想法,不应该被当作
唯一写模拟器的方式!本文主要适用于直译式模拟器("interpreting" EMULATOR)
,与其相对的是编译式模拟器(" compiling " EMULATOR),因为我没有很多重编
译技巧(recompilation techniques)的经验!它也指引了一,两个可以找到这些
技巧数据的地方!
如果你觉得本文错过了什么或者哪里要更正,请E-MAIL给我你的意见!我不回
答激愤的,愚蠢的,和要求ROM IMAGE的问题.也许我错过一些重要的 FTP/WWW
地址在这文件的结尾,假如你知道足以放在那边的数据,请告诉我.如果有其他
FAQ不在本文内的,也请告诉我.
这文件也已经由Bero翻译成曰语.
http://www.geocities.co.jp/playtown/2004/howto_j.htm
前言
怎样,你决定写一个软件模拟器吗?很好的,这篇文件或许能给你一些帮助!它
涵盖了一些当人们问及如何写模拟程序时的通用技术性问题.它同时也提供你
模拟器内部运作的蓝图,让你多少能够根据循!
提纲:
什么能被模拟呢?
什么是" emulation "? 和"simulation "有何不同?
模拟有专利的硬件合法吗?
什么是"interpreting emulator"? 和"recompiling emulator" 有何不同?
我想要写一个模拟器.我该从哪开始?
我该使用何种程序语言?
我要从哪里找到被模拟的硬件的数据?
制作步骤
我该如何模拟一个CPU?
我该如何处理被模拟内存(emulated memory)的访问?
周期行程(Cyclic tasks):这是什么?
程序写作技巧
我该如何优化C代码?
什么是LOW/HIGH-ENDIANESS?
如何使程序具有可携性?
为什么我应该使程序模块化?
其他
什么能被模拟?
基本上,任何要被模拟的系统都有一个微处理器.当然,只有运行较具弹性或
较不具弹性程序的设备是我们感兴趣模拟的.包含:
计算机.
计算机.
家用游戏平台.
大型电玩.
其他.
记下你能模拟的任何计算机系统是必要的,即使它十分复杂(如Commodore Amiga
computer).虽然这样的模拟程序性能也许不太好.
什么是" emulation "? 和"simulation "有何不同?
" emulation "是意图模拟一项设备的内部设计."simulation "是意图模拟
一项设备的功能!譬如,一个程序能够模拟"小向导(PACMAN)"大型电玩硬件和
运行真正"小向导(PACMAN)"ROM,这个程序就是一个EMULATOR!一个为了你的电
脑而写的"小向导(PACMAN)"游戏,但使用图像类似于真正的大型电玩,这个游
戏程序就是一个 SIMULATOR!
模拟有专利的硬件合法吗?
这文件事其实是界于一个灰色地带,模拟有专利的硬件似乎是合法的,但是不包
含非法使用这些用于有专利硬件上的信息!你应该也发现到伴随着模拟器一起
散布SYSTEM ROMS(BIOS,或其他)是违法的,如果这些ROM是有版权的!
什么是"interpreting emulator"? 和"recompiling emulator" 有何不同?
有三种基本规划可以用以设计一个模拟器.他们也能相互结合以获得最佳效
果!
直译型(Interpretation)
一个模拟器运行的方式是从内存一个BYTE一个BYTE的读入被模拟的代码
,再解码这被模拟的代码,再运行适当的命令在被模拟的寄存器,内存,及I/O
,这种模拟器就称为直译型模拟器!这类的模拟器一般的算法如下:
while(CPUIsRunning) /* 利用一个检查CPU是否运行的旗标的无限循环 */
{
Fetch OpCode /* 抓取运算码 */
Interpret OpCode /* 解译运算码 */
}
这种方式的优点包含易于除错,具可携性,易于同步(synchronization,你
可以简化计算所经过的时序周期,并且固定你的模拟器在同一周期数的延迟)
明显的缺点就是性能.直译型模拟器花费相当多CPU时间,并且你可能需要
十分快的计算机来运行你的程序,才能获得尚可的速度.
静态重编译型(Static Recompilation)
在这个技巧中,你试图将一个由被模拟代码写成的程序,翻译成你的计算机所
使用的汇编语言码.结果将成为一个无须任何特殊工具就可以在你的计算机运行的
可执行文件.静态重编译型模拟器听起来似乎不错,不过这通常并不可行的!例如,
你不能完全先行编译会自己修正的代码(self-modifying code),因为不运行
这些代码,是不可能知道它们将会变成什么的!为了避免这种状况,你也许可
以将直译型模拟器或动态重编译型模拟器与静态重编译型模拟器组合起来!
动态重编译型(Dynamic Recompilation)
动态重编译型模拟器与静态重编译型模拟器在本质上是相同的,只不过动态
重编译型模拟器出现在程序运行时.为了代替一次就重编译好所有的代码,我
们可以只在遇到"CALL"或者"JUMP"命令时,立即再作一次重编译任务!为了增加
速度,这种技巧也能与静态重编译型模拟器组合使用.你能在这里找到更多有关
动态重编译型模拟器的数据:
http://www.ardi.com/MacHack/machack.html white paper by Ardi,
creators of the recompiling Macintosh emulator.
我想要写一个模拟器.我该从哪开始?
为了写模拟器,你必须要有良好的计算机程序设计及数码电子学的一般知识背
景.有汇编语言程序设计的经验亦十分重要.你先要做到:
选择所使用程序设计语言.
找到所有有关被模拟的硬件的数据.
重写一个CPU的模拟器或者取得已写好的CPU模拟器代码.
最少先部份地写一些草拟码来模拟硬件的一部份,由这点看来,在写模拟器时同
时作一个拥有允许暂停模拟及看看这个程序正在干嘛这种功能的小型内置除错
器,这样是非常有帮助的.同时你也需要被模拟系统所使用的汇编语言的反组译
程序.如果找不到现成的反组译程序,就自己作一个.试着在你的模拟器上运行
程序看看.使用反组译程序及除错器了解程序是如何使用硬件的,并且适当的调
整模拟器的代码.
我该使用何种程序语言?
最明显的选择方案是 C 及 汇编语言.
以下是他们的优缺分析:
汇编语言
优:1.可以产生较快速的代码.
2.用以模拟的CPU的寄存器可以直接用来保存被模拟的CPU寄存器的数据.
3.许多运算码可以用相似的用以模拟的CPU的运算码来模拟.
缺:1.这种代码没有可携性,也就是不能在不同架构的计算机上运行.
2.代码的除错与维护不太容易.
C
优:1.可以做成具可携性的代码,如此便能使用在不同的计算机及操作系统.
2.相对来说,代码的除错与维护较为容易.
3.可以很快的测试对真实硬件如何运作的不同假设.
缺:一般而言,C 代码通常比纯汇编语言代码慢一些.
对于写一个可运作的模拟器来说,对所选择的程序语言有深入的认识是绝对
必要的,因为这是十分复杂的计划,并且你的代码应该尽可能的优化使之执
行的更快!说的明白一点,作计算机模拟器不应是一个学习程序语言的计划.
我要从哪里找到被模拟的硬件的数据?
以下列出来的地方,你也许想要看看.
Newsgroups
comp.emulators.misc
这是一个有关计算机模拟一般性讨论的Newsgroup.许多模拟器作者也阅读这里
的讨论,不过这里的废话多了一点!在你想刊登问题在这个讨论区之前,请先看
看这里:
http://www.why.net/home/adam/cem/ c.e.m FAQ
comp.emulators.game-consoles
跟comp.emulators.misc差不多,不过特别注重家用游戏平台模拟器.在你想
刊登问题在这个讨论区之前,也请先看看这里:
http://www.why.net/home/adam/cem/ c.e.m FAQ
comp.sys./emulated-system/
comp.sys.*类阶层包含许多特殊计算机系统的讨论区.
藉由读这些newsgroups,你可以获得很多的有用的技术性信息.典型的例子如:
comp.sys.msx MSX/MSX2/MSX2+/TurboR computers
comp.sys.sinclair Sinclair ZX80/ZX81/ZXSpectrum/QL
comp.sys.apple2 Apple ][
etc.
郑重呼吁,先看看这些FAQ在你想在这些新闻讨论区发问之前.
Alt.folklore.computers rec.games.video.classic
FTP
ftp://x2ftp.oulu.fi/ ---- 包含游戏平台及游戏程序设计,
位置在 Oulu , Finland
ftp: // ftp.spies.com/ ----- 大型电玩硬件数据库
ftp: // ftp.komkon.org/pub/EMUL8/ ----- 计算机历史及模拟器数据库,
位置在 KOMKON
WWW
http://www.why.net/home/adam/cem/ comp.emulators.misc FAQ
http://www.komkon.org/fms/ My Homepage
http://valhalla.ph.tn.tudelft.nl/emul8/arcade.html
Arcade Emulation Programming Repository
http://www.classicgaming.com/EPR/
Emulation Programmer's Resource
〖翻译〗如何编写模拟器
我该如何模拟一个CPU?
首先,如果你只需要模拟一个标准的 Z80 或 6502 CPU,你可以用我写的CPU
模拟器! http:// www.komkon.org/fms/EMUL8/
请先确定一下使用条件,在加入它们之前.
对于那些想要写自己的CPU模拟核心或者对于CPU模拟核心感兴趣的人,以下
我提供一个典型C语言CPU模拟器.在真实的模拟器之中,你也许想要保留它的一
部份,或者加入一部份到你自己的CPU模拟器.
Counter=InterruptPeriod;
PC=InitialPC;
for(;;)
{
OpCode=Memory[PC++];
Counter-=Cycles[OpCode];
switch(OpCode)
{
case OpCode1:
case OpCode2:
...
}
if(Counter<=0)
{
/* 检查所发生的中断并运行这些中断 */
/* 周期行程(Cyclic tasks)置于这里 */
...
Counter+=InterruptPeriod;
if(ExitRequired) break;
}
}
首先,我们要给定一个初值到CPU周期计数器"Counter",及程序计数器"C"!
Counter=InterruptPeriod;
PC=InitialPC;
"Counter"包含CPU周期数到下一个可能的中断.注意那些当"Counter"到期时,
不需要发生的中断:你可以使用在很多其他用途上,例如将计时器同步化,或者更
新屏幕上的扫描线."C"包含我们所模拟的CPU将读入的运算码内存地址.
在初值给定后,我们就可以开始主要的循环:
for(;;)
{
也可以用这样的方式,
while(CPUIsRunning)
{
"CPUIsRunning"是一个布尔变量.这有相当的优点,你可以随时藉由设置
"CPUIsRunning"为"0"终止这个循环.不幸的,每次循环经过都必须检查这个变
数,这将花费很多CPU时间,应该尽量避免这样的方式.也不要做这样的循环:
while(1)
{
因为有些编译器会产生检查"1"是不是表示布尔"true"的代码.你一定不希
望编译器产生这种每经过循环一次都要做一次不必要任务的代码.
现在,我们进到循环了,第一件事就是先读入下一个运算码,同时更改程序计
数器"C".
OpCode=Memory [ PC++ ] ;
注意这里,虽然这是从被模拟内存中读入命令的最简单及最快的方式,不过
这不一定可行就是.更通用的访问内存的方式稍后会介绍.
在抓取完运算码之后,我们减少CPU周期数"Counter",减少的数量是这个运算码
所需要的周期数.
Counter-=Cycles [ OpCode ] ;
"Cycles [ ]"表格包含了每个运算码的CPU周期数.提防某些运算码可能因为
参数的不同而有不同的周期数,例如条件跳跃或者副程序呼叫.虽然在程序中稍
后能够再调整.
现在到了解译运算码及运行运算码的时候了:
switch(OpCode)
{
一般人有个误解,以为用switch()结构是没有效率的,因为这会编译成一串
if() ... else if() ...的叙述.当CASE很少时,这倒是真的.不过在大型结构
中(超过100-200个以上的CASE)会编译成一个"JUMP TABLE",这反而十分有效率
.
有两种选择方式来解译运算码.第一种方式是作一个函数表(FUNCTION TABLE)
并且呼叫适当的函数.这种方式比用 SWITCH() 还没效率,因为函数呼叫花费更
大.第二种方式是作一个选项卡表(LABEL TABLE)并且利用"GOTO"叙述.这种方式
比用 SWITCH() 有效率一点,不过只在编译器支持"预先计算选项卡(precomputed
labels)这项功能才能运行.有的编译器不允许你做一个选项卡地址数组(array of
label addresses).
在我们成功的解译并运行运算码之后,再来是检查需不需要任何岔断.在这个
时刻,你也可以完成任何需要与系统时钟同步的行程.
if(Counter<=0)
{
/* 这里运行检查岔断及完成其他硬件模拟的步骤 */
...
Counter+=InterruptPeriod;
if(ExitRequired) break;
}
这些周期行程(CYCLIC TASKS)稍后会帮助.
注意这里,我们并不是简单的使用" Counter=InterruptPeriod; ",而是使用
" Counter+=InterruptPeriod ":这将使得周期计算更为精密,因为在"Counter"
中可能负的周期数出现.
也注意这里:
if ( ExitRequired ) break ;
因为每经过一次循环就检查一次是否离开程序,这样满浪费的,我们只在
"Counter"到期时做这项任务:这样还是会离开模拟器,当你设置
"ExitRequired=1"时,不过这不会花费太多CPU时间.
我该如何处理被模拟内存(emulated memory)的访问?
访问被模拟内存最简单的方式是如同一块平坦的byte(words或其他)数组,
琐碎的处理它,如:
Data=Memory[Address1]; /* 从 Address1 读入 */
Memory[Address2]=Data; /* 写入到 Address2 */
如此简单的内存访问实际上并不总是可能的,原因如下:
分页内存(Paged Memory)
寻址空间可能是零散的在于可切换页(switchable pages,或称BANK).这常被
做为扩充内存的方式,当寻址空间是小的(64KB).
对映内存(Mirrored Memory)
一块内存区域可在几种不同的地址访问.例如,你写入到位置 $4000 将也
出现在 $6000 及 $8000.ROM 也可能被映射到不完全的地址解码.
ROM 保护(ROM Protection)
某些卡匣型的软件(如 MSX 游戏)会尝试写入到自己的ROM,并且在写入成功
时拒绝再继续运行.通常这是为了防拷.为了使这样的软件在你的模拟器上继续
运行,你应该阻止写入到ROM中.
Memory-Mapped I/O
在这种系统中,可能有 Memory-Mapped I/O 设备.访问到这样的内存位置
会产生"特殊效果",因此这样的访问方式应该要追踪.
为了应付这种问题,我们介绍两种映射的函数:
Data=ReadMemory(Address1); /* 从 Address1 读入 */
WriteMemory(Address2,Data); /* 写入到 Address2 */
所有特殊处理,如页访问,对映,I/O处理等等,都可以由这些函数处理.
ReadMemory() 及 WriteMemory() 两种函数在模拟过程中占用很大一部份,
因为他们很常被呼叫.所以,他们一定要尽可能的做得有效率.这里是用以访问
分页寻址空间的函数示例:
static inline byte ReadMemory(register word Address)
{
return(MemoryPage[Address>>13][Address&0x1FFF]);
}
static inline void WriteMemory(register word Address,register byte Value)
{
MemoryPage[Address>>13][Address&0x1FFF]=Value;
}
注意"inline"这个关键字.他告诉编译器将函数直接展开到代码,而不是以
函数呼叫的方式.如果你所用的编译器不支持"inline"或"_inline",尝试利用
"static function"来定义这样的函数:某些编译器(如 WATCOM C ),会以INLINE
的方式优化短的"static function".
在大部分的情况下,ReadMemory() 函数比 WriteMemory() 函数使用的频率
要高出许多倍,你也应该谨记在心.所以,把 ReadMemory() 函数代码尽可能
写得的比 WriteMemory() 函数短,这样是很值得的.
在内存对映(memory mirroring)上还有一个小小的重点:
有此一说,许多计算机都有对映RAM(mirrored RAM),你可以发现某个数值写入
到某个地址却也将会出现在其他地址.你可以在 ReadMemory()函数中处理这样
的状况,但这实在不太值得,因为 ReadMemory()函数呼叫的频率实在大过
WriteMemory() 函数(译按:原意应该是"因为有很多地址都能读到同一个数字
,所以只要读取一个地址就可以,不要读取好几个地址才决定这个数字,
ReadMemory() 函数使用的频率太多,多加入不必要的代码将严重损及效率")
更有效率的方式将是撰写 WriteMemory() 函数时加入内存对映的功能.
〖翻译〗如何编写模拟器
周期行程(Cyclic tasks):这是什么?
周期行程是在模拟机器中应该要定期处理的事情,例如:
屏幕更新(Screen refresh)
VBlank 及 HBlank 岔断
更新计时器(Updating timers)
更新音效参数(Updating sound parameters)
更新键盘/摇杆状态(Updating keyboard/joysticks state)
其他
为了模拟这些行程,你应运行在 2.5 MHz的?50Hz 的更新频率( PAL 图像盲郱?,
VBlank岔断应该以这样社W率发生
2500000/50 = 50000 CPU cycles
现在,如果我们假设屏幕是256条扫描线,但是只有212条扫描线是真的显示在
屏幕上的(i.e. 剩下44条被 VBlink 吃了),我们得到你的模拟器应当花费在更
新每条扫描线是
该以适当的CPU周期数固定它们.例如,如果CPU建议
速度,屏幕显示使术?50000/256 ~= 195 CPU cyles
在这之后,你就应该产生一次 VBlink 岔断,并且直到 VBlink 完成前,不做
任何事.(i.e.
( 256-212 ) *50000/256 = 44*50000/256 ~= 8594 CPU cycles
小心的计算每个行程所需的CPU周期数,然后把他们的最大公约数定成
"InterruptPeriod(中断周期)"并且固定所有其他行程到"InterruptPeriod"
(在每次"Counter"到期时,这些行程不需要运行).
我该如何优化C代码?
首先,正确的选择编译器所提供的优化选项能增加额外的代码性能.在我
的经验中,以下的选项组合可以给你最佳的运行速度:
Watcom C++ -oneatx -zp4 -5r -fp3
GNU C++ -O3 -fomit-frame-pointer
Borland C++
如果你发现有其他更好的选项组合,不管是以上的编译器或者其他不同的编
译器,请你告诉我.
一些循环展开(loop unrolling)的小小建议:
将编译器优化的"循环展开"选项设成"on"可能是很有用的.这项选项将尝
试转换短循环变成平滑的程序片段.我的经验显示,虽然,这项选项不会产生任
何性能改进.但是设置成"ON"时也可在某些极特殊的条件中中断你的代码.
优化C的原始代码比选择编译器选项稍微来得好,并且就一般而言,用编
译器编译代码这是一个与CPU有关的优化方式.几个一般性适用于所有CPU
规则如下.不要把它们的运作方式当做绝对的事实,因为你的用途可能不同:
使用程序分析工具(profiler)!
你的程序在程序分析工具程序(记住GPROF)下运行的情形会显示很多很好的
事情,是以前你从未怀疑的.你会发现,看起来微不足道的程序片段运行的频率
比其他部份多的太多了,并且使整个程序慢下来.将这些程序片段优化或者改
以汇编语言撰写会大幅增加性能.
避免使用C++
避免使用任何结构,这会强迫使用C++编译器代替C编译器去编译你的程序:C++
编译器通常会产生额外的代码.
整数的尺寸(Size of integer)
尝试只使用一种CPU所支持的基底整数尺寸,i.e. 整数(int)尺寸相对于
"short" 或"long"整数的尺寸.如果使用混合的整数尺寸,这将使编译器产生一
堆不同整数长度间转换的代码.这也会增加计忆体访问时间,因为某些CPU当
以基底整数读写数据时会因为对齐基本基底地址边界而使运行速度加快.
寄存器定位(Register allocation)
在每一区中,尽可能的减少使用变量,把最常使用的变量声明为寄存器变量"
register variable"(虽然大部分新的编译器可以自动地把变量变成寄存器变
数 ).这在拥有较多通用寄存器的CPU(PowerPC)比在只有少数专用寄存器的CPU
( Intel 80x86)更有意义.
展开小的循环(Unroll small loops)
如果你想要使小循环在很短的时间内运行,以手动的方式展开小循环成为平
滑的程序片段会是个好主意.看看上面有关自动循环展开的讨论.
字节移位(Shifts) VS. 乘/除法
当你需要乘或除以2的次方数时,应该以字节移位代替( J/128==J>>7 ).
在大部分CPU下,这些方式会运行得较快.同样的,使用字节"AND"运算代替取余
数的运算( J%128==J&0x7F ).
什么是LOW/HIGH-ENDIANESS?
所有的CPU一般都可分成两种,这与他们如何在内存保存数据有关.
High-endian CPU
这种CPU是把一个word的higher byte放在前面的保存方式保存数据.例如,如
果要保存 0x12345678 到这样的CPU,这块内存看起来会像这样:
0 1 2 3
+--+--+--+--+
|12|34|56|78|
+--+--+--+--+
Low-endian CPU
这种CPU是把一个word的lower byte放在前面的保存方式保存数据.跟前面一
样的例子在这种CPU会看起来非常不一样:
0 1 2 3
+--+--+--+--+
|78|56|34|12|
+--+--+--+--+
典型的High-endian CPU的例子是 6502 and 65816 ,Motorola 680x0 系列,
PowerPC ,及 Sun SPARC .Low-endian CPU包含Zilog Z80,大部分INTEL晶片(
包含 8080及80x86),DEC Alpha,等等.
当你写一个模拟器,你必须注意被模拟及模拟别人的CPU是何种ENDIANESS,我
们说,你想要模拟Z80 CPU,这是一个Low-endian CPU,也就是保存16-BIT WORD
时LOWER BYTE在前面.如果你使用的是Low-endian CPU(例如,INTEL 80x86),在
这个例子,每件事都发生的很正常.如果你使用的是High-endian CPU( PowerPC),
突然就出现了放置16-bit Z80数据到内存的问题.如果你的程序必须在两种
机器下运行,你需要某种方式感觉endianess.
一种处理 endianess问题的方法如下:
typedef union
{
short W; /* Word 访问 */
struct /* Byte 访问... */
{
#ifdef LOW_ENDIAN
byte l,h; /* ...在 low-endian 机器 */
#else
byte h,l; /* ...在 high-endian 机器 */
#endif
} B;
} word;
正如你所看到的,一个"WORD"的数据利用 W 可以整个访问.每次你的模拟程
式需要分开访问BYTE时,你可以使用 B.l 及 B.h 访问数据.如果你的程序将在
不同平台编译时,在运行任何真正重要的事情时,你可能先想要测试一下它是否
以正确的endianess方式编译的.
这里是这种测试的一种方式:
int *T;
T=(int *)"/01/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0";
if(*T==1) printf("This machine is high-endian./n");
else printf("This machine is low-endian./n");
如何使程序具有可携性?
正在写.
为什么我应该使程序模块化?
正在写.
copy1997-1998 Copyright by ..Marat Fayzullin
[fms@cs.umd.edu]