Oh cool. So it returns a possibly unstable value when it happens. This is worth looking into. Will do that first then.
Many, many years ago (ten, probably), I wrote a test ROM to start trying to emulate this behavior.
I loaded it onto my SNES copier (Super UFO 8.3j), and it froze the system. The copier stopped working after that. Luckily I had a second one.
I am 99% sure this was just coincidental. But it completely scared me away from trying to test bus conflicts and hardware crashes.
If anyone's willing to give it a go though, this and the SNES CPU revision 1 DMA<>HDMA crash would be very good things to emulate. They're definitely problems homebrew devs could accidentally run into. (and in fact, Parallel Worlds did with the DMA crash.)
The actual values are meaningless - the important part is that the results should match the hardware values (+/- 1, or sometimes +/- 10 when dram refresh gets in the way)
The big revolution for bsnes' timings was me writing two key functions that I use in all of my test_* ROMs:
First, write a function that you can call, and once it returns, Vcounter=0, and Hcounter=0 (not Hdot ... the actual counter.)
Second, write a function that when called, will consume N master clock (21mhz) cycles, where N is the value in the 16-bit A register.
With these two tests, you can get 100% stable results every time you run a timing test. You can write tests that write to PPU registers at exact moments, and then log the results, etc.
If you're not sure how to do it, some of my test ROMs that Evan rehosted on snescentral.com's homebrew page should have the source with them. But I went with a really gross brute-force code generating method for doing them. I'm sure you could do much better ^-^;
I'd highly recommend you make these two functions for your own tests. Use bsnes or Mesen tracelogs that store Vcounter/Hcounter per CPU instruction to ensure the functions are correct.
Once you have this, DRAM refresh is pretty stable. When you want to get into hopeless pedantry, the actual cycle where DRAM refresh fires each scanline varies between CPU revision 1 and 2, but it's a million times easier than the absolute depraved insanity that is the Sega Genesis' four asynchronous DRAM refresh behaviors ^-^;
You could cheat by merging every discreet left + right pair back down to 256.
A better way to do it is to blend every pixel 50% with the *input* (not output) pixel before (or after) it. Here's my code for it (supports 24-bit and 30-bit color):
Code: Select all
if(colorBleed) {
uint32 mask = depth == 30 ? 0x40100401 : 0x01010101;
//note: this isn't demanding enough for #pragma omp parallel for
//unlike with snes_ntsc or HQ2x, OpenMP will just make it slower.
for(uint y : range(height)) {
auto target = output + y * width;
for(uint x : range(width)) {
auto a = target[x];
auto b = target[x + (x != width - 1)];
target[x] = (a + b - ((a ^ b) & mask)) >> 1;
}
}
}
I believe the credit for this goes to either anomie or blargg. Definitely blargg's math blending trick in any case.
Pseudo-hires is used to blend two 256-width layers together, and hires is used to draw onw 512-width layer.
However, it is absolutely possible to draw a 512-width layer using pseudo-hires, or two 256-width layers using hires, if you interleave the tile data appropriately. Now make a demo where you switch between the two with a single button press and what you find is that the output looks 100% identical. That means there is light blurring (as a result of analog video) on a real SNES in both pseudo-hires and true hires mode. So, even though it makes true hires 512-width text (eg G.O.D., Marvelous, Rudra no Hihou, etc) look a bit worse ... it should be blended always.
It's perfectly fine to require blargg's snes_ntsc to simulate the hires blending, but if you want the lightest-weight filter that won't distort the image more than necessary to simulate the pseudo-hires translucency effects, the above code should get the job done.
Up to you of course, Sour ^-^;