NewRisingSun wrote:
Also, am I correct that if you want the correct NES aspect ratio, therefore going from 282 to 752 to 640, thus having a factor of 40/47 instaed of 7/8, each kernel would have to be 80 pixels wide?
It was just luck that things worked out as they currently are. There are three NES pixel alignments with regard to the color clock; 3 NES pixels = 2 colorburst clocks = 8 samples at our ~14.32 MHz sampling rate. Since we generate NTSC pixels at the same rate, that gives 8 RGB pixels out. By rescaling that to 7 pixels, I get an effective 7/8 rescale ratio. The luck is that 7/8 fit so closely, given that the ratio had to have 8 as a denominator. The ratio in my code, 7/8 = 0.875, compared to 40/47 = 0.8511, yields an error of less than 3%. It's greater, so you get slightly fewer NES pixels, but this still gives you room for around 8 overscan pixels on each side when displaying at 640x480.
If you want something more accurate, you'll have to double or triple the number of NES pixel alignments. For example, you could have 6 positions generate 16 raw pixels, then rescale to 13, but this gives you a ratio of 13/16 = 0.8125, worse than before. 9 NES->24 NTSC->20 rescaled gives 0.833333, 12->32->27 gives 0.84375, 15->40->34 gives 0.85, and finally 18 NES->48 NTSC->41 gives 0.8542 (18 NES->48 NTSC->40 gives 0.8333333, worse). There's no reason you
have to do the rescaling in the tables, it's just a lucky occurrence that I took advantage of (with a 3% error, as mentioned).
The kernels can be any size. I chose the general range of the kernel size based on where the RGB values diminish to near zero away from the center, and 14 specifically because 14 * 3 NES pixel alignments * 3 PPU phases = 126, which allows giving 128 32-bit ints for each color entry, allowing a shift instead of a multiply when finding the address of a kernel. I kept all the kernels for a particular color together since they'll usually all get used if that color is used in a frame, thus should use as few cache lines as possible.
You noticed the slight diagonal stripes through solid-brightness areas; I think these might be a symptom of the kernel needing to be a bit wider (it's not filtering, because I'm using the exact same algorithm during initialization as your original code). I'm going to be seeing if I can reduce this without using a wider kernel, since it would cause the optimized loop to have to become more complex and have more temporary variables (currently it's making the most of the current kernel sizes, with no room to spare).
Describing this I've realized that the optimized version's parameters (3 NES pixels->7 output pixels) makes it specific to the NES, so a version for another system would have to be written separately; there's no way a single code base could be made to work with all systems directly.
Quote:
For a solid color, two or three color clocks need to me illuminated the same way, otherwise the original monochrome columns remain visible, which is also why 320x200 text is still readable. Since the CGA's line rate is an integer divisor of the subcarrier frequency, the color burst reference stays the same from line to line. How would this play with your code?
I'll have to try your CGA code myself (the original code you sent me should work, right?); I'm not understanding how the TV or your code can both display the pixels as monochrome and color.
I'd be happy to explain how it works through e-mail, going one step at a time from the bginning rather than the scattered incoherent way it happens here on the boards. I don't like the kind of half-assed approach I end up taking here, but I don't see any other way since a proper explanation would involve first being sure we're coming from the same foundational concepts. Doing that requires a lack of distraction and the committment of both parties.
tepples wrote:
You probably won't need to emulate phosphors in software for the first release.
Yeah, I never set out to cover different phosphors. I just mindlessly described the decoder matrix as modeling phosphor color, which it does not.
NewRisingSun, what's your opinion on the even/odd field merging feature? What it does can best be described by its equivalent. First off, say that normally the PPU phase at the first scanline of each frame is going between 0 and 1 at the moment. So even frames say are on phase 0, and odd frames on phase 1. Some pixels on screen will probably be flickering, due to depending critically on the phase. If the emulator is running on a monitor at exactly 60 Hz, the flicker will blur together, resulting in the average of the two colors it toggles between.
If the emulator is running on a monitor that refreshes at 75 Hz, the pixels will flicker at an uneven rate in a noticeable way. The emulator could avoid this by internally rendering each frame
twice at both PPU phases, then mixing them together before showing it on-screen. This way nothing flickers and what you see (even if the emulator stops) is what you'd see with the original emulator running on a 60 Hz monitor. This is what the even/odd field merging does the equivalent of. An emulator using this would always specify the same PPU phase for each frame (no toggling), and the result would be as if it had specified 0 and 1 and mixed the two together.
Merging the even and odd fields together is fine if nothing is moving on screen, but when something is moving, the result is not the same as an emulator running on a 60 Hz refresh monitor. If something is moving across the screen smoothly, the visual merging won't occur since it's only at each particular place either on an even or odd field, so the even/odd field merging will cause things to shimmer less than they would when moving. They still shimmer somewhat, but not as much. This is the main drawback of it, but it really helps with the monitor refresh rate issue.
Can it easily be improved? Is this doing a disservice to proper NTSC emulation?