|
What follows is my method for emulating a microprocessor.
This topic has been covered many times on the net, but not necessarily
using my specific method. Instead of explaining the basics
of the execution loop, I am going to explain how to simulate the
hardware such as video memory, ports and bank switched ROM/RAM.
Since modern PCs have plenty of memory compared to those from 15
years ago, we are afforded the luxury to "waste" a bit.
The way I implement 8-bit CPU memory maps is to allocate 3 x 64K
(64K for ROM, 64K for RAM and 64K for flags). The flags map
is used to implement non-standard features. A 0 indicates
normal RAM, a 1 indicates ROM and any other number indicates the
'handler' procedure used to work with that address range.
This way, for any arcade game we can have up to 254 unique handler
routines to deal with all of the hardware. Shown below is
a sample function for reading a byte of memory taken from my 6809
emulator:
unsigned char M6809ReadByte(unsigned
char *m_map09, unsigned short usAddr)
{
unsigned char c;
switch(c = m_map09[usAddr+MEM_FLAGS])
{
case 0:
return m_map09[usAddr+MEM_RAM];
break;
case 1:
return m_map09[usAddr+MEM_ROM];
break;
default:
return (mem_handlers09[c-2].pfn_read)(usAddr);
}
}
Each unique flag number has a pair of handler
routines associated with it - one for read and one for write.
The handler routine would get called with the address in question
and return or store the appropriate value. For example, to
implement memory mapped video, the address range for the video RAM
would be marked with the handler number for video RAM and get called
each time a read or write occurred in that range. This way,
time is not wasted in a general memory handler checking for each
address range individually. This method is faster than the
method used by some other emulators simply because there are no
unnecessary address comparisons. This method also solves the
problem of ROM corruption; the MAME implementation of Joust had
this problem because the program was allowed to write onto ROM.
Some games actually try to write into ROM for protection.
Using the above method, bank switched memory is also easy to implement.
For example, in the Williams games the video RAM is mapped to the
same address space as some of the ROM. This is handled by
marking that area to use a handler routine which checks the state
of the bank switch and acts appropriately. Shown below is
a sample handler routine used for the Asteroids bank switch (player
1/2 score info):
void AstBankSWWrite(unsigned
short usAddr, unsigned char ucByte)
{
register unsigned char c;
int i;
c = ucByte & 4;
if (c != (mem_map[MEM_RAM+usAddr] & 4))
{
for (i=0; i<0x100; i++)
{
c = mem_map[MEM_RAM+0x200
+ i];
mem_map[MEM_RAM+0x200
+ i] = mem_map[MEM_RAM+0x300+i];
mem_map[MEM_RAM+0x300+i]
= c;
}
}
mem_map[MEM_RAM+usAddr] = ucByte;
}
Webdesign
by Deep Magic Studios
- HanaHo Games, Inc. Copyright © 2002 |