By Kevin Horton ---------------- OK folks, here it is... what I believe to probably be the final word on NES palettes... maybe, I hope. :-) I have written a small QB proggy that generates NES palettes by computing them the way an NTSC TV would. The results have been very cool I must say. With this code and a bit of adaptation, you should be able to implement hue and tint "knobs" or controls into your emulators. :-) This would allow on the fly adjustment of them as if it were a real TV. I have the "defaults" set to what looks perfect against my TV's I've tested it on, and against the Portendo screen as well. Notably, the background of the zelda intro screen is a yellowish-pink, which this captures very nicely, and the mario 1 background is a blue with a purplish tinge. Everything else looks pretty good too, and with the proper luminances, the colours seem stand out a bit better. So, here it is. It's in QBASIC, but is heavily commented so it should be easily integrated into any emulator with a minimum of effort. Note, for the SIN and COS functions, QBASIC treats them in radians. My code converts normal degrees (360 degrees in a full circle) to radians (2*pi for a full circle). filename is NTSC2RGB.BAS -------------cut here-------------------- 'NES palette generator, by Kevin Horton. 04-08-00 'It mathematically generates an NES palette that can be used by 'NESticle and other emulators that use the palette file. Only the 'first 64 colours are set, and they are stored in triplets... i.e. 'RGB, RGB, RGB. Each byte is an 8 bit colour value. 'I add the remaining portion of the NESticle palette file onto the 'end of the new one because it must be used by NESticle's user 'interface. 'Luma values were measured from my NES using the chroma/luma seperator 'on the filter matrix from Portendo's colour decoder board. 'A value of 1.0 was assigned to the brightest white which is colour '20h or 30h (both are the same). 'Chroma was found to be constant for all colours, which is what I 'expected. I fudged this at .5 which seemed to be about right. (see 'farther down for more details on this) 'The last thing I will do will be figure out what those intensity 'and monochrome bits *really* do. :-) Stay tuned for that at a 'later date. OPEN "nesticle.pal" FOR BINARY AS 1 'show the NESticle palette OPEN "test.pal" FOR OUTPUT AS 2 'open up our new test palette SCREEN 13 '------------------------------------------------------ 'Shows all of our palette bars 'don't need to worry about this :-) x = 4 'size of bars FOR a = 1 TO 64 r = ASC(INPUT$(1, 1)) \ 4 g = ASC(INPUT$(1, 1)) \ 4 b = ASC(INPUT$(1, 1)) \ 4 PALETTE a, r + g * 256 + b * 65536 PALETTE a + 64, 0 LINE (a * x, 0)-(a * x + x, 100), a, BF LINE (a * x, 101)-(a * x + x, 200), a + 64, BF NEXT p = 0 '------------------------------------------------------ 'Read our colour angles from the data table. First data entry goes into 'cols(1), second data entry goes into cols(2), etc DIM cols(16) FOR y = 1 TO 16 READ cols(y) NEXT DATA 0,240,210,180,150,120,90,60,30,0,330,300,270,0,0,0 '------------------------------------------------------ 'Our brightness table. 'br1 is for X0h colours br1(0) is for colour 00h, br1(1) is for 10h, etc 'br2 is for X1h thru XCh 'br3 is for XDh br1(0) = .5: br1(1) = .75: br1(2) = 1!: br1(3) = 1! br2(0) = .29: br2(1) = .45: br2(2) = .73: br2(3) = .9 br3(0) = 0: br3(1) = .24: br3(2) = .47: br3(3) = .77 '-- 'The value pi, for our angle calculations pi = 3.14159265# '-- 'Our initial hue value. Due to the way this is calculated, only use 'positive values here. 332 seems to be the best "standard" setting. 'A range of +/- 15-20 degrees, in about 200-300 steps would probably be 'fairly good "user adjustment" range here. 'This adjusts the colours. hue = 332 '-- 'Our initial tint value. Only values from 0 to 1 should be entered here. '0=least amount of saturation, 1=most. .5 seems to be the best "standard" 'setting here. .25 to .75 or maybe a bit less in 200-300 steps would 'probably be a good range for "user adjustment". 'This adjusts how much white is in the colours. tint = .5 '------------------------------------------------------ 'OK, this is what you were waiting for :-) The action thingy that calcs 'our palette. Ok, in the code, X is the "brightness bits" of the colour, 'and Z is our actual colour... i.e. XZh represents the NES colour value. FOR x = 0 TO 3 FOR z = 1 TO 16 'two loops s = tint 'grab tint y = br2(x) 'grab default luminance IF z = 1 THEN s = 0: y = br1(x) 'is it colour XDh? if so, get luma IF z = 14 THEN s = 0: y = br3(x) 'is it colour X0h? if so, get luma IF z = 15 THEN y = 0: s = 0 'is it colour XEh? if so, set to black IF z = 16 THEN y = 0: s = 0 'is it colour XFh? if so, set to black '-- 'This does a couple calculations. (working out from the parens) 'first, it grabs the angle for the colour we're working on out of the 'array that holds our angles. Then, it divides by 180 and multiplies by 'pi to turn the angle which was in degrees, into radians. theta = pi * ((cols(z) + hue) / 180) '-- 'This next part is where the real work is done. It is a mathematical 'representation of an NTSC chroma decoder matrix. It converts Hue, 'Saturation, and Luminance to raw RGB values. r = y + s * SIN(theta) g = y - (27 / 53) * s * SIN(theta) + (10 / 53) * s * COS(theta) b = y - s * COS(theta) '-- 'RGB must be normalized into a value from 00h thru FFh for our VGA cards :-) 'First, we multiply by 256, then make sure they don't wrap or go negative. 'Finally, they are integerized. r = r * 256 b = b * 256 g = g * 256 IF r > 255 THEN r = 255 IF g > 255 THEN g = 255 IF b > 255 THEN b = 255 IF r < 0 THEN r = 0 IF g < 0 THEN g = 0 IF b < 0 THEN b = 0 r = INT(r) g = INT(g) b = INT(b) 'The variables R,G, and B now contain our actual RGB values. 'Finally, we output these values to our palette file. PRINT #2, CHR$(r); CHR$(g); CHR$(b); 'And show them on the screen. r = r \ 4 g = g \ 4 b = b \ 4 PALETTE (x * 16 + z) + 64, r + (g * 256) + (b * 65536) NEXT NEXT '------------------------------------------------------ 'This last part simply copies the rest of the NESTICLE.PAL file onto the 'end of our palette file. FOR a = 64 TO 255 STEP .25 a$ = INPUT$(1, 1) PRINT #2, a$; NEXT CLOSE 'And that's it.