Creating an Emulator

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

Post Reply
jonx
Posts: 4
Joined: Thu Jun 14, 2007 3:26 am

Creating an Emulator

Post by jonx »

Hello guys,

I have been interested in developing a NES emulator for quite some time and I was wondering if anyone would be willing to give me steps to follow to create my own? I have a lot of programming experience, so I am hoping I should be fine in that regards.

Any help is greatly appreciated!

Thanks
User avatar
Disch
Posts: 1848
Joined: Wed Nov 10, 2004 6:47 pm

Post by Disch »

There's no real definative steps that I can give you.... but as for some general advise:

- Get as much documentation and as many references as you need. I would start with NEStech (old and has minor errors, but still overall pretty decent), and obelisk. Try to get as good of an understanding as to how the NES works as you can.

- Build a simplistic tracer. The thing about making an emu is it's a lot of 'behind-the-scenes' work. So you'll end up writing pages and pages and pages of code without being able to test any of it -- and the when you finally do test it, it'll either work great, or (more likely) will fail horribly. What's more, the emu output often offers little or no signal as to where the problem is; running a game and just getting black screen could be caused by any one of a thousand problems. Having a custom tracer that shows what the game is doing, and even potentially laying out exactly where the game is locking up or crashing is invaluable. Not only when you're starting out with the emu, but also later when you start adding mapper support.


- Keep some potential future problems in mind when designing your emu structure. You don't want to get blindsided by something you forgot to take into account. Most things can be worked in... but some stuff can be a really big pain if you don't plan for them ahead of time. A handful of things you may want to remember:

+ There are 3 PPU cycles to 1 CPU cycle on NTSC... but 3.2 PPU cycles to 1 CPU cycle on PAL! So don't stick to a strict 3:1 ratio if you plan on ever adding PAL support.

+ Mappers can (and will) do some very strange things... including:
* put PRG-ROM at $6000-7FFF (don't always assume this will be RAM)
* change mirroring in funky ways -- specifically, using CHR-ROM as a nametable!
* have both CHR-ROM and CHR-RAM swapped in at the same time.
That's not counting all the crazy tricks MMC5 does -- but most of MMC5's stuff shouldn't be all that hard to work in somehow, so I wouldn't worry about it.


That's about all I can think of. Don't be afraid to ask questions ^^. Good luck!
jonx
Posts: 4
Joined: Thu Jun 14, 2007 3:26 am

Post by jonx »

Thanks for the feedback Disch. So there is no distinct set of steps for creating an emulator huh? I was hoping to go about it like this:

1) Familiarize myself with NES architecture
2) Learn 6502 Assembly
3) get frustrated and quit...

something like that :)
NotTheCommonDose
Posts: 523
Joined: Thu Jun 29, 2006 7:44 pm
Location: lolz!

Post by NotTheCommonDose »

I never coded an emulator for the fact that there are plenty as it is and they will only get better over time and they are one of those projects that require a great deal of time and patience.
jonx
Posts: 4
Joined: Thu Jun 14, 2007 3:26 am

Post by jonx »

Well my motivation for creating an emulator is primarily for personal reasons. There are several sophisticated NES emulators already developed and freely distributed. I was hoping to learn more about the methodology of creating an emulator. I want to learn about how other architectures ( besides X86) function and how I could convert one to the other. I also want to broaden my programming experience etc. Fortunately for me, creating an emulator could help me with all these desires.
jonx
Posts: 4
Joined: Thu Jun 14, 2007 3:26 am

Post by jonx »

Also, does anyone know if there is an IRC channel that focuses on emulator development?

Thanks
User avatar
Disch
Posts: 1848
Joined: Wed Nov 10, 2004 6:47 pm

Post by Disch »

I think #nesdev is on EFnet -- although I don't go in there much. When I have, the discussion always seemed to be kevtris and Q going on about crazy hardware stuff that was way over my head (shows how long it's been since I've gone in there -- Q has been gone for quite a while now)

I regularly idle/talk in #rom-hacking on espernet. The general discussion there varies (seems to be about just about everything but ROM hacking). I think I'm the only person there who's done any kind of emu dev -- although there are several other programmers and general smart people (but there's no shortage of that in #nesdev)
alkhimey
Posts: 1
Joined: Sun Aug 12, 2007 2:08 am

Few questions please.

Post by alkhimey »

1. Why is mirroring needed. If I understand it right then there are places in memory that are exact copy of the $0000 - $0800 area. Is it right?
2. When I write an emulator can I use the standard algorithem (loop and exec the opcodes), or I should implement something spacial for ppu and the chip that handales the sound?
3. Can someone explain the bank switching?
4. In general, how do I manipulate the memory? Can you give an example of implementation? (I assume it is not as simple as array of bytes :roll: )

Thank you.
User avatar
Disch
Posts: 1848
Joined: Wed Nov 10, 2004 6:47 pm

Re: Few questions please.

Post by Disch »

alkhimey wrote:1. Why is mirroring needed. If I understand it right then there are places in memory that are exact copy of the $0000 - $0800 area. Is it right?
Mirroring exists when there's more address space designated than there is physical memory. For RAM mirroring, this happens because there is 8k addresses and only 2k of actual memory. So what happens is that the high bits of the address simply go unused, and the same byte of RAM appears at four seperate addresses.

RAM mirroring is easily accomplished by ANDing the address with a value:

Code: Select all

byte read_ram(word address)
{
  return RAM[ address & 0x7FF ];
}
2. When I write an emulator can I use the standard algorithem (loop and exec the opcodes), or I should implement something spacial for ppu and the chip that handales the sound?
There are several ways to approach this. I break the emu up into sections (CPU, PPU, APU, mapper) and assign each of them a timestamp. I run the CPU first (the opcode loop you describe), and when it does something that affects another system (like say, writes to a PPU reg) I run the PPU up to the current CPU timestamp to "sync it up".
3. Can someone explain the bank switching?
I wrote this doc recently:
http://www.romhacking.net/docs/353/

Also, this might help too:
http://www.romhacking.net/docs/362/
4. In general, how do I manipulate the memory? Can you give an example of implementation? (I assume it is not as simple as array of bytes :roll: )
I keep one buffer for system RAM, one for on-cartridge PRG-RAM, one for CHR-ROM, one for CHR-RAM, and one for PRG-ROM. When loading the ROM, I load PRG/CHR ROM into the appropriate buffers. I also keep 16 function pointers for CPU reading, and 16 for CPU writing (one for each 4k of addressing space)... and any time the CPU reads or writes a byte (once every CPU cycle) I have it call the appropriate function.

ie:

readfunc[0] would read RAM ($0xxx)
readfunc[1] would read RAM ($1xxx)
readfunc[2] would read PPU Regs ($2xxx)
etc

that way reading can be done with:

value = readfunc[ address >> 12 ]( address );

and it will be mapped to the desired area.


For PRG/CHR swapping and NT Mirroring, I keep a series of pointers (one pointer represents a 4k bank of PRG, or a 1k bank of CHR, or a 1k nametable), and to swap PRG/CHR or change mirroring modes, I simply change what those pointers point to -- it's a lot easier to change a pointer than it is to copy large chunks of memory every time the game swaps (which is often several times per frame).
Post Reply