So, this could turn out to be quite the series of blog posts.
Having done a bunch of Advent of Code puzzles and a friend's computer science homework that involved "emulating" a machine, I had the crazy idea the other day to write a proper emulator myself.
Easy-peasy, right? If you really boil it down to the basics (ignoring many great features of modern languages) an emulator is essentially, at it's core a big switch statement... right?
Short answer: No.
Basically, there is a massive difference between implementing a well thought-out theoretical virtual machine and emulating a processor designed in the 1970s.
Anyway, because I'm a glutton for punishment, I cracked on.
I discussed testing in my previous post, so I won't go there again. For now, anyway. I guess I just want to post about some other details related to implementing something like this.
Tedium...
I have always been passionate about coding, and probably always will be. When I'm creating something with code and in the flow, time seems to slip away. However...
Churning through a massive list of instructions to emulate, each with details such as setting flags after the operation etc... has become a bit of a grind.
Anyway, due to a bout of insomnia caused by a couple of factors I won't go into, I have indeed been grinding away at this task.
This is the current result of running the test suite I mentioned.
Basically, I have implemented all of the one-byte opcodes. By this I mean excluding operands. So, LD HL, nn is a one-byte opcode in my mind, though the entire instruction would be three bytes.
This is because the processor will interpret 0x21 as LD HL, nn. Then it will need to get the data for nn, which will be the next two bytes.
So this is a three byte instruction, but a one byte opcode.
One byte... 0 to 255. In the Z80 instruction set, most of these values are used as opcodes, but there's a few gaps. These are used to indicate that there's another byte to read before deciding upon the operation.
As mentioned, if the processor encounters 0x21, it knows this is LD HL, nn and it needs to read the next two bytes to put into the HL register pair.
To expand the instruction set capability without having to use more than one byte to represent every instruction, the chip designers decided to use what I'll call an "indicator" byte to signify that it isn't an instruction, but the next byte will effectively be an instruction from a different list.
In the Z80, I think there's about 3 or 4 of these (sounds vague, but I'll know for sure once fully implemented).
One example is 0xDD. 0x21 is LD HL, nn to the processor. If prefixed with 0xDD though, this is now interpreted as LD IX, nn.
Anyway, long story, short... There's more than the ~250 operations I expected to need to emulate, many of which have documented and undocumented nuances.
What's keeping me grinding? Well, the hope in a few weeks (probably months TBH), I can wire everything together and see this, knowing I wrote the code to make it happen...
Watch this space I guess...
