Hi all, I'm a newbie here and my interest is to create a NES emulator from scratch (just for learning). So what is the basic things to know to create such an emulator?
My thinking that is knowledge in 6502 and NES hardware architecture is necessary. I think rendering is not a big problem and the problem is to know all the things in the architecture of NES.
But can anyone please tell me what is the fundamental principle that lies under the emulator? Let's say, the memory manegement part, the instruction execution part, etc.
Thanks in advance.
Making an emulator requires an understanding of the emulated system (in this case, the NES) and of programming for the target platform (in this case, probably Windows or whatever OS you're using). Emulators do everything that regular video games do... so if you don't know enough of whatever programming language to make a simple game for your platform, an emulator might be a pretty big challenge.
You don't need to know everything up front, though. Writing an emu is a great way to fill in the gaps of your knowledge about both PC programming and NES behavior.
The NES is split into several seperate and distinctly different sections, so you can bite off chunks at a time, rather than working on the whole package at once. However one of the big challenges for beginning emudevers is that the chunks are pretty large, and you'll have to write a LOT of code before you'll be able to test ANY of it. You might spend a week writing hundreds of lines of code for your 6502 core, and when you finally fire it up to try it out, it doesn't work and you have no idea why (this happens to everyone their first time)
Programming skills and experience are really all you need coming into NES emu dev. There's enough information about the NES floating around so that any capable programmer willing to put in the time can make an emulator.
Really the only things I can say are:
1) focus on the CPU first (6502 emulation)
2) CPU instructions consist of several reads and writes. Do not read/write from any array directly anywhere in your code. Hide all reads and writes in a macro or something so that the way your emu does them can be easily changed later.
3) put in a simplistic memory system (CPU addressing space -- $0000-07FF is RAM, etc). A very simple way to do this is to just have a 0x10000 byte buffer that you read/write to. This is very simple and easy to set up and use, so it's good to start out with, however you'll find that it's terribly impractical for a full emulator. So you'll want to change it later... hence point #2 above.
4) don't worry so much about interrupts until you have a mostly functioning 6502 core.
5) -WRITE A TRACER- I can't stress this enough. Just something which makes a text log of each 6502 instruction your emu is running and some other information (like A,X,Y contents, status flags, etc). It's a lot of additional work, but trying to debug your core is next to impossible unless you can step through each instruction and watch what your core is doing. It's a 100% guarantee that your 6502 code won't work the first time you run it (or even after the first dozen fixes), and without tracing it to see what it's doing wrong and how, trying to fix it is like looking for a needle in a haystack.
6) throw together a simple file loader so that you can load test ROMs and whatnot, but make it easily replacable.
7) WRITE A TRACER. Seriously. Will save your life.
8) I'd recommend starting with test ROMs like the ppu-free version of nestest.nes, which doesn't require any PPU functionality in an emu and puts your CPU through rigorous tests. It's important to get the CPU working solid first, so that when you add PPU support and stuff, you don't have to worry about whether a graphical glitch is caused by your PPU code or your CPU code.
9) WRITE A TRACER. Just have to mention it one last time. You'll never run out of uses for it. Once you solve all your CPU bugs, it will help track down and fix PPU bugs. Then APU bugs. And mapper bugs. And any other kind of bug in your program.
Anyway hope that helps. Dunno if that's really what you were asking about -- I'm not quite sure what you meant by the "fundamental principle" of an emulator. I mean I guess the fundamental principle is emulation -- but something tells me that's not the answer you were looking for XD
The fundamental principle of an emulator is that it runs the same sequence of instructions that another machine runs given the same input. Put a tracer in your emulator (as a macro so you can condition it out at compile time), and compare your emulator's trace logs to those from another emulator that has a tracer.
I have to agree with Disch, writing some form of Tracer is very important so you can really see what is happening. Otherwise how would you ever know?
I also agree that your first steps are a simple ROM loader and the CPU core. Lots of bad things will happen when your cpu core is wrong, so there's not much point in trying to emulate graphics or sound or complex mappers until your cpu core is debugged. The NES has a great cpu test rom you can run without any real graphics emulation. That's what I used which helped me catch what I did wrong.
I'd also like to throw out there that the NES is probably one of the easiest systems to get started emulating. It's harder to emulate it with high accuracy, but a decent emulator that will run some games isn't that hard.
I actually started working on an emulator not too long ago.
I started with the 6502 CPU. The problem was, how to test it. So I worked on a disassembler, which is really just a tracer. I had my own code which I could compare to the traced output. It ended up helping a lot. I ended up breaking the disassembler, but keeping and enhancing the tracing since I've needed it a lot (like when I messed up my RTI routine to always push the start of a reset as the address).
For me, the next step is the PPU. I'm actually breaking the work apart since I've been able to work this long without anything graphical so I know I dont need the entire PPU working from the start. I'm focusing on background first, then scrolling and mirroring, then sprites, and then supporting input, and someday mappers. Last will be the APU, and then I'll start adding additional mappers, etc..
It helps that I have some very basic NROM code done so I dont need a lot of stuff working right away.
I can't say i my way is good and I know I'll be bitten later on, but its a way to see demonstable progress and yes you definitely learn a lot.
Al