Thursday 23 February 2017

Part 1: Booting a NES cartridge

Foreword

This is the first part in my series on writing a NES emulator in JavaScript.

Since the NES game console also had had a 6502 compatable CPU under cover, we can utilise a lot from my C64 JavaScript emulator on GitHub.

So, in this post I will take my source from my my C64 JavaScript emulator on Github and strip it down to a minimum. This strip down version will be our starting point for our JavaScript NES emulator series.

But, before we start with our stripping down exercise in this post, we be looking at the NES cartridge image format. I think this will be a good starting point.

The cartridge image will be targeting in this series will be PaperBoy.

I will end this post by single stepping through a couple of instructions of the NES cartridge.

I would have preferred that by the end of this post be able to boot a NES cartridge at full speed and then just look at some memory locations if the appropriate welcome was written. However, being new to the whole NES arhitecture I wouldn't be able to interpret at this point the contents of screen memory to tell whether the correct startup message was written.

So for now single stepping as end goal for this post will need to do.

The iNES file format

Most of the classic NES games you can get for download on the Internet in a format called iNES.

The iNES format was originally created by Marat Fayzullin when he wrote one of the first NES emulators.

Ever since then, the iNES format has become in someway the defacto standard for providing NES games for NES emulators.

The most important part of the iNES file is the 16 byte header at the start of the file.

Here is a very brief description of the 16 byte header:

  • 0-3: Constant $4E $45 $53 $1A ("NES" followed by MS-DOS end-of-file)
  • 4: Size of PRG ROM in 16 KB units
  • 5: Size of CHR ROM in 8 KB units (Value 0 means the board uses CHR RAM)
  • 6: Flags 6
  • 7: Flags 7
  • 8: Size of PRG RAM in 8 KB units (Value 0 infers 8 KB for compatibility; see PRG RAM circuit)
  • 9-15: Zero filled
I would like to point out that sometimes bytes 9-15 is used to embed extra flags, which I will not be covering in this post.

I will also not be covering in detail what flags Flags 6 and Flag 7 contains. I would, however, like to mention that the upper nybbles of Flags 6 and Flags 7 represent the mapper number. More on mappers later on.

Following the 16 byte header is the actual cartridge data. The data starts with a number of 16KB PRG ROM segments as specified by byte 4 of the header.

Following the PRG ROMS is a number 8KB CHR segments as specified by byte 5 of the header.

Dissecting the PaperBoy cartridge image

As mentioned previously, we will be using PaperBoy as the target game cartridge for this blog series.

So, let us start right away by looking at the 16 byte header of the PaperBoy game cartridge image:


You can see in the ASCII section the header starts physically with the word "NES".

The next piece of relevant information is that byte 4 indicates that this cartridge contains 2 blocks of 16KB program ROM. This is equals 32KB of total program ROM. The NES console have allocated memory addresses 8000h-ffffh for program ROM, So these two program ROMS segments can fit within the memory address space without an issue.

We are, however, not so lucky with the character ROMS. The NES console can only accomodate 8KB of character ROM. In Byte 4, however, we see that this game has 4x8KB character ROMs!

This is where mappers come in, as mentioned earlier on. A mapper is a chip that basically switch sections of ROM in and out of view, very similar to what the makers of the Commodore 64 did with banking.

It is interesting to note that this mapper chip doesn't reside within the NES console, but rather within the game cartridge itself!

Putting the mapper chip within the cartridge provides some freedom by not being restricted to a particular banking scheme, while keeping the overall architecture of the NES console simple.

These mapper functions will need to be emulated and in effect we will need to know which mapper we are dealing with when emulating a NES game cartridge.

The iNES file format gives us this information via the high nibbles of byte 6 and 7. The number formed by these nibbles is an index to a list of mappers origanlly provided by Marat Fayzullin. Here is a very short extraction of the list:

iNES Mapper NumberMapper Name
0NROM, no mapper
1Nintendo MMC1
2UNROM switch
3CNROM switch
4Nintendo MMC3
5Nintendo MMC5

Let us now see which mapper our game cartridge uses.

The upper nybbles of byte 6 and 7 forms the number zero. Number 0 corresponds to NROM or no mapper, within the list.

This is very confusing, because with this mapper we will not have access to all CHR ROM banks.

This confusing mapper actually confirms other observations I had when trying to play PaperBoy on other NES emulators. All the intro screens would display fine, but when getting to the main game screen you would only see ascii characters displayed. The following screenshot taken from the John NES Lite emulator illustrate this:


We fix this behaviour by just modifying the PaperBoy game image with the correct mapper number.

From the list of mappers I found that CNROM (aka Mapper Number 3) is the correct one. This mapper only provide bank switching capability for CHR ROM banks.

Well, I think we got enough information from the header. Let us start with some coding!

Starting simple

As mentioned previously, I will be using code of C64 emulator as baseline, stripping out C64 specific stuff.

Stripping out the C64 stuff, we are only left with the following files:

  • Cpu.js
  • Memory.js
  • index.html
Our index.html page will look as follow:


For now I have stripped out the screen canvas.

We also need to modify the logic for attaching an image as highlighted in red. In our C64 emulator we used this to attach a tape image. We need to modify the code to cater for a NES cartridge image.

The code behind the attach button within index.html looks as follow:

...
<button onclick="attachCartridge()">Attach</button>
...
      function attachCartridge() {
        mymem.attachCartridge(document.getElementById('file').files[0], mycpu);        
      }
...

And the implementation of attachCartridge within Memory.js is as follows:

  this.attachCartridge = function(file, cpu) {
       var reader = new FileReader();
       reader.onload = function(e) {
         var arrayBuffer = reader.result;
         var cartridgeData = new Uint8Array(arrayBuffer);
         var i = 0;
         var posInData = 0x10;
         for (i = 0x8000; i < 0x10000; i++) {
           mainMem[i] = cartridgeData[posInData];
           posInData++;
         }
         cpu.reset();
         alert("Cartridge attached");
       }
       reader.readAsArrayBuffer(file);

  }


We pass the file that the user selected as parameter and initiate an asynchronous read at the end of the method. When reading is finished the onload callback is invoked loading the contents of the file within the memory array between locations 8000h and ffffh.

Everything the emulator needs is contained within the cartridge image and is therefore no need to load additional ROMS in the background as we did with our C64 JavaScript emulator.

Because we don't need to load additional ROM files in the background, we strictly speakig don't need to serve our emulator from a web server. You should be able to open the emulator directly from your local file system.

There is one final change we need to make to the writeMem function within Memory.js:

  this.writeMem = function (address, byteval) {
    if (address >= 0x8000)
      return;
    mainMem[address] = byteval;
  }


With this code we avoid writes within the program ROM region which is within the region 8000h and ffffh.

You might be wondering why would a program bother to write to the ROM region. Well, you can expect this behaviour when working with NES mappers.

A NES mapper chip intercepts writes to the ROM region and takes the value written as an indication which ROM bank should be made visible.

Since the game we emulate only have bank switching enabled for CHR ROM, we will not worrying about implementing bank switching for now. We only need to ensure that writes to the Program ROM region don't override the ROM contents, which we did with the above if statement.

This is the only coding needed for this post!

A Test Run

As mentioned previously we will only be single stepping in this post.

To get a feel on what instructions gets executed here is quick list of the first couple of instructions you will encounter when single stepping:

804a STA $2000
804d STA $2001
8050 STA $0b
8052 SEI
8053 CLD 
8054 LDX #$ff
8056 TXS 
8057 JSR $813e
813e LDA #$17
8140 STA $8a

Not very exciting, but we will get there!

In summary

In this post we had had look the iNES file format.

We also created a stripped down NES emulator and managed to single step a couple of instructions of a game cartridge.

In the next post we will be running our NES emulator at full speed, intercepting writes to the IO ports of the video chip.

We will then examine these writes and determine what we should implement next for emulation.

Till next time!

Monday 30 January 2017

Introduction

Foreword

In the Eighties there were a number of popular 8-bit game consoles.

One of them was the NES (Nintendo Entertainment System).

The CPU of the NES was also based on a very popular 8-bit CPU of that time , which was the 6502.

Last year I spend some time writing two blogs on writing an emulator emulating another 6502 machine, the C64 for both JavaScript and Android.

I reckoned that if you have tackled the emulation of the 6502, you can apply the knowledge emulating another 6502-based console.

Hence, in this series of blog posts I will be attempting the same approach for a NES emulator for JavaScript.

Again we will be following the approach of starting from scratch and building it up bit by bit as required.

The CPU part of things, however, I will not be developing from scratch since I have already done a JavaScript implementation of the 6502 for the C64, which I will use in my NES JavaScript emulator. Of course I will modify the 6502 emulation code a bit to fit within the NES-context.

Approach

As mentioned in the previous section, I will be leveraging off my C64 emulator code for the CPU implementation. In fact, I will be starting with the code of my C64 emulator as baseline, of course stripping out C64 specific functionality such as tape-emulation, VIC-II emulation and so on.

We will work towards emulating a specific game cartridge. The one we will be using will be Paperboy.

In Summary

In this blog I gave a brief outline on what the posts I have planned for this Blog.

In this blog we will be developing a NES emulator in JavaScript using the code of my previous C64 JavaScript Emulator as baseline.

We will work towards emulating the game cartridge PaperBoy.

In the next post we will have a look at the NES image file format. This file format is basically a file image of all the contents of a NES game cartridge.

We will also try to get our emulator to single step through the 6502 machine code on the cartridge.

Till next time!