Got any tips for Early NES Emulator Development?
Moderator: Moderators
__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.
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.
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.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>
2. 3. You are dead right about that.
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: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)
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
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:Fx3 wrote:2. value = table[index] is faster than value = *(table + index).
Code: Select all
value = index[table];
value = *(index+table);
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 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.
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.
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.
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.
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.
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.Fx3 wrote:<newbie> What is the cool usage of assert() after all? </newbie>
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.
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.
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.
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...
<hunch>MottZilla wrote:but there's a strange issue in Zelda 2 I have to look at what is causing that...
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>