The quest for mid-screen palette changes!

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

Post Reply
User avatar
BioMechanical Dude
Formerly AlienX
Posts: 137
Joined: Fri Apr 18, 2014 7:41 am
Location: Bulgaria

The quest for mid-screen palette changes!

Post by BioMechanical Dude »

After successfully setting up the MMC3 and figured out how to use the IRQs, I decided to try something very few NES games attempt and change the palette mid-screen. One of the characteristics of the MMC3 scanline counter is that if the Sprites are loaded from Pattern Table 1 and the backgrounds are loaded from Pattern Table 0, the counter will trigger an IRQ at around pixel 260 of the current scanline. This means that we should be able to change at least one color without any visible graphical glitches.

So I started with a program that displays the following image:

Image

I added an IRQ, which triggers at scanline 120, containing the following code:

Code: Select all

COW:
  LDA #$00
  STA $2001  ;Disable rendering

  LDA $2002  
  LDA #$3F
  STA $2006
  LDA #$00
  STA $2006
  
  LDA #$16
  STA $2007  ;Change Main Background color
  
  LDA #%00011110  ;Enable sprites, Enable backgrounds
  STA $2001
  
  LDA #$01
  STA $E000 ;Disable IRQ

  RTI
I also added the following code in the NMI section to change back to the original color before the next frame starts:

Code: Select all

  LDA $2002
  LDA #$3F
  STA $2006
  LDA #$00
  STA $2006
  LDA #$21
  STA $2007
When I ran the program, I ended up with this:

Image

Now, I knew there were going to be problems. And it turns out that writing to $2006 messes with the scroll register. So I changed the IRQ code a bit and wrote to $2005, hoping to move the screen to where it should be:

Code: Select all

COW:
  LDA #$00
  STA $2001  ;Disable rendering

  LDA $2002  
  LDA #$3F
  STA $2006
  LDA #$00
  STA $2006
  
  LDA #$16
  STA $2007  ;Change Main Background color
  
  LDA #%00011110  ;Enable sprites, Enable backgrounds
  STA $2001
  
  LDA #$00       
  STA $2005
  LDA #$78            ;Set Y Scroll position to 120
  STA $2005
  
  LDA #$01
  STA $E000 ;Disable IRQ
  
  RTI
Unfortunately, this didn't change anything at all. In an online MMC3 guide, I saw that instead of writing to $2005, they wrote to $2006, like so:

Code: Select all

  LDA #%00011110  ;Enable sprites, Enable backgrounds
  STA $2001
  LDA #$00 
  STA $2006
  STA $2006
This brings the screen down a bit:

Image

I decided to see what would happen if I put in a higher value on the second write to $2006

Code: Select all

  LDA #%00011110  ;Enable sprites, Enable backgrounds
  STA $2001
  LDA #$00        
  STA $2006
  LDA #$78
  STA $2006
This brings the screen down more, but not by much:

Image

So, can somebody tell me what is the right way to go about this? How do I fix these problems?

Thanks!
Greetings! I'm That Bio Mechanical Dude and I like creating various stuff like movies, games and of course chiptunes!
You can check out my YouTube Channel.
You can also follow me on Twitter.
User avatar
pubby
Posts: 583
Joined: Thu Mar 31, 2016 11:15 am

Re: The quest for mid-screen palette changes!

Post by pubby »

lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: The quest for mid-screen palette changes!

Post by lidnariq »

BioMechanical Dude wrote:One of the characteristics of the MMC3 scanline counter is that if the Sprites are loaded from Pattern Table 1 and the backgrounds are loaded from Pattern Table 0, the counter will trigger an IRQ at around pixel 260 of the current scanline. This means that we should be able to change at least one color without any visible graphical glitches.
No, it does not mean that.

It is not possible to change one color without any visible glitches. At all. Ever.

At the very best, the scanline following the palette change will have no sprites.

But even this requires absolutely zero variation in the CPU timing. And IRQs on the 6502 always have some jitter.

The window in which you can disable rendering and write to the PPU is 64 pixels long. That's 21 CPU cycles.

In order to write a new palette entry, you need to write to PPU registers a total of 7 times. That's 28 CPU cycles, of which the first seven cycles can happen early:

STa $2006 - can be significantly early - always contains $3F
STb $2001 - timed such that the final write happens on pixel 255/6/7 - disable rendering, probably contains 0 but other values are possible
STc $2006 - the palette index to update
STd $2007 - the new palette value
STe $2006 - restore correct scroll - note that the fine "y" 4s bit is always cleared when you update the scroll register using just $2006
STf $2006 - finish correcting scroll
STg $2001 - re-enable rendering

Note: There is no time here to load your three registers with new values! (other than after "a") There are a very few don't care bits in the above sequence ("b" & $E1, "c" & $E0, "d" & $C0, "e" & $C0) that help a little, but probably not enough.

So you basically have to move this entire sequence early in order to have enough time to load the registers with the values you want to write. There will be a visible glitch and you can't cover it with a sprite (because rendering will be disabled). Furthermore, there are very strict timings on when you can disable rendering without breaking sprites for the entire remaining redraw.

If there are sprites that intersect the place where you want the palette update, you only can move the entire sequence early by 16 pixels, enough time to LDx immediate twice, or LDx zeropage once. (If there are no sprites near the split, you can move the entire sequence early by 64 pixels) But the earlier you move it, the more visible the glitch.
User avatar
Bregalad
Posts: 8056
Joined: Fri Nov 12, 2004 2:49 pm
Location: Divonne-les-bains, France

Re: The quest for mid-screen palette changes!

Post by Bregalad »

Blagg has discovered a way to get rid of jitter by abusing the $4014 sprite DMA, I don't remember the details. I don't think it's applicable to MMC3 IRQs, but rather NMI, but I might be wrong.

C64 demoes usally uses crazy hardware abuse to remove jitter entirely, and they are able to write to hardware video registers at a fully stable timing.

As for the original topic, if your goal is just to change the background colour (or even a significant part of the palette) once in the middle of the frame, for example in order to change the palette for the status bar, it's probably best to rely on a single glitchy scanline. If you want to change the background colour each line, it's possible and I did a demo about it 9 years ago, which was itself inspired by a demo by Memblers I think.
Last edited by Bregalad on Sun Nov 17, 2019 2:22 pm, edited 1 time in total.
User avatar
Dwedit
Posts: 4924
Joined: Fri Nov 19, 2004 7:35 pm
Contact:

Re: The quest for mid-screen palette changes!

Post by Dwedit »

Fantastic Adventures of Dizzy is one of the few games to do midscreen palette changes well.
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!
User avatar
rainwarrior
Posts: 8732
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: The quest for mid-screen palette changes!

Post by rainwarrior »

Since this question comes up a lot, I decided to start a wiki article on it:
Wiki: Palette change mid frame

Please feel free to edit/contribute to it.

(lidnariq: how would you feel about the Indiana Jones diagram being uploaded to the wiki?)
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: The quest for mid-screen palette changes!

Post by lidnariq »

rainwarrior wrote:(lidnariq: how would you feel about the Indiana Jones diagram being uploaded to the wiki?)
By all means!
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: The quest for mid-screen palette changes!

Post by tokumaru »

BioMechanical Dude wrote:I decided to try something very few NES games attempt and change the palette mid-screen.
There's a reason for that, you know. There are several limitations that apply to mid-screen palette changes, so most programmers come to the conclusion it's not worth the trouble.

It's just not possible to change even a single color without disrupting the background and/or the sprites for at least 1 scanline, so outside of status bars or title screens, cutscenes and the like, this is of little use. And I wouldn't trust emulators when testing this either, a lot of them aren't accurate enough to show the correct results for mid-screen PPU manipulations.
User avatar
dougeff
Posts: 3079
Joined: Fri May 08, 2015 7:17 pm

Re: The quest for mid-screen palette changes!

Post by dougeff »

BioMechanical Dude, this is one of the things I have tried and failed to do.

My understanding of the PPU, is that during rendering, the PPU is auto-incrementing the PPU address, using the same bits as 2005/2006, to fetch the correct pixels to put on the screen.

If you change 2006, midscreen, it starts fetching tiles/bytes from that new address.

Also, you can't change Y scroll, midscreen, by doing 2 writes to 2005.

You can with the 2006/2005/2005/2006 trick, however.



One easy thing you CAN do, is have the top portion of the first nametable reserved for a HUD. Set the Y scroll to avoid showing this for most of the screen. Then, near the bottom of the screen, 2 quick writes of zero to 2006 (I think) should then show the HUD at the bottom.

Or something like that.
nesdoug.com -- blog/tutorial on programming for the NES
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: The quest for mid-screen palette changes!

Post by tokumaru »

dougeff wrote:One easy thing you CAN do, is have the top portion of the first nametable reserved for a HUD. Set the Y scroll to avoid showing this for most of the screen. Then, near the bottom of the screen, 2 quick writes of zero to 2006 (I think) should then show the HUD at the bottom.
You also have to clear the fine X scroll using $2005, since $2006 doesn't affect that, otherwise the status bar will jitter left and right by up to 7 pixels as the game scrolls.
Post Reply