2008-03-13

colem_gba - A simple CPU simulator

为了继续看看CPU Emulator的资料,我决定选择Z80 CPU作为扩展点(我想也许Z80指令简单比较容易快速看明白吧).我找到了一个叫colem_gba的压缩包,这是一个典型的、架构在Z80 CPU上的Emulator的案例.
==========================================
首先了解一下colem_gba大概是做什么的
colem这个词,懂这个的人一般都这么写ColEm(ColecoVision Emulator) - ColEm is a portable emulator of the old ColecoVision videogame console written in C.ColecoVision是一种始于80年代的家庭插卡式电视游戏机.

gba means GameBoy Advance,是任天堂于2001年3月21日发售的便携型游戏机,可兼容大部分以前的Game Boy和Game Boy Color的游戏,一般简称为GBA.

Briefly, colem_gba是一个Coleco和GBA游戏仿真器,它运行在Z80 CPU上. 它可以在Unix和Dos上编译运行.

下面再了解一下colem_gba source code的overview:
- The ColEm source code distribution includes C sources for a portable Coleco emulator and screen/keyboard drivers for Unix/X and MSDOS.

- COLECO.ROM file in the current directory. This ROM contains ColecoVision startup code and BIOS. It must be 8192 bytes long. ColEm won't run without it.

- CART.ROM file in the current directory. CART means cartridge. 就是我们常说的卡带(魂斗罗32合1卡带,呵呵). 我想CART.ROM里面想必就是游戏内容了吧.
==========================================
最后,开始读代码(之前先看了看Makefile,没什么特别的)....
File: gba.c
/*
初始化环境
启动emulation
退出并清空相应的资源

*/
int main(void)
{
UPeriod=4;
monstartup(0x8000000,0x80fffff);
InitMachine();
Verbose=5;
StartColeco("CART.ROM");
TrashColeco();
TrashMachine();
return 0;
}

/*
- Allocate memory
* Allocating 64kB for CPU address space
* Allocating 16kB for Video address space
- Load ROM image
* Load COLECO.ROM at 0x0000 - 0x2000, 8k size; (内存的低端)
* Load CART.ROM at 0x8000 - 0x10000, 32k size; (内存的高端)
- Initialize hardware
* Initialize VDP registers
* Reset SN76489 PSG
* Reset Z80 registers
** PC register: 0x00
** SP register: 0xF000
- Run code fetched from COLECO.ROM(0x0000 - 0x2000) on CPU

*/
int StartColeco(char *Cartridge);

/*
这个函数对于了解CPU Emulator工作机制来说是最关键的,作为预备知识,必须对Z80 CPU有所了解(特别是Z80 CPU Registers)
所以我先找了一些Z80的资料,对照着该代码对Z80 CPU的定义,了解清楚再看这个函数的具体实现...

*/
word RunZ80(Z80 *R);

/*
Z80 CPU用208 bits R/W内存作为寄存器提供给程序员用,其中包含了Stack Pointer, Program Counter,2个Index register,1个REFRESH register,1个INTERRUPT register
这208 bits可以有几种寄存器组合,比较典型的是下面这种:
AF,BC,DE,HL,4个主寄存器,16-bit;前8-bit主要用来存放算术或者逻辑运算结果,后8-bit是一个标志位寄存器 - 比如标示运算结果是否为零;
IX, IY: 2个Index register.16-bit;主要用来处理Table类型的数据;
PC(Program Counter): 程序计数器,16-bit,存放着目前执行指令所在的地址 - The PC contains the memory address from which our emulated CPU will read its next opcode
SP(Statck Pointer): 栈寄存器,16-bit,
AF1,BC1,DE1,HL1,4个补充寄存器(Complementary registers),16-bit;前8-bit主要用来存放算术或者逻辑运算结果,后8-bit是一个标志位寄存器;(为啥叫)
IFF,I:中断寄存器,8-bit;用来存放中断地址的高8位;
R:刷新寄存器;8-bit;每取一次指令,该寄存器加1;据说该寄存器是对程序员透明的;
*/
typedef struct
{
pair AF,BC,DE,HL,IX,IY,PC,SP; /* Main registers */
pair AF1,BC1,DE1,HL1; /* Shadow registers */
byte IFF,I; /* Interrupt registers */
byte R; /* Refresh register */

int IPeriod,ICount; /* Set IPeriod to number of CPU cycles */
/* between calls to LoopZ80() */
int IBackup; /* Private, don't touch */
word IRequest; /* Set to address of pending IRQ */
byte IAutoReset; /* Set to 1 to autom. reset IRequest */
byte TrapBadOps; /* Set to 1 to warn of illegal opcodes */
word Trap; /* Set Trap to address to trace from */
byte Trace; /* Set Trace=1 to start tracing */
void *User; /* Arbitrary user data (ID,RAM*,etc.) */
} Z80;

RunZ80函数的实现让我大失所望- 和以前看过的那个"如何实现一个CPU模拟器"文章中写的原型差不多.没有考虑过多的CPU细节,比如时钟,模拟器中是指令的执行驱动时钟,真实的CPU上是时钟驱动指令的执行.
每条之指令的处理都在Codes.h里面.暂时没时间和需求让我一一细看.

No comments: