Skip to content

Month: May 2018

Writing a NES emulator in Javascript: Part 2 (starting on the CPU)

Let’s start with the most obvious choice: The CPU. The NES’s 2A03 CPU is a modified 6502 processor. There’s an interesting story behind the choice of processor:

“The Nintendo core processor was a 6502 designed with the patented technology scraped off, We actually skimmed off the top of the chip inside of it to see what it was, and it was exactly a 6502. We looked at where we had the patents and they had gone in and deleted the circuitry where our patents were.”

Although there were changes, the NES microprocessor ran 99% of the 6502 instruction set. “Some things didn’t work quite right or took extra cycles,”

One important difference is the removal of the decimal mode, which was patented technology. The flag still exists, but is permanently disabled.

The focus of this implementation was to get code working, and so no effort is being made to optimise anything. For instance, some of the bitwise function are implemented using string operations and will be switched over later.

In order to test against a reference, I’m using nestest.nes which the docs describe as follows:

This here is a pretty much all inclusive test suite for a NES CPU. It was designed to test almost every combination of flags, instructions, and registers. Some of these tests are very difficult, and so far, Nesten and Nesticle failed it. Nintendulator passes, as does a real NES (naturally). I haven’t tested it with any more emulators yet.

I attempted to check the states of all flags after most instructions. For example, CPY and CMP shouldn’t affect the overflow flag, while SBC and ADC should. Likewise, all forms of wrapping ARE tested for- zeropage wrapping being the tests most emulators fail.

Starting the code

The log output for nestest.nes from Nintendulator is used as a reference. The CPU is represented by a class that contains the processor logic and attributes for the various parts of the CPU:

 

class CPU {

  constructor() {
    this.memory = new Memory;
    this.registers = {PC: 0, SP: 0, P:0, A:0, X: 0, Y: 0};
    this.flags = {carry: false, zero: false,
      interrupt_disable: true, decimal_mode: false,
      break_command: true, overflow: false, negative: false};
    this.running = false;
    this.cycles = 0;
    this.logger = new Logger;
    this.stack = new Array;
    this.log("CPU Initialized");
  }

Memory access is split out into its own class which contains functionality for getting and setting memory. The NES includes memory mapped I/O Implementing memory mapping in the CPU involves mapping particular addresses to different hardware elements. The fetch() function of the memory class accomplishes the memory mapping for retrieval detailed here:

 

  fetch(addr) {
    if(addr >= 0 && addr <= 0x7FF) {
      // 0000-00FF Zero-paged region
      // 0100-01FF - Stack Memory
      return this.ram[addr];
    } else if (addr >= 0x800 && addr < 0x0FFF) {
      // mirrors RAM
      return this.ram[addr-0x800];
    } else if (addr >= 0x1000 && addr < 0x17FF) {
      // mirrors RAM
      return this.ram[addr-0x1000];
    } else if (addr >= 0x1800 && addr < 0x1FFF) {
      // mirrors RAM
      return this.ram[addr-0x1800];
    } else if (addr >= 0x2000 && addr < 0x2007) {
      // NES PPU registers
    } else if (addr >= 0x2008 && addr < 0x3FFF) {
      // Mirrors of $2000-2007
    } else if (addr >= 0x4000 && addr < 0x4017) {
      // NES APU and I/O registers
    } else if (addr >= 0x4018 && addr < 0x401F) {
      // APU and I/O functionality
    } else if (addr > 0x4020 && addr < 0xFFFF) {
      if (addr >= 0xC000 && addr < 0xFFFF) {
        return this.rom[(addr-0xC000)];
      }
    }

  }

We'll get to how the ROM part works in the next article.

I opted to implement the opcodes as a giant switch() statement, which while not optimal will get the job done. An alternative approach I could've used was the one Imran Nezar used of creating an object containing all the operations as values, with the opcodes as the keys.:

  execute() {

    this.log("Beginning execution at " + this.registers.PC);
    this.running = true;

    while(this.running == true) {

      let opcode = this.memory.fetch(this.registers.PC);
      switch(opcode) {

      case ops.NOOP:
        let nb1 = this.memory.fetch(this.registers.PC+1);
        let nb2 = this.memory.fetch(this.registers.PC+2);
        let j = utility.Utility.merge_bytes(nb1, nb2);
        this.log("JSR " + j, this.registers.PC);
        let bytes = utility.Utility.split_byte(this.registers.PC+3);
        this.stack.push(bytes[0]);
        this.stack.push(bytes[1]);
        this.registers.SP -= 2;
        this.registers.PC = j;
      break;
      case ops.TSX:
          [...]
      break;
      case ops.TXS:
          [...]
      break;
      case ops.TYA:
          [...]
      break;

In the next article I'll talk a little about how to load a ROM file and how to get the code booting.

Writing a NES emulator in JavaScript: Part 1

Warning: these may be some long articles

Introduction & Inspiration

I’ve always wanted to write an emulator, I’ve found the concept fascinating, and I thought it was finally time to complete a emulation project. I went with the NES for the console, and JavaScript as the language. The reason being the NES is well documented, there are functional emulators out there with the ability to provide debugging output, and I owned a NES myself.

 

About Emulators

If you’re unfamiliar with how emulators work, aside from that they let you play videogames on your computer, the basic explanation is that the software you’re using is a code version of the original concept. Instead of the NES hardware reading and acting on the instructions contained in the ROM code on the cartridge, a program handles the CPU instructions, creates its own RAM, and maps graphical output instructions to visual output on your screen. This is incredibly complex, and hard to get the hang of, but it’s also a lot of fun, and provides some interesting problems to solve.

The height of technological progress

For instance, the NES hardware is a little idiosyncratic, and often restrictive in what it allowed programmers to be able to do. As a result, NES game programmers learned to exploit aspects of the hardware in very interesting ways, relying on the exact hardware implementation’s quirks to produce the result. This could mean that a “perfect” emulator that runs each component exactly may not run games properly.

The Hardware

Let’s take a look at the NES hardware. The NESdev wiki has been an amazing resource so far, and it’s a great place to start from. In these really early stages, gathering an idea of the hardware to be implemented and a rough sketch of how things fit together would be useful.

The goal of the first phase was to get a rough outline of the code and get a few instructions working. In order to do this a few things need completing:

  1. Basic CPU emulation What flags and registers does the CPU use? How does it calculate clock cycles for synchronization?
  2. Memory Mapping How do different parts of hardware map to addresses in memory? (this is the primary way that the CPU communicates with other hardware parts)
  3. Basic ROM Loading. Where is the executable data stored in the data from the cartridge?
  4. Ability to debug instructions Let’s be real, I have no clue what I’m doing with a lot of this, I’m going to need something to check against when I get stuck.

The CPU

The NES CPU core is based on the 6502 processor and runs at approximately 1.79 MHz. Reading this PDF helped immensely. The 6502 has the following registers:

  • A Accumulator Register
  • X, Y Index Registers
  • SP Stack Pointer
  • P Program Counter
  • S Status Flag

At its most simplest, a CPU is only really doing a few simple things. We fetch a byte from memory, decode the opcode, and take some action based on it. And then we do it again. And again. Until we have a reason not to. The CPU is where I’m going to start, and in the next post I’ll talk a bit about how that’s implemented.