Skip to main content
GameDev.net gamedev.net
🔒 Locked

I'm writing an NES emulator

Started by essial Aug 16, 2009 at 1:04 AM 34 replies 8.2k views
Original Post
essial
essial
**edit** You can follow my progress at http://projectemulate.blogspot.com/ now. **end edit** I have finally decided to write an NES emulator. I've always been fascinated by emulators, and I think I am to the point skill-wise to actually be able to pull it off. I'm well aware that there are countless NES emulators out there, but again, I want to write one both for fun, and to be able to say that I did :) I'm on day 3 now and here's where I'm at: Obviously thing's aren't quite right yet, but I'm getting there. Currently, most games crash (I'm testing games that don't require a mapper, I'll add mapper support once I get everything else working). Input appears to be working correctly, and of course no sound yet. I'm doing this in C++ and am using an array of function pointers to call the opcodes efficiently without going through a giant if/else or switch statement (conditional jumps destroy CPU caching). If you guys want I'll post updates as they come; otherwise I wont say anything else about it -- just wanted to let you guys know! [Edited by - essial on August 16, 2009 11:06:32 PM]
laeuchli
laeuchli
Looks pretty cool...
Nytegard
Nytegard
Nice job.

It doesn't matter if there are a billion of the exact same program if you are enjoying writing it, and are learning from it.
Koobazaur
Koobazaur
That's neato. Can you elaborate more on the process? I never done anything like an EMU or virtual machine and would actually be curious to how one works? Are you basically just taking all the instructions in the ROM file and just writing C++ equivalents using stuff like SDL, while also creating a "fake" CPU / RAM / etc as a virtual environment for it? Or is it more involved than that?
Comrade, Listen! The Glorious Commonwealth's first Airship has been compromised! Who is the saboteur? Who can be saved? Uncover what the passengers are hiding and write the grisly conclusion of its final hours in an open-ended, player-driven adventure. Dziekujemy! -- Karaski: What Goes Up...
essial
essial
Quote:
Original post by Koobazaur
That's neato. Can you elaborate more on the process? I never done anything like an EMU or virtual machine and would actually be curious to how one works? Are you basically just taking all the instructions in the ROM file and just writing C++ equivalents using stuff like SDL, while also creating a "fake" CPU / RAM / etc as a virtual environment for it? Or is it more involved than that?


It's a bit more involved. If I am successful at making this emulator I plan on releasing full docs on how I did it (along with what I believe to be the CORRECT NES specs, as some are wrong), and possibly even a few videos.

But basically at the heart of the system is the processor; Since the processor in the US nintendo is called 2A03 (a derivative of the 6502), I created a class called "CNES2A03". Once I did that, it was a matter of having function pointers for memory read/writes (in the real world this would be sent to the address bus and such), handlers for the opcodes, a set of flags, and some very important functions such as "processOpcode()" and "generateNMI()". Of course it gets a lot more complicated once you get into the video rendering (as you can tell by my shots :p).

But as I said, if I can make this thing work, I'll release a nice fresh and modern doc explaining everything accurately.
essial
essial
Yeah right now I am fighting an issue that seems to be related to a stack overflow (meaning, when RTS is called, the wrong address is poped off the stack. It's pretty obvious when seen through my CPU trace:

[#$F4FA] $18 <- CLC (Clear Carry Flag)
[#$F4FB] $F0 <- BEQ (Branch if Equal)
[#$F4FD] $38 <- STC (Set Carry Flag)
[#$F4FE] $66 <- ROR (Rotate Right)
[#$F500] $66 <- ROR
[#$F502] $66 <- ROR
[#$F504] $66 <- ROR
[#$F506] $66 <- ROR
[#$F508] $66 <- ROR
[#$F50A] $66 <- ROR
[#$F50C] $66 <- ROR
[#$F50E] $60 <- RTS (Return from subroutine)
[#$0FE5] $0 <- BRK (Break - This address should not be executed at)
[#$0FE6] $0 <- BRK (Break)
[#$0FE7] $0 <- BRK (Break)

So hopefully I can figure this one out and get mario and donkeykong running.

On the other hand, I finally have nestress.nes running (nes stress test rom) without crashing:

WazzatMan
WazzatMan
You should probably blog this. I'd definitely like to see the progress on this project as it unfolds. Then again, posting here makes it easier for you to get feedback. Best of luck!
essial
essial
If enough people actually want me to blog about it then I will, I just figured there wouldn't be too much interest as this is not exactly something new :)
essial
essial
Well if I disable my kill-switches (invalid ops, etc) mario looks like it's doing ~something~..


But anyway, I'm off to run some errands but I'll be back later :)
HostileExpanse
HostileExpanse
Quote:
Original post by WazzatMan
I'd definitely like to see the progress on this project as it unfolds.


Agree with this.
AndreTheGiant
AndreTheGiant
Quote:
Original post by essial
If enough people actually want me to blog about it then I will, I just figured there wouldn't be too much interest as this is not exactly something new :)


If you do a good job of documenting what youve done and why, etc, that might be new.

If I was doing something this interesting, I would blog it :)
essial
essial
Alright, I'll start a blog. I haven't gotten into blogging yet, so what site would you guys suggest I use? Once I set it up I'll post here with the URL. Again, I've got a lot to do today so I wont have any more updates until tomorrow but tonight when I get home I'll check your replies and start blogging my progress. Thank you for your interest in this project :)
benryves
benryves
Quote:
Original post by essial
Alright, I'll start a blog. I haven't gotten into blogging yet, so what site would you guys suggest I use?
This one? [wink] Other people have written about emulator projects in the GameDev journals in the past.
[Website] [+++ Divide By Cucumber Error. Please Reinstall Universe And Reboot +++]
essial
essial
Alright, I have created a blog here. I JUST created it, and I haven't done anything with blogging yet so it may not be until tomorrow (monday) before the site makes any sense, so have some patience :)
trzy
trzy
Quote:
Original post by essial
Yeah right now I am fighting an issue that seems to be related to a stack overflow (meaning, when RTS is called, the wrong address is poped off the stack. It's pretty obvious when seen through my CPU trace:

[#$F4FA] $18 <- CLC (Clear Carry Flag)
[#$F4FB] $F0 <- BEQ (Branch if Equal)
[#$F4FD] $38 <- STC (Set Carry Flag)
[#$F4FE] $66 <- ROR (Rotate Right)
[#$F500] $66 <- ROR
[#$F502] $66 <- ROR
[#$F504] $66 <- ROR
[#$F506] $66 <- ROR
[#$F508] $66 <- ROR
[#$F50A] $66 <- ROR
[#$F50C] $66 <- ROR
[#$F50E] $60 <- RTS (Return from subroutine)
[#$0FE5] $0 <- BRK (Break - This address should not be executed at)
[#$0FE6] $0 <- BRK (Break)
[#$0FE7] $0 <- BRK (Break)

So hopefully I can figure this one out and get mario and donkeykong running.


It seems unlikely that you have bugs in your RTS code because then nothing else would likely work. Are you handling mirroring correctly? Some memory areas on the NES are mirrored (this is common in most computer systems), meaning different addresses point to the same physical memory. A simple way to debug this problem would be to keep track of where each JSR is writing the return address to and then making sure it matches the address that RTS is reading from.
----Bart
essial
essial
Yes I BELIEVE I have properly mirrored all addresses in both the 2A03 processor, as well as the NES PPU (for writes to the I/O addresses from the processor). What I'm going to do tonight is run nestress.nes, and follow the source code line for line to see if anything odd turns up. I know my RTS function itself is good -- I'm mainly leaning towards stack thrashing or something annoying like that.
BenThereDoneThat
BenThereDoneThat
Very jealous! Thanks for creating the blog, it's nice to see projects like this as they're coming along.
----------------------------My site: www.sudoexec.net
essial
essial
For your information, here is my mirroring logic for the CPU:
if ((newAddr >= 0x0800) && (newAddr < 0x2000)) {	newAddr %= 0x0800;} else if ((newAddr >= 0x2008) && (newAddr < 0x4000)) {	newAddr = 0x2000 + ((newAddr - 0x2008) % 0x08);}


I obviously do this for both reads and writes. And ALL reads/writes done on the processor goes through these methods (I don't index the memory array directly, the processor class doesn't even have access to it at all!).
essial
essial
YAY figured it out :D The stack is TOP DOWN, I was working the stack in reverse. I knew I was in the right area. ALL games are running without crashing now :) I guess it's on to the PPU tonight to get some awesome screenies up tomorrow.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.