Saturday, January 17, 2015

An overview of writing a 6502 emulator

Overview

If you are interested in writing an emulator for a CPU, this is a good guide on getting started. If your interested in emulation, starting out with a simple emulator makes more sense. If you eventually want to write a more complicated emulator, the 6502 is simple enough that it isn't overwhelming, yet complex enough that it contains a good number of concepts that a more complicated CPU would have.

Basic Components and Concepts of the 6502

There are a few components and concepts of the 6502 that you will need to understand. This is just a brief overview of concepts, and there are many wonderful informative guides about these concepts. I recommend reading from several sources to get a deeper understanding

Stack:
This is a special location in the memory between $0100 and $01FF. The stack is a LIFO (Last In First Out) type of object. Its used as a temporary storage location. There is no protection against overflows. Its also fixed in place. The stack starts at the top of memory.

Registers:
Registers are used to hold small bits of information in the CPU. They are usually extremely fast to use, and are used to perform math operations, etc. They may also hold special processor flags, or pointers and counters to different places in memory.
  
Addressing:
The 6502 has 13 different ways that memory can be addressed.

Instructions:
The 6502 has 56 official instructions or OP Codes. The instructions are what carry out the work.

Memory:
This isn't actually a part of the 6502, memory would be a separate set of chips in a real device. For the purpose of an emulator though, the memory is considered a part of the CPU. There are a few special places in memory. there is a maximum of 64kb of memory, and all of the values in memory are values from 0-255.

The first 256 bytes of memory is the Zero Page memory.
The next 256 bytes of memory is the stack.
The last 6 bytes of memory contain 3 special addresses. At $FFFA - $FFFB is the Non-Maskable Interrupt address $FFFC-$FFFD is the power on reset address. and $FFFE - $FFFF contains the BRK/IRQ address.  These addresses are used by the CPU to find the code that gets executed when one of those specific events occur.

The rest of the memory can be divided up however the hardware dictates.


Where do you begin?

The hardest part, at least for me, was figuring out where to begin. There is a lot of information to absorb, and it will take some time to build up a mental model about how things work. The documentation online will only take you so far, and you are likely to encounter gaps.

 You are going to start by writing a method that performs a single CPU Operation. Mine ended up looking like this.


     Inside the ExecuteOpCode(); is a giant switch statement that switches on the CurrentOpCode value. You will want to write code that implements each instruction inside your switch statement. Start with the easy addressing modes, and make sure they work before doing the more difficult ones. The bulk of the work is going to be getting those instructions working correctly, if the behavior deviates at all from the real CPU, it won't work correctly.

You will also need to implement all of the Processor Flags and Registers an make sure they are being set and cleared. The Overflow and Negative flags are probably the most confusing flags, and I remember them giving me some trouble when dealing with Addition and Subtraction. 

Other Functionality

There is some other functionality you will need to add in order for things to work correctly. You will want to emulate the PowerOn and reset functions. You will also need to implement Interrupt Requests and NonMaskableInterrupts. 

You also are going to want some set of tooling to verify that things are working correctly. I wrote a really fancy test harness that shows all of the information at a glance, allows me to load and save the state of my program, set breakpoints, etc. You don't need to get this fancy, but it doesn't hurt, especially if you have limited knowledge of emulation and assembly language.

How will you know it works?

You need to test and then test some more. I ended up with just under 1000 unit tests. Most of my tests just loaded an opcode into memory and ensured that it worked correctly. Once you have implemented all of the opcodes, your next focus will probably be on some integration testing. I used a wonderful and very thorough test program by Klaus Dormann https://github.com/Klaus2m5/6502_65C02_functional_test that he wrote to test 6502 hardware that he was building. There are other test programs too. If your goal is emulating a specific device, there may be additional specific test programs.


Useful Links

http://visual6502.org/wiki
http://homepage.ntlworld.com/cyborgsystems/CS_Main/6502/6502.htm
http://www.atariarchives.org/alp/
http://en.wikipedia.org/wiki/MOS_Technology_6502
http://www.obelisk.demon.co.uk/6502/
http://www.6502.org





No comments :

Post a Comment