HOME
PROJECTS
LOOK AT MORE TUTORIALS
Interpreting CPU Instructions via an Array of Function Pointers
Author: zenogais Difficulty Level: Intermediate


    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.