Click here to Skip to main content
Click here to Skip to main content

My Nes - Nes Emulator

, 19 Dec 2009 GPL3
Rate this:
Please Sign up or sign in to vote.
My Nes is a portable Open Source NES/FAMICOM emulator written in C#.

MyNes_NitendoEmulator/Main.JPG

Introduction

Famicom (Family Computer) is a Nintendo® Entertainment System. It was the world’s most widely used videogames console during the 1980s. From its initial release in 1983 until it was discontinued in 1995, the console brought gaming into more homes than ever before and paved the way for the videogame industry as it stands today.

For more information about the Nintendo® Entertainment System, please visit http://nesdev.parodius.com.

Emulation is the process of simulating hardware to enable the software developed for it to be used on an otherwise incompatible system.

My Nes is an emulator for this system. At this time, it covers the popular games and it closed to cover over than 1000 games.

My Nes is completely portable, which means that you can port it into another platform or system like Xbox. Although it uses windows codes in some places for drawing and rendering sound but not the main NES engine.

This article should cover what you need to understand this project, also My Nes can be found at http://sourceforge.net/projects/mynes/ for developing, downloading what's new ...

But before you start reading this article or looking at the code, I recommended that you get a good knowledge about the real NES hardware and the other stuff.

And sorry if I'll abasing you when I say that you should get a knowledge about the "byte" and how to get a "bit" from the "byte" and what is the "short" ......

Background

My Nes includes three projects:

  1. The launcher called "My NES", it just the main EXE for the program and it initialize the main window form.
  2. The Core which includes the Windows forms and the classes for its like settings. I decided to add the windows forms in this library so that it will be lonesome from the NES engine. When you want to port into another platform or system, you should take care of this first.
  3. The NES project: it's a DLL library includes the classes that work together to emulate the NES system. these classes are completely lonesome with windows (exapt drawing, input handling and sound rendering).

The purpos of My Nes project is to use the engine (3rd project) to emulate the NES.

How does My Nes engine emulate?

Central Processing Unit (CPU)

The NES uses the NMOS processor based on the 6502, the 2A03, The chip differed from a standard 6502 in that it had the ability to handle sound, but in this engine i made the sound work done at APU class.

However, like any system, CPU is the heart and it controls everything using the registers.

The 6502 has five 8-bit registers and one 16-bit register. In each system cycle, the cpu fetch the instruction from the memory to execute, and the execution decides what's gonna happen at the next cycle and what's every part of the system has to do.

So 6502 emulation done at CPU6502.cs class, just take a look :

//Registers 
public byte a_register; 
public byte x_index_register; 
public byte y_index_register; 
public byte sp_register; 
public ushort pc_register;
//Flags 
public byte carry_flag; 
public byte zero_flag;
.......//methods for addrissing modes, opcodes ...
 
//the cycles are done here in the "while" loop
//don't forget that the Thread will handle it.
public void RunProcessor()
{
   previousPC = pc_register;
   while (!_NesEmu.isQuitting) 
   {
    currentOpcode = _NesEmu.ReadMemory8(pc_register);//get the next opcode from the memory
    switch (currentOpcode) //do the opcode
    {
     case (0x00): OpcodeBRK(); break; 
     case (0x01): OpcodeORA(); break; 
     case (0x05): OpcodeORA(); break;
     ........
    }
    if (tick_count >= _NesEmu.Ticks_Per_Scanline) 
    //if the cpu done the required cycles for the scanline
    //and this depending on NTSC or PAL.
    { 
     if (_NesEmu.myPPU.RenderNextScanline()) //tell the picture unit to render the scanline
     { 
       //do the interrupt routine.
       Push16(pc_register); 
       PushStatus(); 
       pc_register = _NesEmu.ReadMemory16(0xFFFA); 
     } 
     tick_count = tick_count - _NesEmu.Ticks_Per_Scanline; 
    }
   }
}

The Memory

All the memory is located at the NesEmulator.cs class, and the CPU access the memory using ReadMemory8, WriteMemory8 and ReadMemory16 methods.

Also NesEmulator.cs class handles everything about the ROM (cart memory) including the access of mappers. Mappers are the most important stuff in the memory, 'cause the access of the cart is depending on the mapper. In the real NES cart, the mapper comes inside of it. But the INES format does not has the ability to handle a mapper, so we have to write each mapper (256 one) inside the emulator.

MyNes_NitendoEmulator/Mem_Diagram.jpg

"This figure taken from http://nesdev.parodius.com/NESDoc.pdf"

There's no buses in the emulator, you know, just the read and write methods.

Take a look at the mapper # 2 code:

internal class Mapper2 : IMapper //IMapper is the interface !!
{ 
   Mappers Map;//Mappers is the general class for bank switching 
   //and the engine depends on Mappers class for accessing any mapper.
   //It includes a field called "CurrentMapper" and it will hold this 
   //mapper when the cart is mapper 2.
   public Mapper2(Mappers Maps) 
   { Map = Maps; } 
   //When the system ask to write into cart (and rom is read only you know)
   //it asks to tell the mapper something . 
   public void Write(ushort address, byte data) 
   { 
     if ((address >= 0x8000) && (address <= 0xFFFF)) 
     {
      Map.Switch16kPrgRom(data * 4, 0); //this means :
      //attach the prg bank # (data * 4) in the cart into the
      //address 0x8000 in the main memory.
     } 
   } 
   //At the reset or start of the system, it calls this method
   //to setup the mapper.
   public void SetUpMapperDefaults() 
   {    
      //last prg rom into address 0xC000.
      Map.Switch16kPrgRom((Map.mapperCartridge.prg_rom_pages - 1) * 4, 1);
      Map.Switch8kChrRom(0); 
      
   } 
   //some mappers needs to tick in each scanline and the system calls this method
   //every scanline even that mapper (like this) doesn't have a ticker.
   public void TickTimer() { } 
}

Picture Processing Unit (PPU)

The NES has the 2C02 processor to serve as PPU. This processor handles the graphics and the CPU access the PPU via memory addresses 0x2000 to 0x4020 and this area of memory called I/O registers.

Also in My Nes engine, the PPU emulation done at class called PPU, and it render each scanline like the follow:

public bool RenderNextScanline()
{
  int i; 
  if (currentScanline < 240)
  {
   //Clean up the line from before 
   //Remember everything done on the offscreenBuffer array which holds
   //the 256 * 240 pixels in 16-bit color format.
   if ((uint)nameTables[0x1f00] > 63) 
   { 
    for (i = 0; i < 256; i++) 
    { offscreenBuffer[(currentScanline * 256) + i] = 0; sprite0Buffer[i] = 0; } 
   } 
   else 
   { 
    for (i = 0; i < 256; i++) 
    { offscreenBuffer[(currentScanline * 256) + i] = (
        short)Nes_Palette[(uint)nameTables[0x1f00]]; sprite0Buffer[i] = 0; } }
  
 
  //We are in visible territory, so render to our offscreen buffer 
  if (spritesVisible) 
     RenderSprites(0x20); 
  if (backgroundVisible) 
     RenderBackground(); 
  if (spritesVisible) 
     RenderSprites(0);
  if (!noBackgroundClipping) 
  { for (i = 0; i < 8; i++) 
     offscreenBuffer[(currentScanline * 256) + i] = 0;
  } 
  if (sprite0Hit == 0) 
  { for (i = 0; i < 256; i++) { if (sprite0Buffer[i] > 4) sprite0Hit = 1; } }
 
  //The mapper timer !! 
  if (backgroundVisible || spritesVisible) 
     myEngine.myMapper.TickTimer();
  }
  
  //The moment of truth, render the buffer into the screen !!
  if (currentScanline == 240) { myVideo.CurrentDrawer.RenderFrame(offscreenBuffer); }
  
  currentScanline++;
  //Render the sound ...
  if (currentScanline == Scanlinesperframe) 
  { if (myEngine.SoundEnabled) { myEngine.myAPU.Render(myEngine.my6502.total_cycles);..... }
 
  //Are we about to NMI on vblank? 
  if ((currentScanline == ScanlinesOfVBLANK) && (executeNMIonVBlank)) 
  { return true; } 
  else { return false; }
}

Audio Processing Unit (APU)

The APU.cs renders the sound of the NES, and I will not write any thing about the APU because I'm still confused about this part of the NES and I really include it in the engine hoping someone help me on it. However, take a look at the source.

Cool but, how does it work?

The RunProcessor() method at the CPU loops to execute, and as I say before, this execution may tells every part what to do. Also the I/O registers is the most important area in the memory, so when the CPU access this area (Read or Write), the other parts do their job depending on the values of the registers. For example, a write anto address 0x2000 will change the sprite size (8x8 or 8x16), background address .... in the PPU.

Stop that method "RunProcessor()" and the system will stop. Run it and the system will run -_-

How to use the engine?

The main window (in the Core) uses the engine like this :

NesEmulator _Nes;
Thread gameThread; 
ThreadStart myThreadCreator;
public void OpenRom(string FileName)
{
  CartHeaderReader rom = new CartHeaderReader(FileName);//used to check the validate of the rom
  if (rom.validRom)
  {
   if (rom.SupportedMapper()) 
   {
    _Nes.QuitEngine();
    _Nes = new NesEmulator(panel1, statusStrip1);
    _Nes.InitializeEngine();
    _Nes.LoadCart(rom.FilePath);
    //Setup
    //region 
    _Nes.CurrentSystem = MainCore.Settings.NesRegion;
    ......
 
    //Launch ... 
    myThreadCreator = new ThreadStart(_Nes.DoFrame); 
    gameThread = new Thread(myThreadCreator); 
    gameThread.Priority = ThreadPriority.Highest; 
    gameThread.Start();
   }
  }
}

I think that's easy Smile | :)

Rendering the Video

I written 2 GDI renderer class for video, and it directly renders the offscreenBuffer from the PPU.

I used to draw it into a Bitmap image first then draw it into a Control which will be the screen surface and that to make TakeScreenShot() done easily.

Graphics GR;//The drawer
Control _Surface;//the drawing surface like "panel" control.
//These 4 values changed depending on NTSC, PAL and DrawSize.
int Screen_X = 0; 
int Screen_Y = 0; 
int Screen_W = 0;//width 
int Screen_H = 0;//height
Bitmap bmp = new Bitmap(256, 240);
public unsafe void RenderFrame(short[] ScreenBuffer) 
{ 
  if (Surface != null & !_IsRendering & _CanRender) 
  { 
   _IsRendering = true; 
   //Render the bitmap 
   BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, 256, _ScanLines),
       ImageLockMode.WriteOnly, PixelFormat.Format16bppRgb565); 
   short* numPtr = (short*)bmpData.Scan0;//using the pointer is the best way to make it fast. 
   for (int i = _FirstLinesTCut; i < ScreenBuffer.Length - _FirstLinesTCut; i++) 
   { 
     //ScreenBuffer is an array of 16-bit color format so we don't have to convert it
     //so that saves speed.
     numPtr[i - _FirstLinesTCut] = ScreenBuffer[i];
   } 
   bmp.UnlockBits(bmpData); 
   //Draw it !! 
   GR.DrawImage(bmp, Screen_X, Screen_Y, Screen_W, Screen_H);
   //Draw the text if we have to 
   if (TextApperance > 0) 
   { 
   GR.DrawString(TextToRender, new System.Drawing.Font("Tohama", 16, FontStyle.Bold), 
   new SolidBrush(Color.White), new PointF(30, Surface.Height - 50)); 
   TextApperance--;
   } 
   _IsRendering = false; 
  } 
}

That code taken from Vid_GDI_16Bit class and using it makes My Nes runs at 80 ~ 100 fps (after enabling the Speed Throttling).

Point Of Interest

Playing the NES is realy cool but emulating the NES in your hands, that's a different story !!
My Nes can be the start point of your emulator for the NES or any system , all have the same principles.

Also My Nes is closing to be a REAL emulator, it's cool to play using it since it covers the most nice NES games which can be playable perfectly.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)

Share

About the Author

Ala Hadid
Software Developer
Syrian Arab Republic Syrian Arab Republic
Start programming at 2008, my first successful project was AHD Subtitles Maker "ahd-subtitles-maker.webnode.com".
I'm studying Physics in Hims university.
Programming is my favorite hobby that fill my empty times.

Comments and Discussions

 
SuggestionVideo Tutorials Serie about NES emulation PinmemberLuca D'Amico4-Nov-13 14:55 
Questionwelcome to: http://www.netetrader.com Pinmemberkuif8sa9s9-Aug-12 15:54 
GeneralMy vote of 5 PinmemberMember 89327415-May-12 9:44 
Generalممتاز يعطيك العافية PinmemberMazen el Senih14-Mar-12 10:08 
GeneralRe: ممتاز يعطيك العافية PinmemberAla Hadid22-Mar-12 10:20 
QuestionMy NES doesn't work on any computer I own PinmemberMifdongs24-Nov-11 19:25 
AnswerRe: My NES doesn't work on any computer I own [modified] PinmemberAla Hadid26-Nov-11 23:22 
AnswerRe: My NES doesn't work on any computer I own Pinmembersgraves10-Jan-12 17:51 
GeneralRe: My NES doesn't work on any computer I own PinmemberAla Hadid11-Jan-12 12:16 
NewsVersion 2.3 is out !! PinmemberAla Hadid29-Mar-11 12:49 
Generalاعجبني PinmemberTheDevelopper13-Feb-11 0:28 
GeneralRe: اعجبني PinmemberAla Hadid13-Feb-11 2:31 
NewsNew version 0.8.1.15 PinmemberAla Hadid14-Jan-10 23:19 
GeneralNew ver 0.6.4.6 released PinmemberAla Hadid8-Nov-09 0:09 
NewsVer 0.5.2.3 released !! PinmemberAla Hadid30-Oct-09 1:41 
NewsNew Release Ver 0.4.1.2 PinmemberAla Hadid17-Oct-09 15:12 
GeneralRe: New Release Ver 0.4.1.2 Pinmembermaxim galayko29-Oct-09 9:23 
AnswerRe: New Release Ver 0.4.1.2 PinmemberAla Hadid30-Oct-09 0:39 
GeneralRe: New Release Ver 0.4.1.2 Pinmembermaxim galayko31-Oct-09 12:27 
AnswerRe: New Release Ver 0.4.1.2 PinmemberAla Hadid5-Nov-09 7:17 
GeneralNDS PingroupNorm .net8-Oct-09 0:38 
GeneralRe: NDS PinmemberAla Hadid8-Oct-09 5:25 
GeneralROMS PinmemberGabPsy18-Aug-09 7:56 
AnswerRe: ROMS [modified] PinmemberAla Hadid21-Aug-09 7:04 
GeneralRe: ROMS PinmemberThe_Mega_ZZTer7-Oct-09 17:09 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141220.1 | Last Updated 19 Dec 2009
Article Copyright 2009 by Ala Hadid
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid