It is currently Sat Dec 16, 2017 6:17 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 76 posts ]  Go to page 1, 2, 3, 4, 5, 6  Next
Author Message
PostPosted: Sat Oct 05, 2013 12:24 am 
Offline
User avatar

Joined: Fri Oct 04, 2013 11:56 pm
Posts: 42
Location: Wisconsin
Hey guys, I'm new here. Glad I found the place :D

I'd say I'm a pretty good programmer. I've been programming for about 5 years. Started with Python, then Javascript, then C#, and just recently I've done a few things in C++. 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

Afterwards, I thought it would be interesting to write a N64 emulator. I did some research for about an hour until I came to the conclusion that it would be ridiculously hard haha. So I decided I'd try emulating a simpler system. I did some research on the Chip-8, and programmed my own, all in one sitting. I learned a lot, but I like a challenge. The Chip-8 was far too easy, so yesterday I started an NES emulator. I've been doing a LOT of reading, and a bit of programming. After what I've read and programmed so far, I can say this is definitely more challenging!

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

EDIT: Also, does anyone know where I could find source code for a very simple and organized emulator? I looked at the Nintendulator source, and it's crazy. I'm a freak about commenting, organizing, and simplifying my code. It makes it very very easy for someone else to read and understand.

_________________
Current emulator progress http://forums.nesdev.com/viewtopic.php?f=3&t=10558


Top
 Profile  
 
PostPosted: Sat Oct 05, 2013 2:18 am 
Offline

Joined: Sat Jan 23, 2010 11:41 pm
Posts: 1161
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.


Top
 Profile  
 
PostPosted: Sat Oct 05, 2013 2:45 am 
Offline
User avatar

Joined: Fri Oct 04, 2013 11:56 pm
Posts: 42
Location: Wisconsin
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?

_________________
Current emulator progress http://forums.nesdev.com/viewtopic.php?f=3&t=10558


Top
 Profile  
 
PostPosted: Sat Oct 05, 2013 2:55 am 
Offline

Joined: Sat Jan 23, 2010 11:41 pm
Posts: 1161
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.


Top
 Profile  
 
PostPosted: Sat Oct 05, 2013 11:50 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10165
Location: Rio de Janeiro - Brazil
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.

Quote:
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.

Quote:
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.

Quote:
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).

Quote:
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!

Quote:
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!).

Quote:
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.


Top
 Profile  
 
PostPosted: Sun Oct 06, 2013 1:09 pm 
Offline
User avatar

Joined: Fri Oct 04, 2013 11:56 pm
Posts: 42
Location: Wisconsin
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

_________________
Current emulator progress http://forums.nesdev.com/viewtopic.php?f=3&t=10558


Top
 Profile  
 
PostPosted: Sun Oct 06, 2013 5:10 pm 
Offline
User avatar

Joined: Fri Nov 19, 2004 7:35 pm
Posts: 3969
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!


Top
 Profile  
 
PostPosted: Sun Oct 06, 2013 7:34 pm 
Offline
User avatar

Joined: Fri Oct 04, 2013 11:56 pm
Posts: 42
Location: Wisconsin
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

_________________
Current emulator progress http://forums.nesdev.com/viewtopic.php?f=3&t=10558


Top
 Profile  
 
PostPosted: Sun Oct 06, 2013 8:55 pm 
Offline
NESICIDE developer
User avatar

Joined: Mon Oct 13, 2008 7:55 pm
Posts: 1058
Location: Minneapolis, MN
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.


Top
 Profile  
 
PostPosted: Mon Oct 07, 2013 9:16 am 
Offline
User avatar

Joined: Fri Mar 08, 2013 9:55 pm
Posts: 349
Location: Linköping, Sweden
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.


Top
 Profile  
 
PostPosted: Mon Oct 07, 2013 9:29 am 
Offline
User avatar

Joined: Fri Mar 08, 2013 9:55 pm
Posts: 349
Location: Linköping, Sweden
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.


Top
 Profile  
 
PostPosted: Mon Oct 07, 2013 3:08 pm 
Offline
User avatar

Joined: Fri Oct 04, 2013 11:56 pm
Posts: 42
Location: Wisconsin
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.

_________________
Current emulator progress http://forums.nesdev.com/viewtopic.php?f=3&t=10558


Top
 Profile  
 
PostPosted: Mon Oct 07, 2013 3:48 pm 
Offline
User avatar

Joined: Fri Mar 08, 2013 9:55 pm
Posts: 349
Location: Linköping, Sweden
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.


Top
 Profile  
 
PostPosted: Mon Oct 07, 2013 7:44 pm 
Offline
User avatar

Joined: Fri Oct 04, 2013 11:56 pm
Posts: 42
Location: Wisconsin
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

_________________
Current emulator progress http://forums.nesdev.com/viewtopic.php?f=3&t=10558


Top
 Profile  
 
PostPosted: Tue Oct 08, 2013 2:53 am 
Offline
User avatar

Joined: Fri Oct 04, 2013 11:56 pm
Posts: 42
Location: Wisconsin
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

_________________
Current emulator progress http://forums.nesdev.com/viewtopic.php?f=3&t=10558


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 76 posts ]  Go to page 1, 2, 3, 4, 5, 6  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: creaothceann, Google Adsense [Bot] and 5 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group