Many new emu authors begin writing their
emulators using the traditional giant switch statement. While this
is fine for simple CPUs, it becomes more trouble than its worth
once you start working with complex processors found in Next-Gen
consoles and similar advanced consoles. But an array of function
pointers is also applicable in many other ways to basic emulators.
This tutorial is designed to give beginning emulator authors a grasp
on why using an array of function pointers is better than using
a large switch statement, and present a few examples to show you
how they can be used.
You may be asking yourself right now, why would I want to choose
an array of function pointers over something that is much simpler
like a large switch statement. The answer is simple, and is found
in a few key reasons:
- Performance - An array of function
pointers is more often than not faster than a large switch statement,
this may not be true if you are using an optimizing compiler.
- Simplicity - An array of function
pointers is a more elegant solution to the problem of translating
CPU instructions, and its also much easier to sort through and
make changes to.
As you can see these reason may or may not be important to all emulators,
but they are valid reasons, and this is why I am going to show you
how to use an array of function pointers, first lets look at a little
source code :
//Lets Start Off With A Simple Chip8 Example
unsigned short opcode;
unsigned short PC;
void (*Chip8Table[17])();
void (*Chip8Arithmetic[16])();
////////////////////////////////////////////////////////////////////////////////////////////
void fetch()
{
opcode = ((memory->readmem(PC)<<8) + memory->readmem(PC+1))
PC += 2;
}
void execute()
{
//===================================
// Fetch Next Opcode And Execute It
//===================================
fetch();
Chip8Table[(opcode&0xF000)>>12]();
}
////////////////////////////////////////////////////////////////////////////////////////////
void cpuNULL()
{
// Do Nothing
}
////////////////////////////////////////////////////////////////////////////////////////////
void (*Chip8Table[17]) =
{
cpuNULL , cpuNULL, cpuNULL, cpuNULL, cpuNULL, cpuNULL, cpuNULL, cpuNULL,
cpuARITHMETIC, cpuNULL, cpuNULL, cpuNULL, cpuNULL, cpuNULL, cpuNULL, cpuNULL,
cpuNULL
};
|
Pretty simple, neh? Well it doesn't take much
more than that to emulate the entire Chip8 CPU. But wait, this previous
example does have a problem. There are instructions in the Chip8 such
as instruction 0x8000 that are not like normal instructions. Based
on a seperate parameter passed in with this instruction, its function
changes completely. This function table doesn't completely prevent
us from using a switch() statement now does it? Well, if you add another
function table it does so let's add this shall we
unsigned short opcode;
unsigned short PC;
void (*Chip8Table[17])();
void (*Chip8Arithmetic[16])();
////////////////////////////////////////////////////////////////////////////////////////////
void fetch()
{
opcode = ((memory->readmem(PC)<<8) + memory->readmem(PC+1))
PC += 2;
}
void execute()
{
//===================================
// Fetch Next Opcode And Execute It
//===================================
fetch();
Chip8Table[(opcode&0xF000)>>12]();
}
////////////////////////////////////////////////////////////////////////////////////////////
void cpuNULL()
{
// Do Nothing
}
void cpuARITHMETIC(){
Chip8Arithmetic[(opcode&0x000F)]();
}
////////////////////////////////////////////////////////////////////////////////////////////
void (*Chip8Table[17]) =
{
cpuNULL , cpuNULL, cpuNULL, cpuNULL, cpuNULL, cpuNULL, cpuNULL, cpuNULL,
cpuARITHMETIC, cpuNULL, cpuNULL, cpuNULL, cpuNULL, cpuNULL, cpuNULL, cpuNULL,
cpuNULL
};
void (*Chip8Arithmetic[16]) =
{
cpuNULL, cpuNULL, cpuNULL, cpuNULL, cpuNULL, cpuNULL, cpuNULL, cpuNULL,
cpuNULL, cpuNULL, cpuNULL, cpuNULL, cpuNULL, cpuNULL, cpuNULL, cpuNULL
};
|
Now as you can see, when the opcode 0x8000 is run across, it instead
executes a function which will execute a function in the Arithmetic
table. This is a simple way to replace those complex double switch
statements, and hey its even faster.
|