Mike's blog about random software stuff

Rust and Emulators part 2: 6502 processor

A couple of weeks ago, I decided to start learning some Rust, and started by creating a chip8 emulator. In my previous blog post , I mentioned I was planning to emulate something like a NES in the future. And that’s what I have started doing now. My journey towards a working NES emulator starts by creating a 6502 processor emulator.

I spent a few weekends writing the 6502 emulator, and before long, I had a first version up and running. There are probably countless bugs in this initial version of the emulator and the code is a bit of a mess, but I managed to run some simple programs with it anyways. Here’s how I started my implementation.

Resources

The main resources I used for my first implementation were the NESdev wiki and a great video series by David Barr, aka javidx9, aka OneLoneCoder. The information sorted out by the people behind the wiki really made the programming a lot faster than I would have thought. Also, David Barr does a really nice job of explaining how everything works. Many thanks to these people for creating these resources for everyone to enjoy!

Starting by parsing the instructions

I started by creating a HUGE match pattern that parses all of the legal instructions into an Instruction struct. The 6502 processor can execute 56 different types of instructions and each of them can use a set of 13 different types of addressing modes. Besides of all the official combinations of these instructions, there are also illegal instructions for all the different possible 256 values representable by an 8-bit number. I decided to only parse the legal ones, and parse the other ones to return a NOP instruction.

To represent a single instruction, I created the following struct:

pub struct Instruction {
    pub value: u8,
    pub itype: InstructionType,
    pub addressing_mode: AddressingMode,
    pub cycles: u8,
    pub bytes: u8,
}

The value field represents the actual 8-bit number of the instruction, and the other ones are parsed by the match statement using said value. There wasn’t really anything more to this than just carefully looking up all the values from the instruction reference guide. Not much to say about that, just a lot of manual copying the data to form my structs. This took a long time, I spent quite a few hours getting everything copied.

Executing instructions according to the parsed data

After being able to parse the instructions, I had access to the actual instruction and it’s addressing mode. It was time to create a few more lookup tables again based on this information.

I created a CPU class that’s job is to read a value from memory using a program counter, use the previously created code to parse the Instruction from read memory, and then to execute the instruction.

Luckily, the different kinds of addressing modes and instruction types could be handled separate from each other. The addressing mode tells our CPU where the data that we are using exists in. For example, it could state that we should operate on the accumulator, which is simply a register in the 6502 CPU. Or, it could be a memory location pointed by the two following bytes in the memory. If the addressing mode stated that we’ll operate on the ram memory, I parsed and saved the address. If the addressing mode stated that we should operate on a register, I did nothing.

One thing the about the addressing mode is, that it may require additional CPU cycles if the memory we are using exists on a different memory page than we’re currently on. So, when handling the addressing mode, I returned a value for the rest of the program that contained information about the need for an additional clock cycle.

After parsing the memory location we’re operating on, I was able to start implementing the actual instructions. Again, this called for a huge lookup table, selecting a function based on the itype field in out Instruction struct. Implementing the instructions wasn’t really that different from the chip8 instructions, there were just a lot more of them. Again, there were instructions that could require additional cycles to run them. I returned this information from each of the instruction implementations like I did with the addressing modes.

Some of the instructions required a quite a bit of thinking. When do I need to set a bit in the status register, when do I need to clear them, things like that. In the current version there are most likely a bunch of instructions setting the wrong bits, or setting them incorrectly, but I guess I’ll need to fix them later.

After creating a simple implementation for all of the 56 different operations, the first version of the emulator was almost done. Finally I just needed a quick way to check that at least something is working. I achieved that by writing print functions for all of the register states, status flags, and the values in the ram, and copied a short assembly program to the CPU memory. I set the program counter of the CPU to that location and made the program execute an instruction every time I press enter.

The printed values ended up looking something like this:

Instruction: LDX, Addressing Immediate
Memory:
$0x00:  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00
$0x10:  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00
Program memory:
$0x500:  0xa2  0x0a  0x8e  0x00  0x00  0xa2  0x03  0x8e  0x01  0x00  0xac  0x00  0x00  0xa9  0x00  0x18
$0x510:  0x6d  0x01  0x00  0x88  0xd0  0xfa  0x8d  0x02  0x00  0xea  0xea  0xea  0x00  0x00  0x00  0x00
Registers: 
PC: 0x502
A: 0x00
X: 0x0a
Y: 0x00
SP: 0x100
C: 0, Z: 0, I: 0, D: 0, B: 0, V: 0, N: 0

Instruction: STX, Addressing Absolute
Memory:
$0x00:  0x0a  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00
$0x10:  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00
Program memory:
$0x500:  0xa2  0x0a  0x8e  0x00  0x00  0xa2  0x03  0x8e  0x01  0x00  0xac  0x00  0x00  0xa9  0x00  0x18
$0x510:  0x6d  0x01  0x00  0x88  0xd0  0xfa  0x8d  0x02  0x00  0xea  0xea  0xea  0x00  0x00  0x00  0x00
Registers: 
PC: 0x505
A: 0x00
X: 0x0a
Y: 0x00
SP: 0x100
C: 0, Z: 0, I: 0, D: 0, B: 0, V: 0, N: 0

A huge wall of text that isn’t really too nice to look at, but just enough to verify that at least the simple program I added was working. Not much, but a good starting point to get going with the rest of the system.

Next, I’ll need to study the next steps, and start creating more parts or the NES system.

Here’s the the source code, in case you’d like to have a look. Happy hacking!