Changing Palette Multiple Times During Refresh

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

User avatar
neilbaldwin
Posts: 481
Joined: Tue Apr 28, 2009 4:12 am
Contact:

Changing Palette Multiple Times During Refresh

Post by neilbaldwin » Mon Jun 21, 2010 8:27 am

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

User avatar
Dwedit
Posts: 4236
Joined: Fri Nov 19, 2004 7:35 pm
Contact:

Post by Dwedit » Mon Jun 21, 2010 8:59 am

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.
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!

User avatar
tokumaru
Posts: 11469
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru » Mon Jun 21, 2010 9:12 am

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.

User avatar
neilbaldwin
Posts: 481
Joined: Tue Apr 28, 2009 4:12 am
Contact:

Post by neilbaldwin » Mon Jun 21, 2010 9:28 am

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.

User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg » Mon Jun 21, 2010 9:48 am

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.

User avatar
neilbaldwin
Posts: 481
Joined: Tue Apr 28, 2009 4:12 am
Contact:

Post by neilbaldwin » Mon Jun 21, 2010 11:14 am

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?

User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg » Mon Jun 21, 2010 12:42 pm

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.

User avatar
neilbaldwin
Posts: 481
Joined: Tue Apr 28, 2009 4:12 am
Contact:

Post by neilbaldwin » Mon Jun 21, 2010 1:52 pm

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

:)

User avatar
tokumaru
Posts: 11469
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru » Mon Jun 21, 2010 2:34 pm

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.

User avatar
Memblers
Site Admin
Posts: 3770
Joined: Mon Sep 20, 2004 6:04 am
Location: Indianapolis
Contact:

Post by Memblers » Mon Jun 21, 2010 2:37 pm

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: Select all

                  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

User avatar
Memblers
Site Admin
Posts: 3770
Joined: Mon Sep 20, 2004 6:04 am
Location: Indianapolis
Contact:

Post by Memblers » Mon Jun 21, 2010 2:45 pm

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.

User avatar
tokumaru
Posts: 11469
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru » Mon Jun 21, 2010 2:55 pm

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.

Bananmos
Posts: 468
Joined: Wed Mar 09, 2005 9:08 am
Contact:

Post by Bananmos » Mon Jun 21, 2010 4:07 pm

(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! :)

User avatar
tokumaru
Posts: 11469
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru » Mon Jun 21, 2010 4:54 pm

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.

User avatar
neilbaldwin
Posts: 481
Joined: Tue Apr 28, 2009 4:12 am
Contact:

Post by neilbaldwin » Mon Jun 21, 2010 5:16 pm

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.

Post Reply