NaqNes

Manne Cederborg

Manne Cederborg

Gameplay Programming / Systems Design

 

Title: NaqNes

Company: Hobby project

Platforms: PC, DirectX 9, x64

Period: Developed as a hobby project around 2013

Language: C++

 

Overview

I started NaqNes as a hobby project for two main reasons. The first reason is that I wanted to understand how an emulator is made, so I started with something I thought would be simple, the Nintendo 8-bit. It does not have an advanced graphics chip that would need 3d calls to be translated into DirectX calls or any other graphics API. During its prime time, it was also said the Nes could do scrolling, something that was not possible on the PC at the time. I wanted to understand why this was the case. The second reason I wanted to work on this was that I wanted to evolve my knowledge of C++. Through this project, I learned a lot about memory handling and computer sicence.

 

Specifications

The main goal of the project was to make a fully working Nintendo 8-bit emulator that could be portable and easily integrated into other APIs. In my example I generate a texture that is copied to a DirectX texture and displayed in a window.

System Emulation

 

Central Processing Unit

The Nes uses a Ricoh 2A03 processor that is based of the 6502. This was one of the most used CPUs in its time. It's very well documented and made it easy to find information about the instructions used.

 

The CPU is emulated using an interpretation model, where the code just reads one operation code from the supplied file and selects the correct function that runs the logic.

Picture Processing Unit

The PPU runs in parallel with the CPU and keeps in sync using the CPU clock cycle. Most of the graphics drawn are changed by setting a pointer to the location of where the PPU should start reading. This makes it very easy to scroll in games.

 

However, if only a portion of the screen scrolls, you need to know exactly when the scanline was on that part of the screen (e.g. the black bar at the top in Zelda). This was done by calculating the clock cycles and using an interrupt that you would get from rendering the first sprite in the scene. (In Zelda, this sprite is the blue bomb.)

 

Many of the graphics effects were achieved by using bugs in the hardware. E.g. Balloon Fight makes stars twinkle in the background by corrupting some memory while the PPU is reading it. You are normally not allowed to change any graphics pointer while the PPU is rendering.

What I learned

What is left to do

All the CPU instructions are correctly emulated. The clock cycles are however not calculated 100 % correctly. This has to do with special events causing extra cycles. One of these cases is when the memory is read or written over a pagefile.

 

Since all games came on cartridges that contained hardware, the cartridges could expand the original system's hardware. They can have different amounts of memory, switched at run time using different methods. The most common method is to read from a specific memory address to change the memory chip. Some can even extend the sound card on the system by adding additional channels. There are around 250 different cartridges, implementing them all would take too much time, so there are currently support for 3 different cartridge types.

 

There is currently no sound emulated in my version.

 

 

What could be made better?

  • Make a debugging tool from the start

I spent more time than I should have finding small bugs with the code for the different instructions. Just one flag being set incorrectly caused issues that were very hard to find.

 

  • An interpretation emulator is very slow, but its a very good start for learning. If I started over with the project, I would try a dynamic recompilation method instead.

 

  • I need a system for adding new cartridges faster, but for this, the memory system needs to be redone. It's currently in the CPU code and hard to override.

 

  • I need to optimize the code, since much of the time is used for jumping in and out of function calls and the code does not take the cache into account. Lots of virtual functions slowing everything down.