Writing a 6502 Emulator in .NET

The following is an implementation of a very simple 6502 emulator in C#. It's simple for a good reason. This emulator will become my "test" 6502 platform with which to write a code editor, compiler and debugger for in Visual Studio 2010. I thought it might be interesting to talk about doing a basic 6502 emulation, so here's the details...


Writing a basic 6502 emulator in .NET is actually very easy. And of course, this means then that there's not a whole lot you can do with it... at least in real practical terms.

The 6502 on its own doesn't do very much - and also doesn't come with any real usable memory, so we can cheat a little and "attach" 64K of (16-bit addressable) memory as though it was native and directly accessible.

So what 6502 properties do we need to model?
  1. The Program Counter (PC) - a 16-bit pointer to the next instruction in memory that will be "executed".
  2. The Stack Pointer (SP) - a 256-byte value that points to a virtual stack of memory (which for the C64) begins at memory location $01FF and goes down to $0100. 
  3. The Status Register - a single byte which contains a bit-wise view of the B, C, D, I, N, V & Z status flags. 
  4. The A, X & Y registers - single bytes
  5. Some RAM - a 64K byte array 
That's all the properties we need. Now we need to add the behaviour of a running 6502 - which in it's simplest form is this:

  1. Fetch next instruction (pointed to by the PC)
  2. Increment PC to next memory location 
  3. Execute fetched instruction (this is the guts of the work - many things can happen here)
  4. Repeat

So we have a chip that has properties and behaviours. Sounds like a C# class doesn't it?

The class diagram to the left shows my basic implementation. Let's look at the Methods (behaviours) - and let's see what it takes to execute a basic "program".

First - we initialise the CPU.

Our PC points to the first memory location at $0000

A program needs to be stored in memory somewhere. We can just do a direct manipulation of the Ram[] byte array - and hand code our machine langue into it. Then we point the PC to the start of the program, and begin the execution cycle.


The above is just a VS 2010 Test Fixture that loads $A9 (LDA) into memory location 0, and $FF (255) into memory location 1. That's our simple ML program, when we run it, the A register should go from 0 to 255.

Let's look at the run method.


It's pretty basic - run a continuous loop until the isExecuting flag is set to false. On each iteration, it fetches the next opcode from memory (pointed to by the PC) and sends it to the execute method. This next method is where it all happens. What to actually do with each instruction. It's in this method that we'd implement the behaviour of each and every 6502 OpCode, and importantly, how the 6502 behaves depending on the addressing mode (how to handle the bytes following the opcode). Initially, I've only implemented a couple of OpCodes. Here's how I've done it.


So for each OpCode - we have a giant switch statement. When we match on an OpCode the 6502 simulation just does what a normal 6502 would do. In this first case, our $A9 LDA command (immediate addressing) - just looks to the byte in the next memory location and loads it into the accumulator. So we do a GetNextMem command (which will read the memory at location 1 - remember the PC is auto-incremented whenever we read using this method). The byte is read out of location one, and then stored in the A register. When the next OpCode is read, it'll be $00 - which our emulator currently takes to mean (BRK) - and so isExecuting is set to false.

OK that's that! A bare bones 6502 emulator that:

  • Has no compiler or assembler (we write directly into memory using hand coding)
  • Has no disassembler
  • Has no graphics, sprites, sound, interrupts or anything like that
  • Is not clock cycle driven. There's not emulation of a physical chip and how clock cycles run. We just run our .NET code to effectively behave like a chip.
But all that's OK... because we want something really simple to drive our primary goal, and that's the development of our VS2010 tooling.

(I'll put this code up on Google Code at some point - as I continue to expand it).

1 comment:

  1. Did you ever complete this project, like the straight forward approach

    ReplyDelete