@PlainOldPants The raw palette will send its RGB values to the shaders in normalized 0…1 values. This is what we have from NESDev:
Standard Video
| Type |
IRE level |
Voltage (mV) |
| Peak white |
120 |
|
| White |
100 |
714 |
| Colorburst H |
20 |
143 |
| Black |
0 |
0 |
| Blanking |
0 |
0 |
| Colorburst L |
-20 |
-143 |
| Sync |
-40 |
-286 |
NES Measurements
| Signal |
Potential |
IRE |
| SYNC |
48 mV |
-37 IRE |
| CBL |
148 mV |
-23 IRE |
| 0D |
228 mV |
-12 IRE |
| 1D |
312 mV |
≡ 0 IRE |
| CBH |
524 mV |
30 IRE |
| 2D |
552 mV |
34 IRE |
| 00 |
616 mV |
43 IRE |
| 10 |
840 mV |
74 IRE |
| 3D |
880 mV |
80 IRE |
| 20 |
1100 mV |
110 IRE |
| 0Dem |
192 mV |
-17 IRE |
| 1Dem |
256 mV |
-8 IRE |
| 2Dem |
448 mV |
19 IRE |
| 00em |
500 mV |
26 IRE |
| 10em |
676 mV |
51 IRE |
| 3Dem |
712 mV |
56 IRE |
| 20em |
896 mV |
82 IRE |
I’m changing black to 0 because we are going to assume no setup on black. I believe blargg did his own measurements, but can’t find them. Do they line up with this chart? I’m concerned about the repeatability of this measurement and how the IRE values were derived (looks like assuming 1 V is 100 IRE and defining 0 IRE as 312 mV. We know 0D must be less than 0 IRE because of how it can be interpreted as a sync on some TVs).
Let’s simplify this to only the grayscale palette values and ignore the IRE:
| Signal |
Potential |
| 0D |
228 mV |
| 00 |
616 mV |
| 1D |
312 mV |
| 10 |
840 mV |
| 2D |
552 mV |
| 20 |
1100 mV |
| 3D |
880 mV |
| 30* |
1100 mV |
*Not measured, assuming same as $20.
R value from the raw palette represents a pair of voltages indexed from 0 to 3:
| R-value (Normalized) |
Potential Low |
Potential High |
Vpp |
| 0 |
228 mV |
616 mV |
388 mV |
| 1/3 |
312 mV |
840 mV |
262 mV |
| 2/3 |
552 mV |
1100 mV |
548 mV |
| 1 |
880 mV |
1100 mV |
220 mV |
However, when there’s emphasis, it will modify this only when the emphasis attenuator is active:
| R-value (Normalized) |
Potential Low |
Potential High |
Vpp |
| 0 |
192 mV |
500 mV |
208 mV |
| 1/3 |
256 mV |
676 mV |
420 mV |
| 2/3 |
448 mV |
896 mV |
448 mV |
| 1 |
712 mV |
896 mV |
184 mV |
We can turn that into an array:
[[0.228, 0.616],
[0.312, 0.840],
[0.552, 1.100],
[0.880, 1.100],
[0.192, 0.500],
[0.256, 0.676],
[0.448, 0.896],
[0.712, 0.896]]
x is the low-level PPU clock output position which we calculate ourselves based on pixel position, current field, and if we’re playing Battletoads. In the active video portion there are 256 pixels corresponding to 2048 clock cycles. We can calculate Y:
Convert R-value into an appropriate integer, 0 to 3.
Check which attenuator bits (B) are set AND check if the attenuator color cycle is active for this x based on which bits are set. If it is, add 4 to R-index.
Check if the current cycle for the given hue (G) gives us voltage high or voltage low.
Y(x)= Array[R-index + 4 * Emphasis(B, x)][high or low from VHL(G, x)]
Emphasis(B, x) is a function that returns 0 or 1, VHL(G, x) is a function that returns 0 or 1.
Do I have that right? If so, I can figure out how to handle G and B values later, and then finally scaling the voltage levels to the appropriate (unenforced) range, 0 to 1 corresponding to 0 to 100 IRE.