JavaScript & the Strategy Pattern

Just recently I started working on a web-based NES emulator, creatively named TixNES, after my first TixBoy Gameboy Emulator project. The idea of this project, as well as the Gameboy one, is that it’s implemented in pure browser JavaScript - no libraries, no frameworks, no nothing. However, quite early during development of the emulator CPU, I came across a small hurdle.

Compared to the Gameboy, the NES has much, much fewer different instructions. However, it has a bunch more addressing modes for each instruction. For example - there’s 5 variations of the LDX instruction, the only purpose of which is to load a value into a register. Even though every variation effectively does the same thing, just slightly differently, I’d have to implement every variation manually. At least that’s what I thought until I came up with an amazing solution that utilizes the Strategy Pattern… sort of.

JavaScript features this amazing function called Function.bind(), which allows for creation of pointers to wrapper functions which, when called, call the original function with parameters specified in the .bind() call. For example:

var print = console.log.bind(null, "Hello World!");
print();

This bit of code creates a print variable which can be called, and when that happens, Hello World! is printed to the console, as that’s the parameter given in the parameters of .bind(). (You can safely ignore the null, that’s just a required parameter for context of the this keyword)

But how is this useful, you may ask? Well, it already proved to be useful in my Gameboy Emulator project. Instruction decoding is always a pain, and I didn’t feel like writing a giant switch-statement with over 500 cases for each instruction, so I created a “funcmap” object, which maps the opcode values to function pointers created using the .bind() function. Now instruction decoding is as easy as funcmap[opcode]()! My NES emulator takes the same approach. However, in order to support all these different variations of instructions, there’s two layers of .bind() objects. And this is where the Strategy Pattern fun begins.

Let’s get back to the LDX instruction as an example. The basic operation of it is always going to be the same - load a value from memory and store it in register X. Therefore, I implemented a “general” function of the LDX instruction which takes two parameters: a “load function” and a “callback flag”. The load function is a pointer to one of the “load function implementations” - effectively where the Strategy Pattern aspect comes in. There’s an implementation for immediate values, then a separate implementation for absolute addressing, etc. This load function then takes a callback function that’s called when the memory read operations are all done and the read value is stored in a temporary buffer. My LDX function sets itself as the callback function, but using .bind() to call itself with the “callback flag” parameter set, so it knows that there’s a value in the temporary buffer that should be written to the register X. That explanation may be a bit convoluted, so here’s some example code:

// This is a temporary buffer in a global scope to store values between function calls
var tmp = [];

// Reads an 8-bit value at PC, increases PC and immediately calls the callback function
function _read8_immediate(callback) {
    tmp.push(readByte(registers.pc++));
    callback();
}

// LDX Instruction - Loads a value from memory into register X
function _ldx(loadfunc, cycle) {
    switch(cycle) {
        default:
        	// This is executed on the first call - the load function is
			// scheduled to be executed on the next CPU cycle
            nextfunc = loadfunc.bind(this, _ldx.bind(this, null, 1));
            break;
        case 1:
        	// This is executed as a callback from the load function.
            registers.x = tmp.pop();
            nextfunc = fetchInstruction;
            break;
    }
}

// Map Immediate-addressing variant of LDX to opcode 0xA2
funcmap[0xA2] = _ldx.bind(this, _read8_immediate);

(For context in the code block above - nextfunc is called every CPU cycle)

And that’s all there is to it! Now I can re-use my _read8_immediate function for other instructions that support reading immediate values, and I can add new addressing variations of LDX by simply adding a new entry in the funcmap with a different function pointer in the .bind() call. This is especially useful because it implicitly handles all the timing-related stuff in the load functions, so there’s no risk of making mistakes due to copy & pasting code all over the place.

Written on June 22, 2021