Got any tips for Early NES Emulator Development?

Discuss emulation of the Nintendo Entertainment System and Famicom.

Moderator: Moderators

ReaperSMS
Posts: 174
Joined: Sun Sep 19, 2004 11:07 pm
Contact:

Post by ReaperSMS » Mon Mar 24, 2008 12:49 am

__assume sounds like VC only. Can't recall ever hearing about it for GCC.

In most cases, the range check won't mean dick for fps. # of ifs mostly depends on the code and the compiler, and where that if is. If it's per frame, who cares, per scanline, it might matter, per pixel and yeah, you've done something horribly wrong.

WedNESday
Posts: 1236
Joined: Thu Sep 15, 2005 9:23 am
Location: Berlin, Germany
Contact:

Post by WedNESday » Mon Mar 24, 2008 3:25 am

Fx3 wrote:<offtopic>

1. Depending of the source, one "if()" less means +2FPS.
2. value = table[index] is faster than value = *(table + index).
3. "value = color"; if( cond ) value &= mask" is slower than value = color & mask.

Boo...
</offtopic>
1. Only 2 FPS? And the rest dude. You stick another if/switch inside of the pixel function, and you will lose a lot more than that.
2. 3. You are dead right about that.

Marty
Posts: 40
Joined: Fri Nov 12, 2004 5:02 am

Post by Marty » Mon Mar 24, 2008 5:57 am

mozz wrote:By the way, another extension I read about somewhere, but I've never tried it? (It might be MSVC-only)

Code: Select all

 __assume(x < 256)
MSVC-only afaik. It optimizes nicely with switch-cases if using default: __assume(0);. Doesn't seem to help much otherwise, or at least not on the 2005 edition. Still nice to have for production code, using something like this:

Code: Select all

#include <cassert>

#ifdef NDEBUG
 #if defined(_MSC_VER) && _MSC_VER >= 1300
  #define NES_ASSERT(x) __assume(x)
 #else
  #define NES_ASSERT(x) ((void)0)
 #endif
#else
 #define NES_ASSERT(x) assert(x)
#endif
Fx3 wrote:2. value = table[index] is faster than value = *(table + index).
If that's true, I'd switch compiler if I were you. They are the exact same thing. Just different ways of writing it. Here are others:

Code: Select all

value = index[table];
value = *(index+table);

User avatar
Dwedit
Posts: 4412
Joined: Fri Nov 19, 2004 7:35 pm
Contact:

Post by Dwedit » Mon Mar 24, 2008 9:48 am

Never forget to make "Release" builds. Debug builds have optimizations turned off.
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!

mozz
Posts: 94
Joined: Mon Mar 06, 2006 3:42 pm
Location: Montreal, canada

Post by mozz » Mon Mar 24, 2008 10:59 am

ReaperSMS wrote:__assume sounds like VC only. Can't recall ever hearing about it for GCC.

In most cases, the range check won't mean dick for fps. # of ifs mostly depends on the code and the compiler, and where that if is. If it's per frame, who cares, per scanline, it might matter, per pixel and yeah, you've done something horribly wrong.
I guess NES emulators do more work per instruction than some other kinds of bytecode interpreter. (Maybe the project I was thinking of was a Smalltalk or Java bytecode interpreter. Those only do a couple instructions worth of work for many of their bytecodes, and the dispatch overhead can be quite significant.)

ReaperSMS
Posts: 174
Joined: Sun Sep 19, 2004 11:07 pm
Contact:

Post by ReaperSMS » Mon Mar 24, 2008 11:28 am

Not so much less work per instruction as it is a low instruction rate overall. Other bytecode systems don't bother with execution throttling, as they aim at peak performance. For a NES emulator, there's not a whole lot of point to breaking 1.789MHz, which isn't too difficult given that individual instruction handlers burn 2-7 virtual cycles a piece.

Most of the time will be spent in the PPU/APU.

Now, branching certainly costs inside the CPU core, but it doesn't matter much in the end when you consider the sheer clock advantage on the host. You could burn nearly 3000 cycles per instruction and still keep up. Most of the time, you won't break 50 cycles per instruction. Also, no amount of hinting to clean up the dispatch will help when the indirect jump through the jumptable (and it will almost certainly be a jumptable) is unpredictable. The only way around that really is dynarec or STC, which are needlessly complicated for the problem at hand.

User avatar
MottZilla
Posts: 2835
Joined: Wed Dec 06, 2006 8:18 pm

Post by MottZilla » Mon Mar 24, 2008 2:24 pm

Thank you ReaperSMS for your explanation. I have gotten my renderer working off the "loopy_v" and "loopy_t" terms. And for many games it is fine. But I seem to be having an isue whenever a game modifys scrolling mid-frame usually via Sprite 0 waiting.

What happens is the static area at the top in games such as SMB and Castlevania, the bar is indeed static. But the area below the status bar, constantly jiggles horizontally. From what it looks like to me, it seems that perhaps every frame the scrolled portion of background is drawn 1 pixel further right until it hits 8 and wraps back down to 0. I'm not sure what is causing it yet. But I am properly fetching H and HV from loopy_t on hblank I believe.

ReaperSMS
Posts: 174
Joined: Sun Sep 19, 2004 11:07 pm
Contact:

Post by ReaperSMS » Mon Mar 24, 2008 2:32 pm

Sounds like you've got FH screwed up somehow, off by one or so.

User avatar
Zepper
Formerly Fx3
Posts: 3223
Joined: Fri Nov 12, 2004 4:59 pm
Location: Brazil
Contact:

Post by Zepper » Mon Mar 24, 2008 2:33 pm

<newbie> What is the cool usage of assert() after all? </newbie>

User avatar
loopy
Posts: 399
Joined: Sun Sep 19, 2004 10:52 pm
Location: UT

Post by loopy » Mon Mar 24, 2008 4:18 pm

Last edited by loopy on Wed Aug 20, 2008 11:36 am, edited 1 time in total.

mozz
Posts: 94
Joined: Mon Mar 06, 2006 3:42 pm
Location: Montreal, canada

Post by mozz » Mon Mar 24, 2008 6:44 pm

Fx3 wrote:<newbie> What is the cool usage of assert() after all? </newbie>
assert(expr) is a way of saying "at this point in the program, (expr) is true". In a debug build, if it turns out to actually be false at run-time, the program will display an error message and exit. But in a release build (one with NDEBUG defined) the code assert(whatever) is defined away to nothing.

So, some rules of thumb:

* Use assert to check your assumptions at run-time. It can help you catch programming mistakes (bugs), and verify assumptions about the host compiler/system (e.g. if your program requires that sizeof(void*)==4 in order to function properly, then assert(sizeof(void*)==4) somewhere near the top of main() seems like a good idea).

* Use assert(0); or assert(!"Should never get here"); or something like that, for places in the control flow that should be unreachable if your program is working correctly. (E.g. in the default case of a switch statement, if the program is never supposed to go outside of the regular cases of the switch.) The idiom assert(0) can be thought of as saying "assert not reached".

* DON'T use assert to detect run-time errors (such as bad input from the user, or bad data read from a file). Because assert() compiles to nothing in release builds. Use regular if statements and write normal error handling code for those cases.

* Some people like to program in a "design by contract" style, where each routine has certain "preconditions" which must be true when the routine is called, and then is guaranteed to meet certain "postconditions" when it finishes. There are also sometimes "invariant conditions" such as a loop invariant (must be true on each iteration of the loop), or a class invariant (must be true at the end of the constructor, and must be true at the beginning and end of each method call except for the destructor). Anyway, assert can be used to check these things at run-time, though its a bit more clumsy than doing it in a language with language-level support for DBC. But its still very workable.


Assertions are a form of documentation: they declare the intent of the programmer ("things are supposed to be like this, or my program is not working correctly"), but they declare it in a way that can be checked by the compiler at run-time (at least in debug builds). If you use them well, they are a handy tool to help you get confidence that your code is actually working the way you think it is working.

User avatar
MottZilla
Posts: 2835
Joined: Wed Dec 06, 2006 8:18 pm

Post by MottZilla » Tue Mar 25, 2008 4:29 pm

I figured out why it was jiggly. For whatever reason, my timing is not correct as far as PPU and CPU sync, or perhaps just timing of VBlank flag setting.

When I read the trace output from my emulator, the problem is that the scroll is being updated outside of HBlank, causing the FineX for the split scroll area to be incremented a random amount of times before reaching the area it was intended to be used at.

So I need to figure out what exactly is wrong with my PPU timing. I've noticed 2 problems actually. One of them is the jiggle from writing outside HBlank, but there is another issue when playing for a few minutes it seems like its starting to render the picture at the wrong address or something like it might be a sync issue. I'll have to figure it out. I think the most likely issue is the timing between when VBlank's flag is set and when games expect HBlank is, is incorrect in my emulator.

I did make a temporary fix which checks if there is a mid-frame finex write outside of hblank making it delay it till hblank. But that is not ideal as I shouldn't have to do that when I have the timing correct.

User avatar
MottZilla
Posts: 2835
Joined: Wed Dec 06, 2006 8:18 pm

Post by MottZilla » Tue Mar 25, 2008 6:19 pm

I found out another bunch of issues and now it's actually looking much nicer. For one thing I misarranged things and was corrupting the VRAM pointer by updating it when it shouldn't be and that sort of thing. Now Ninja Gaiden and Zelda look great. Infact now everything seems to look great, but there's a strange issue in Zelda 2 I have to look at what is causing that...

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

Post by Disch » Tue Mar 25, 2008 7:17 pm

MottZilla wrote:but there's a strange issue in Zelda 2 I have to look at what is causing that...
<hunch>

don't allow the user to press both left+right or up+down at the same time. Some games didn't count on an NES controller making this possible and thus will gritch very weirdly when the user presses both simultaneously.

Games I know of where this is an issue includes Battletoads and Zelda 2.

</hunch>

User avatar
Zepper
Formerly Fx3
Posts: 3223
Joined: Fri Nov 12, 2004 4:59 pm
Location: Brazil
Contact:

Post by Zepper » Tue Mar 25, 2008 7:37 pm

yeah...

Post Reply