October 16, 2022

I've recently been messing around with the MOS 6502 processor which was used in many retro systems of the 1980s, however, what's annoyed me is that there isn't really an easy way just to mess around with code and see what happens. The closest thing is the Virtual 6502 which is decent, but it's online which means you have to deal with uploading files and such, and it also doesn't really have enough configuration options for my taste. Considering I like D and that I couldn't find a DUB package that already emulated the 6502, I decided to make my own emulator, which is currently called Emu6502 (very original name, I know). It supports most features of the 6502, with the exception of a few small things (which are listed under the TODO section of the readme). Here's an example program:

module example;

import std.stdio;
import emu6502;

void main() {
  ubyte[0x10000] memory;
  ubyte[] code = [
    0xA2, 0x00,       //   lda #0
                      // loop:
    0xBD, 0x0E, 0x80, //   lda data,x
    0x8D, 0x00, 0xE0, //   sta $E000
    0xC9, 0x00,       //   cmp #0
    0xE8,             //   inx
    0xD0, 0xF5,       //   bne loop
    0x00,             //   brk
                      // data: .asciiz 'Hello, World!\n'
    0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x2C, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x0A, 0x00
  ];
  // put code into memory
  foreach(i, b; code)
    memory[0x8000+i] = b;
  // set reset vector
  memory[0xFFFC] = 0x00;
  memory[0xFFFD] = 0x80;
  // create emulator
  auto emu = new Emu6502(
    (ushort address) {
      return memory[address];
    },
    (ushort address, ubyte value) {
      if(address == 0xE000)
        write(cast(char)value);
      else
        memory[address] = value;
    },
    (ubyte n) {}
  );
  emu.reset();
  emu.throwExceptionOnBreak = true;
  // run until brk triggered
  try {
    while(true)
      emu.step();
  } catch(BreakException) {}
}

Links: Github Repo DUB Package