This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

Changing Palette Multiple Times During Refresh

Changing Palette Multiple Times During Refresh
by on (#63171)
Is it possible to modify the palette data using timed splits? I've read that you can access the palette during hblank (as long as you turn off bits 3 & 4 of $2001) but I've so far not managed to make it work.

Neil

by on (#63173)
Games that do change the palette turn off the screen first, do the writes, then turn is back on. Remember that the "safe time" to turn off the screen is during rendering on the right half of the screen, before pixel 255 has rendered, but other games turn off the screen at different times.

by on (#63175)
This can be tricky, since just the time of HBlank is not enough to do anything meaningful. You have to set the address of the color(s) to be changed, change them, and reset the scroll. HBlank is not enough for all that, so you'll probably have to disable rendering for a while. But them you have the issue of color glitches, since the PPU will render the color pointed by the VRAM address while rendering is off.

It can definitely be done though, but it will probably require some trial and error, preferably on real hardware, since recently we have noticed that emulators are not particularly accurate when it comes to disabling/enabling rendering mid-frame.

by on (#63176)
tokumaru wrote:
This can be tricky, since just the time of HBlank is not enough to do anything meaningful. You have to set the address of the color(s) to be changed, change them, and reset the scroll. HBlank is not enough for all that, so you'll probably have to disable rendering for a while. But them you have the issue of color glitches, since the PPU will render the color pointed by the VRAM address while rendering is off.

It can definitely be done though, but it will probably require some trial and error, preferably on real hardware, since recently we have noticed that emulators are not particularly accurate when it comes to disabling/enabling rendering mid-frame.


Hmmm, yes that's pretty much what I've been seeing so far.

Someone over on the chipmusic forum wondered if it would be possible to do a matrix of independently coloured squares as a kind of LED visualizer. It was my thinking that the only way you could *probably* do it would be to change the palette every row of squares.

See here;

http://chipmusic.org/forums/topic/1714/ ... d-nes-rom/

It would be quite a cool idea if it could be pulled off.

by on (#63177)
Since you have a couple of black scanlines between each row, you can probably do the palette changes during hblank. As mentioned, you can't do them mid-scanline because you'll at the very least get little glitches of the color you're writing. I figure you can write at least three colors inside each hblank. Every fourth color would be black, so that it would show during the scanline. So I imagine you could write 12 colors in four scanlines. You'd preload them into A, X, and Y, so you could do three $2007 writes in a row.

You could even do timed writes to $2001 during the scanline, and control color tint for each square, so that you could get a bigger palette. You just need something to generate all this color data.

by on (#63181)
Yep, that's what I thought.

I knocked up a nametable for this based on 3 x 3 char squares with a 1 char black border around each one (thus keeping it attribute friendly). That gives you 8 x 7 squares so you'd need 3 palettes (as you say, every 4th colour would be black so that gives you 3 palettes of 3 colours = 9).

Therefore, if you had 8 black scan lines between each row, could you change palette 1, then on the next scan line change palette 2, then palette 3 - all in the space of the 8 black scan lines?

Do you still have to turn off the screen during hblank in order to write the palette? I can't see how you'd have enough time?

From what I've read you should;

1) wait for x>255
2) turn screen off ($2001=$08?)
3) write palette
4) turn screen on, write scroll registers

is that right?

by on (#63184)
Sounds about right, though you'd do $2001=0 to disable rendering, and restore scrolling before you enable the screen. I think you've got to do combinations of $2005 and $2006, because $2005 only affects the temporary register, which isn't fully copied to the address counters during hblank. I was messing with writing scroll registers yesterday and it's apparently pretty involved to restore them properly. I haven't done much mid-frame PPU effects, so hopefully someone else here has more experience with the proper values to write.

The main thing I was thinking about was avoid visual glitches. You turn rendering off, OK, no glitch, as the PPU still shows the background color. Then you set the address to $3F01 off-screen, then write to $2007 three times quickly. That's a 12-cycle window where you'd get a glitch if it were part of the visible scanline. After that, the PPU address is at $3F04, which you should have set to black in initialization. You can then wait while that scanline is displayed (black), while you get your next three values ready to write in the next hblank. After doing that for four scanlines, you've rewritten all 12 entries, and are again sitting on a black entry. Then you just need to restore the scrolling registers. Assuming that takes you another scanline, you only need five black scanlines between rows.

Once I've got this new PPU synchronization technique documented (almost finished the PAL documentation), I'll probably mess around with this to see how many palette entries you can rewrite in hblank. Considering you can only get 7 $2007 writes at most, rewriting three is probably reasonable.

by on (#63188)
Tough gig :)

Easy enough to do the splits and change the palette but it's the scroll that's giving me trouble.

Presumably you have to set the Y scroll to compensate for turning the screen on/off e.g if your first split is 24 scan lines down the screen, you need to offset the Y scroll register by -24 scan lines so that once you turn the screen back on it's being drawn from the correct place?

I can't even work out how to write the scroll register properly LOL

:)

by on (#63191)
Resetting the scroll is pretty easy, specially if you don't need fine Y positioning (i.e. you always reset the scroll to the top of a tile, never the middle of it), which in this case I don't think you do.

You might be able to do it by simply writing to $2006 twice. You have to form a 16-bit value that looks like this: 0000NNYYYYYXXXXX (same format you'd use when writing tiles to the name tables).

NN = name table index, %00 to %11;
YYYYY = tile row, 0 to 29;
XXXXX = tile column, 0 to 31;

I'd probably keep that 16-bit value in RAM. Since each row of leds is 3 tiles tall and there is 1 tile of empty space before the next one, I'd add 128 (32 * 4) to that value before writing it to $2006 in order to set the new scroll to 4 tiles below last time.

by on (#63192)
As was said, writing in the order of $2006, $2005, $2005, $2006 is required. Should be a pretty cool trick to do the palette writing during hblank, to hide the side-effects.


Here is a raster-based PPU vertical-only scrolling loop that I use. This doesn't need to turn the screen off or anything like that. Maybe not the most useful example, but maybe fun anyways. scroll_table is in RAM, and is a unique vertical scroll position for every scanline. The look-up tables are here: vram_lo.bin and vram_hi.bin.

Code:
                  ldy #0
   scanline_loop:
                  lda scroll_table,y            ; 4    4
                  tax                           ; 2    6
                  lda vram_addr_hi,x            ; 4    10
                  sta $2006                     ; 4    14
                  stx $2005                     ; 4    18
                  lda #0                        ; 2    20
                  sta $2005                     ; 4    24
                  lda vram_addr_lo,x            ; 4    28
                  sta $2006                     ; 4    32
                                                ;
                  lda irrational_counter        ; 3    35
                  clc                           ; 2    37
                  adc #$55                      ; 2    39
                  sta irrational_counter        ; 3    42
                  bcc @nowhere                  ; 2/3  44.6
   @nowhere:                                    ;
                                                ;
                  ldx #11                       ; x*11 + 1
   :                                            ;
                  dex                           ;
                  bne :-                        ;

                  nop
                  nop
                  nop



                                                ;
                                                ;
                  iny                           ; 2    46.6
                  cpy #162                      ; 2    48.6
                  bne scanline_loop             ; 3    51.6
                                                ;
                                                ; 2    53.6
                                                ; 59

by on (#63194)
tokumaru wrote:
You might be able to do it by simply writing to $2006 twice.


I don't think you can, I had a different effect in mind and I could have been doing it wrong, but I remember having all sorts of trouble trying to do any mid-frame vertical scrolling until I found out about the specific order to write both the regs in.

But still, you could be right if VRAM address/nametable data is in the right position, since this is easier than changing it every single line.

by on (#63197)
Memblers wrote:
tokumaru wrote:
You might be able to do it by simply writing to $2006 twice.

I don't think you can

I think you can if you don't need fine scrolling. In my game, which enables rendering late and needs fine scrolling, I write to the registers in the order you said ($2006, $2005, $2005, $2006), and it works great. But AFAIK (based on loopy's doc), the reasons we need the $2005 writes are because the first write to $2006 screws up the fine Y scroll (so we need to fix it through $2005) and because $2005 is the only way to set fine X scroll.

It seems that the problems are related to fine scrolling only, so unless the fine X scroll gets corrupted when rendering is off, I'm pretty sure you can get away with using $2006 only if you don't want to fine scroll at all.

by on (#63206)
(Started writing this and then noticed others got to it before. still, I think this might give some more explanation on what's happening :)

The skinny on NES scrolling is still the best reference on this. Though I know some people have had difficulty getting a grip on it, so here's a "quick" summary.

- $2005 and $2006 both enable writing to the same temporary register 't'. 't' is normally copied into the vram address 'v' only at start of the first scanline, but v may be partially updated during rendering by writing $2005 and $2006. v is what the PPU uses for the addressing and fine scrolls when rendering the screen.

- The layout of the 18-bit register v is yyyVHYYYYYXXXXXxxx. HXXXXXxxx is the 9-bit x scroll coordinate and VYYYYYxxx is the 9-bit y scroll coordinate.

- The double-write functionality of $2005 and $2006 shares the *same toggle*. Thus, first writing to $2006 and then to $2005 would write the high byte of $2006 as usual and then the Y-scroll instead of the X-scroll, since the first write toggled the double-write latch for $2005 as well.

- Writing the second byte to $2006 is the *only* way to update the Y-scroll during rendering, transferring t to v.

The way some games used $2005 to do split-screen effects by changing the x-value was by writing $2005 twice during on a particular scanline. Quoting Loopy's doc, this is what happens:

2005 first write:
t:xxxxxxxxxxxABCDE=d:ABCDExxx
x=d:xxxxxABC

Or, if you prefer:
t[4:0] = d[7:3]
fine scroll x is directly set to d[2:0]

- t now contains 00?????? ???XXXXX

2005 second write:
t:xxxxxxABCDExxxxx=d:ABCDExxx
t:xABCxxxxxxxxxxxx=d:xxxxxABC

Or, if you prefer:
t[9:5] = d[7:3]
t[14:12] = d[2:0]

- t now contains 0yyy??YY ???XXXXX

scanline start (if background or sprites are enabled):
v:xxxxxAxxxxxBCDEF=t:xxxxxAxxxxxBCDEF

Or, if you prefer:
v[13] = t[10]
v[4:0] = t[4:0]

Note here that at scanline start, the bits in v corresponding to the Y-scroll value are *NOT* copied from t - they remain as they were. Thus, changing the Y-scroll through $2005 during rendering does nothing at all.

The most flexible way to update v if by actually writing $2005 and $2006 in the order $2006,$2005,$2005,$2006. To see what happens here, again refer to Loopy's doc:

2006 first write:
t:xxABCDEFxxxxxxxx=d:xxABCDEF
t:ABxxxxxxxxxxxxxx=0 (bits 14,15 cleared)

Or, if you prefer:
t[13:8] = d[5:0]
t[15:14] = '00'
- t now contains 00yyVHYY ????????

2005 'second' write: (*TOGGLE FLIPPED BY WRITE TO $2006*)
t:xxxxxxABCDExxxxx=d:ABCDExxx
t:xABCxxxxxxxxxxxx=d:xxxxxABC

Or, if you prefer:
t[9:5] = d[7:3]
t[14:12] = d[2:0]

- t now contains 0yyyVHYY YYY?????

2005 'first' write:
t:xxxxxxxxxxxABCDE=d:ABCDExxx
x=d:xxxxxABC

Or, if you prefer:
t[4:0] = d[7:3]
fine scroll x is directly set to d[2:0]

- t now contains 0yyyVHYY YYYXXXXX

2006 second write:
t:xxxxxxxxABCDEFGH=d:ABCDEFGH
v=t

Or, if you prefer:
t[7:0] = d[7:0]
v[17:3] = t[14:0]

- t now contains 00yyVHYY YYYXXXXX
- v has been updated to yyyVHYYYYYXXXXXxxx (highest bit of yyy got implicitly set to 0)

scanline start (if background or sprites are enabled):
v:xxxxxAxxxxxBCDEF=t:xxxxxAxxxxxBCDEF

Or, if you prefer:
v[13] = t[10]
v[4:0] = t[4:0]

Thus, the trick is to consistently write the same bits over again redundantly - just to get that update of v that the second write to $2006 triggers. This requires some bit shuffling that's best done using lookup tables, like in Memblers's example.

But as Tokumaru says, this is probably overkill for the application you are writing now. Since you just want to display the same cubes over and over again, you can get away with just writing zero to $2006 twice after each palette update. This will make your display start rendering from coordinate (0,0), which will just contain the same blocks of pixels separated apart. And those blocks will of course have been updated with fresh colors from your skillfully hblank-hidden palette updates. But it's still good to know the whole picture I think.

Happy coding! :)

by on (#63207)
Bananmos wrote:
Since you just want to display the same cubes over and over again, you can get away with just writing zero to $2006 twice after each palette update.

Even better than what I first imagined! It doesn't get any simpler than this.

by on (#63208)
Quick proof of concept;

http://dl.dropbox.com/u/5493868/litewall.nes

It's only doing two splits at the moment so the first, second and third (onwards) rows are switching palettes. And it's only doing one palette (i.e. 3 writes to $2007).

Yeah, it occurred to me after I'd posted the scroll problem that I didn't in fact need to sync the y scroll....doh! Just zeroing it does the trick.

by on (#63209)
Looks pretty glitch-free, congratulations! =)

by on (#63213)
Expanded it a bit;

http://dl.dropbox.com/u/5493868/litewall2.nes

This time only one split (after first row) but I'm swapping 9 colours during the 8 black pixel gap.

Top row of colours cycles slowly one way while second line cycles faster in the opposite direction.

A little tiny glitch appears sometimes in Nestopia (Mac) - I'll put that down to emulation errors ;)

by on (#63227)
Minor note, changing the palette during in the middle of the screen makes a small glitch appear in the overscan area. Emulators don't show it, but TV capture cards with their wider display do see it.

by on (#63236)
Widescreen LCD TVs also show quite a bit more of the overscan than old boxy CRTs, especially on the left and right sides.

by on (#63266)
I put a bit more time in on this tonight and got the timing code into a more usable form (though it still needs a bit of tweaking as you'll see). This time I'm doing all 8 splits (well, 7) to make the whole grid animatable. I also programmed two crude animations - one cycles all the colours of the matrix from start to end, left to right. The other animates a spiral that radiates from the centre outwards. I put a simple timer in to periodically switch between the two.

One cool thing I figured out is that if I got the timing tight enough, I could squeeze the vertical gaps between the blocks enough to get an extra row of blocks (the NES resolution is 256 x 240 so doing 32 x 32 pixel blocks only gives you enough vertical height for 7 rows). Therefore, it's now a proper 8 x 8 grid!

http://dl.dropbox.com/u/5493868/litewall3.nes

by on (#63268)
What would be really cool is to change the color tint ($2001) every scanline between every box, giving you several times the color palette you currently have. That'd require really tight timing.

by on (#63290)
blargg wrote:
What would be really cool is to change the color tint ($2001) every scanline between every box, giving you several times the color palette you currently have. That'd require really tight timing.


"Tight" is quite an understatement :)

Did another update;

http://dl.dropbox.com/u/5493868/litewall5.nes

Use U/D/L/R to select the mode. Use A/B to increase/decrease animation speed.

Could do some cool stuff with the scroll text mode but I have to leave it alone for a while. I implemented two quick "controls" that you can put anywhere in the text string. One to set the background/foreground colour and the other to specify a pause. You can see both of these in the scroll text mode.

by on (#63292)
This is very cool !

by on (#64103)
I've teamed up with neilbaldwin on this. I think I took too long getting the engine revamped before another project captured his interest, so I put together my own non-interactive demo using the new engine (NTSC only):

blargg_litewall-2.zip

Using the new NMI synchronization technique, this sets the $2001 tint bits mid-scanline for every color square, and also automatically flashes between two different colors/tints, allowing a large and subtly-varying color palette. Every one of the 80 colors squares can have its own color. I'll post the engine if anyone else is interested in writing some patterns for it. It's very modular and flexible, and I've developed a good framework for doing patterns and transitions between them with minimal trouble.

There are no graphical glitches on an NTSC NES, not even in the overscan area, as you can see by these video captures. I'll be able to try on Nestopia tomorrow to see how it fares. For now it's best to assume that there are glitches in emulation.

Image Image

by on (#64105)
Nestopia 1.40 runs it fine. Want me to YouTube this one too?

by on (#64119)
In nestopia 1.4.0g-linux I see a single pixel of jitter on the left side of the squares.

This is so amazing. I feel like I'm back in the VGA days with palette cycling ;)

by on (#64131)
This is amazing ! We're back into the days where homebrew scenes brings something new !

It seems Nestopia runs fine, but I'm too lazy to try my powerpak right now to compare.

by on (#64132)
[quote="blargg"]I've teamed up with neilbaldwin on this. I think I took too long getting the engine revamped before another project captured his interest, so I put together my own non-interactive demo using the new engine (NTSC only):

Ahhh, sorry blargg. :(

At least half of my attention is always on audio stuff and I'd been working on Pulsar well before I was messing about with the litewall thing. It just happened I had a massive lightbulb-moment with the audio stuff I was doing - you know those times when something has been evading you for ages and suddenly you see the way forward then you can't ignore it?

I do get sidetracked easily though, yes. :)

Anyway, the audio stuff has hit another brick wall so I might have a go at litewall again.

Something I was wondering was if there's enough processing time to do colour addition/subtraction i.e. if two different-coloured animated cells collide or overlap. I had an idea to put two (or more but it might get too busy) colour generators at random coordinates in the grid and have them emit a stream of colour from a different hue then where the waves cross you could mix the colours.

by on (#64142)
That's really awesome.

So, theoretically, someone could convert a video or gif to 10x8 pixels and color reduce it to the full NES palette?

...At which point it would look pretty bad, but hey, full palette, right?

by on (#64143)
nice work, guys -- this is really cool!

by on (#64147)
tepples, sure, go ahead and YouTube it. It's just a work-in-progress, but I suppose it's interesting anyway.

neilbaldwin, no problem, I know how it is getting caught up in various projects. Now that I got that NMI sync technique posted, I have some attention to devote to this again. It was good to see that it could handle transitions between patterns without bogging down too much. Heh, I was going to try some color addition, mixing two patterns together. Given that hue is the upper 5 bits, and brightness is the lower 3, it's not difficult. We could of course add an extra palette that's organized to make this even easier.

UncleSporky wrote:
So, theoretically, someone could convert a video or gif to 10x8 pixels and color reduce it to the full NES palette?

The full 192-color palette (currently; haven't explored using color tint for anything more than darkening), plus 16 gray levels. :)

EDIT: tried on Nestopia, and there is a slight tint emulation glitch on the left edges of color blocks. It's hardly noticeable, so Nestopia gives a reasonable idea of what it looks like on a NES.

by on (#65493)
In case anyone's looking for the latest version of my litewall, along with a manual:

Litewall for NES/SNES