First steps in writing an emulator

Discuss emulation of the Nintendo Entertainment System and Famicom.

Moderator: Moderators

User avatar
janzdott
Posts: 42
Joined: Fri Oct 04, 2013 11:56 pm

First steps in writing an emulator

Post by janzdott »

Hey guys, I'm new here. Glad I found the place :D

I've been interested in emulators since I found out they existed. I downloaded a N64 emulator and started playing old N64 games from my childhood. The N64 was my favorite, and the nostalgia of playing those games again was great :D

I thought it would be interesting to write an emulator. Yesterday I started an NES emulator. I've been doing a LOT of reading, and a bit of programming.

I'm using C++. So far, I wrote a NES class that holds all the other components. I made a CPU class, and started a cpuCycle() function, which has switch statement for the opcodes. I plan to write a function for each opcode, and a function for each addressing mode. That way, I'd have a lot less functions compared to if I wrote a function for each unique opcode with addressing mode. That should work, right? I also wrote a Memory class, which doesn't do much besides have methods for reading and writing. I believe I understand mirroring, but it seems strange. When you write to an address on the CPU memory, it gets mirrored to the corresponding mirror addresses. Can I program this explicitly into the CPU Memory class? Can anything change how the CPU memory is mirrored? I also wrote a ROM loader. It reads the header, and loads the 1 or 2 program banks into the CPU PRG memory. I'm only going to worry about ROMs with 1 or 2 program banks, until I get it running.

So now, my NES reads the ROM and loads the program banks into memory. Now what? I'm a little confused. The program counter starts at 0, right? Which corresponds to the very first byte in the CPU memory? What gets loaded there when the ROM loads? Or does the program counter start at the lower PRG bank?

I know, it's quite a few questions. But I'm finding it hard to find answers to simple questions like these. Trust me, I've been doing a LOT of reading. I'd like to have the CPU memory set up first, so I can start programming the opcodes. That should be my next step, right? Or are there other things I have to get set up first? Could someone give me a basic step-by-step order I should program things in? Some help would be GREATLY appreciated. Thanks :D
Last edited by janzdott on Wed Oct 16, 2019 8:57 pm, edited 2 times in total.
Shiru
Posts: 1161
Joined: Sat Jan 23, 2010 11:41 pm

Re: First steps in writing an emulator

Post by Shiru »

Take a look at SIDE and PIE.

A function per opcode and per addressing mode will work and could allow to organize things clearly, but don't forget that it could hit the performance a lot as well, with many thousands of funciton calls per frame (so think about inlining, call table, etc).

Read/write handlers is the place to implement mirroring, so yes, Memory class is the place.

When you writing an emulator, one of very first things to do is to take a look at memory map. If you do so, you'll realize NES can't start from address 0, because there is RAM. The answer is in 6502 docs, though, read about so called 'vectors' (namely reset vector), they are the thing that tells CPU where to start and where interrupt handlers are located.

And kind of an offtopic response, I've been a programmer for about 20 years now, wrote few emulators, but still I wouldn't say I'm a pretty good programmer.
User avatar
janzdott
Posts: 42
Joined: Fri Oct 04, 2013 11:56 pm

Re: First steps in writing an emulator

Post by janzdott »

Thank you. Yeah, I'm going to inline the functions. I'm not too worried about performance yet. I just want to get it up and running before I worry about optimization. So the reset vector tells the program counter where to start? If I have the program banks loaded into memory, and get memory mirroring set up, and set the program counter to the reset vector, can I start programming opcodes and letting the CPU cycle?
Shiru
Posts: 1161
Joined: Sat Jan 23, 2010 11:41 pm

Re: First steps in writing an emulator

Post by Shiru »

Yes, the program counter is loaded with value from the reset vector at reset.

I'd say that writing and debugging a 6502 emulator with a NES emulator at once is way more difficult thing to do than doing these things separately, because you could make mistakes in both counterparts without being sure where it is. A better approach for start would be just writing 6502 emulator, with very simple abstact system, like plain 64K RAM where you will manually put some test code. Just make your 6502 emulation code well separated from everything else, and you'll be able to both debug it in the test enviroment (when you need to catch a bug), and use it in complete NES emulator enviroment.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: First steps in writing an emulator

Post by tokumaru »

janzdott wrote:When you write to an address on the CPU memory, it gets mirrored to the corresponding mirror addresses. Can I program this explicitly into the CPU Memory class?
The best way to handle memory mirroring is doing it like the hardware does: partially decoding addresses. The reason $0000-$07FF is mirrored all the way up to $1FFF is because the NES only has 2KB of memory to fill a space of 8KB. Once the NES detects that $0000-$1FFF is being accessed (detecting $0000-$07FF would need more hardware, because it would have to watch more address lines), it uses the 11 bits it takes to address 2KB and ignores the remaining 2 address line it would take to access 8KB. Mirroring is just a side effect of ignoring some address lines, because it's cheaper to do so. You can do the same thing in software.
Can anything change how the CPU memory is mirrored?
Carts can map anything they want from $4020 to $FFFF. Even though this is not terribly common, it's certainly possible.
I also wrote a ROM loader. It reads the header, and loads the 1 or 2 program banks into the CPU PRG memory. I'm only going to worry about ROMs with 1 or 2 program banks, until I get it running.
Keep in mind that the NES doesn't "load" anything, no data is copied anywhere. It instantaneously sees the ROM when it's powered on. Even when bankswitching is used nothing is ever copied anywhere, what happens is that the address lines are manipulated to make different sections of a larger memory chip visible in the small window that the CPU can see.
The program counter starts at 0, right?
Nope. It starts at the address pointed by the RESET vector. The 6502 looks for 3 special addresses at the end of the addressing space: $FFFA-$FFFF contain the addresses the CPU is supposed to jump to in case of an NMI, RESET or IRQ. If the RESET vector points to $0000 then the CPU will try to execute code from there, but that wouldn't really work because $0000 is RAM, and right after power on it's contents are undefined (NMI and IRQ can safely point to RAM if the program puts the code to handle these interrupts there though).
What gets loaded there when the ROM loads?
Nothing, which means the CPU would try to execute a "random" sequence of undefined bytes as if they were code... that's a certain crash!
I'd like to have the CPU memory set up first, so I can start programming the opcodes. That should be my next step, right? Or are there other things I have to get set up first? Could someone give me a basic step-by-step order I should program things in?
The CPU is a good place to start. Keep in mind that you must have the CPU, PPU and APU running in parallel at all times, each one doing there thing and interacting with each other, so you'll have to program these components in a way that they can either run little by little one at a time (often called "cycle-by-cycle", which is somewhat slow depending on the machine that's running the emulator), or predict when the next interaction with other components will be and emulate up until that point (this is significantly harder!).
I looked at the Nintendulator source, and it's crazy. I'm a freak about commenting, organizing, and simplifying my code.
Nintendulator is one of the most accurate NES emulators out there, so it's no surprise that it's source code is pretty complex. I'm not sure how simple you can keep things if you plan on making accurate emulators, because there's all sorts of little hardware quirks that make it impossible to solve problems with straightforward solutions. If you just want to get games running (as opposed to faithfully emulating all hardware aspects), things get easier and you can make use of game-specific hacks (this makes your emulator suck as a game development tool though, since it's specifically tailored for existing games). I believe most N64 emulators are like that though, since cycle by cycle emulation gets incredibly slower as the complexity of the systems increases.
User avatar
janzdott
Posts: 42
Joined: Fri Oct 04, 2013 11:56 pm

Re: First steps in writing an emulator

Post by janzdott »

Thanks guys. That answered all my basic questions. So, for memory mirroring, the NES has to know when memory gets accessed, right? Would it work if I kept an array of function pointers that get called when the memory is read from or written to? And about the whole CPU testing... I found a website that had programs that test the CPU. It might have been this website actually, I don't remember. But that'll definitely be my next step. Thanks guys :D
User avatar
Dwedit
Posts: 4924
Joined: Fri Nov 19, 2004 7:35 pm
Contact:

Re: First steps in writing an emulator

Post by Dwedit »

Also, this really should be moved to the NESemdev section...
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!
User avatar
janzdott
Posts: 42
Joined: Fri Oct 04, 2013 11:56 pm

Re: First steps in writing an emulator

Post by janzdott »

Well it says NESemdev now, but someone must've moved it. Sorry! I'll post any further questions I have in this thread when they come up
User avatar
cpow
NESICIDE developer
Posts: 1097
Joined: Mon Oct 13, 2008 7:55 pm
Location: Minneapolis, MN
Contact:

Re: First steps in writing an emulator

Post by cpow »

janzdott wrote:Would it work if I kept an array of function pointers that get called when the memory is read from or written to?
Yep that would work.
janzdott wrote:And about the whole CPU testing... I found a website that had programs that test the CPU. It might have been this website actually, I don't remember. But that'll definitely be my next step. Thanks guys :D
I gathered oodles of them and put them up on GitHub here.
User avatar
ulfalizer
Posts: 349
Joined: Fri Mar 08, 2013 9:55 pm
Location: Linköping, Sweden

Re: First steps in writing an emulator

Post by ulfalizer »

For what it's worth, I do "slow" CPU emulation with interrupt polling in each instruction and no prediction, and CPU emulation accounts for about 4-5% of the runtime in my emulator (out of a total of using about 40% of one core on my two-year-old Core-i7 2600K). Most of that is in the read() and write() routines. My PPU code is very slow due to rendering pixel-for-pixel and doing sprite/bg pixel selection and sprite evaluation like the real PPU.

The point is that you should know your range of target systems before optimizing, and not optimize parts of your program prematurely on a guess that they'll be significant. For modern desktop systems, you can usually get away with doing the straightforward thing in an NES emulator.
User avatar
ulfalizer
Posts: 349
Joined: Fri Mar 08, 2013 9:55 pm
Location: Linköping, Sweden

Re: First steps in writing an emulator

Post by ulfalizer »

When it comes to inlining, one thing you should definitely do is whole-program/link-time optimization. When you're callings lots of function per tick, letting the compiler inline across compilation units can help a lot.
User avatar
janzdott
Posts: 42
Joined: Fri Oct 04, 2013 11:56 pm

Re: First steps in writing an emulator

Post by janzdott »

ulfalizer wrote:For what it's worth, I do "slow" CPU emulation with interrupt polling in each instruction and no prediction
Hmm, I was going to write a interrupt function that just sets the program counter whenever it's called. How did you set it up to check for interrupts every cycle? I would imagine that's how the CPU actually handles interrupts, but I'm not very knowledgeable about this stuff... yet :wink:

Though I do have about half of my opcodes written now. But I can't test it until they're all done. It's probably gonna be riddled with bugs. I'll stay up late working on it, and I'll find out tonight!

Oh, and one more dumb question to clarify something haha. Each memory read/write uses one cycle, right? I've been looking at a table that shows how many cycles each opcode uses. Do I just waste dummy cycle(s) for the PPU and APU to catch up when the number of cycles in my opcode doesn't match the table? I'm a little confused by this, and obviously keeping the timing correct is very important.
User avatar
ulfalizer
Posts: 349
Joined: Fri Mar 08, 2013 9:55 pm
Location: Linköping, Sweden

Re: First steps in writing an emulator

Post by ulfalizer »

janzdott wrote:
ulfalizer wrote:For what it's worth, I do "slow" CPU emulation with interrupt polling in each instruction and no prediction
Hmm, I was going to write a interrupt function that just sets the program counter whenever it's called. How did you set it up to check for interrupts every cycle? I would imagine that's how the CPU actually handles interrupts, but I'm not very knowledgeable about this stuff... yet :wink:
The CPU checks for interrupts each instruction. The precise details are at http://wiki.nesdev.com/w/index.php/CPU_interrupts, though it's overkill to get the timing exactly right when starting out (you could just check for pending interrupts between instructions). In addition to setting the program counter, an interrupt also saves the old program counter and the flags on the stack.
janzdott wrote:Oh, and one more dumb question to clarify something haha. Each memory read/write uses one cycle, right? I've been looking at a table that shows how many cycles each opcode uses. Do I just waste dummy cycle(s) for the PPU and APU to catch up when the number of cycles in my opcode doesn't match the table? I'm a little confused by this, and obviously keeping the timing correct is very important.
Yup, each read/write is one cycle. In fact, every cycle executed by the 6502 is either a read or a write cycle, with some being dummy reads/writes that don't do any useful work. Those can still be significant for some games due to reads/writes to certain addresses having side effects though - see Cobra Triangle in http://wiki.nesdev.com/w/index.php/Tric ... late_games.

You can look in http://nesdev.com/6502_cpu.txt to see what reads/writes are done for different instructions. Implementing the instructions like in that doc is feasible, and makes the timing work out "automagically" without tables. You can also factor out the fetch of the opcode and the byte after that, since all instructions do it.
User avatar
janzdott
Posts: 42
Joined: Fri Oct 04, 2013 11:56 pm

Re: First steps in writing an emulator

Post by janzdott »

ulfalizer wrote:You can look in http://nesdev.com/6502_cpu.txt to see what reads/writes are done for different instructions. Implementing the instructions like in that doc is feasible, and makes the timing work out "automagically" without tables. You can also factor out the fetch of the opcode and the byte after that, since all instructions do it.
Thanks, that's a pretty helpful page. I hadn't read that one before. But I'm a little confused. It says, "The processors also use a sort of pipelining. If an instruction does not store data in memory on its last cycle, the processor can fetch the opcode of the next instruction while executing the last cycle." So only the opcodes that don't store data in memory on the last cycle do that, or do all of them? Is it necessary to emulate that behavior?

And by the way, thanks for helping guys. I wasn't expecting this forum to be as active as it is! Time to get crackin' on the keyboard and load up a test program and get this CPU up and running tonight :twisted: I'm expecting bugs up the wazzoo though haha
User avatar
janzdott
Posts: 42
Joined: Fri Oct 04, 2013 11:56 pm

Re: First steps in writing an emulator

Post by janzdott »

Well I tried out a CPU test ROM, and a hard time getting it to work, but there was a problem with my ROM loading. I got it working now, and it executes a lot of code until it hits a loop. I'm not sure if that's intended or not. I do have 4 opcodes that aren't implemented yet. I wrote a function that dumps the memory to a text file. My next step is to write one that dumps the opcode and all the registers for each cycle, so I can compare it to the correct log file and see what problems I have to fix. :D
Post Reply