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

Status Bar + palette swap

Status Bar + palette swap
by on (#154381)
Hi everyone,

I've got a status bar which is working fine (with sprite 0 hit) here's the code in my NMI routine :

Code:
    LDA #$00
    STA $2006
    STA $2006

    STA $2005
    STA $2005
                        ; switch CHR bank
    LDA #$03
    TAX
    STA bankTable,X
                        ; switch nametable to $2400
    LDA ctrl_var
    EOR #%00000001
    STA $2000

@WaitNotSprite0:
        LDA $2002
        AND #%01000000
        BNE @WaitNotSprite0   ; wait until sprite 0 not hit

@WaitSprite0:
        LDA $2002
        AND #%01000000
        BEQ @WaitSprite0      ; wait until sprite 0 is hit

        LDX #$10
@WaitScanline:
        DEX
        BNE @WaitScanline

                        ; switch CHR bank
    LDA #$00
    TAX
    STA bankTable,X

                        ; switch nametable back $2000
    LDA ctrl_var
    STA $2000


Everything's fine for now.

What I'd like to do is to swap some colors in the palette for the status bar, and put the original colors back for the rest of the screen.

I manage to do this BUT instead of having the $2000 nametable starting "below" the status bar, it begins "under" the status bar. Am I clear ? (not sure ...)

Here's the code I used :

Code:
    LDA #$00
    STA $2006
    STA $2006

    STA $2005
    STA $2005
; PALETTE SWAP
        LDA #$3f
        STA $2006
        LDA #$01
        STA $2006
        LDA #$30
        STA $2007

        LDA #$00
        STA $2006
        STA $2006

                        ; switch CHR bank
    LDA #$03
    TAX
    STA bankTable,X
                        ; switch nametable to $2400
    LDA ctrl_var
    EOR #%00000001
    STA $2000

@WaitNotSprite0:
        LDA $2002
        AND #%01000000
        BNE @WaitNotSprite0   ; wait until sprite 0 not hit

@WaitSprite0:
        LDA $2002
        AND #%01000000
        BEQ @WaitSprite0      ; wait until sprite 0 is hit

        LDX #$10
@WaitScanline:
        DEX
        BNE @WaitScanline

    LDA #%00000001  ; rendering disabled + grayscale
    STA $2001

; PALETTE SWAP
    LDA #$3F
    STA $2006
    LDA #$01
    STA $2006
    LDA #$1A
    STA $2007

    LDA #$00
    STA $2006
    STA $2006

                        ; switch CHR bank
    LDA #$00
    TAX
    STA bankTable,X

                        ; switch nametable back $2000
    LDA ctrl_var
    STA $2000


I guess I'm doing something wrong, writing to a wrong register, or forgetting to write to a register ...
Any advice ?
Re: Status Bar + palette swap
by on (#154385)
This is my starting point :

Attachment:
File comment: Screen 1
screen_1.png
screen_1.png [ 101.99 KiB | Viewed 5159 times ]


There's an action when I press B in front of the foutain, and the text box appear. I want to change 3 colors for the text box and then write back the original colors after the sprite 0 hit.

If I add the first palette swap, I've got this :

Attachment:
File comment: Screen 2
screen_2.png
screen_2.png [ 96.29 KiB | Viewed 5159 times ]


Which is ok.

But if I write back the original colors :

Attachment:
File comment: Screen 3
screen_3.png
screen_3.png [ 92.12 KiB | Viewed 5159 times ]


The colors are ok, but the "image" is misplaced.

Any thoughts ?


Sorry for the english ...
Re: Status Bar + palette swap
by on (#154386)
After you write back the original colors, how are you setting the scroll position? You need to use the $2006-$2005-$2005-$2006 sequence.
Re: Status Bar + palette swap
by on (#154387)
Mid-screen palette swaps are particularly tricky, and you should definitely not be relying on FCEU alphabet soup alone to do your testing, because it's notoriously inaccurate when it comes to raster effects.

In order to successfully modify the palette mid-screen, you have to turn off rendering. There's a very short amount of time during Hblank when the PPU is not touching $2006 or its memory, but this time is not enough to set the PPU address and update colors, so unless you turn off rendering, there will be a conflict between you trying to write colors and the PPU trying to render tiles.

Also, when rendering is off and the VRAM address is pointing to the palette area, the color being pointed gets displayed on the screen. This means that you get "rainbow" glitches if you don't time the writes to happen during hblank.

In order to keep things glitch-free, you're gonna need some blank scanlines, because you can only really do stuff during hblank. First you wait for the hblank to turn rendering off, and then you can point to the palette you want to change. Then you wait for the next hblank and blast the new colors (pre-loaded in A, X and Y), and then reset the scroll (using the $2005/6 trick) and enable rendering on the next hblank.
Re: Status Bar + palette swap
by on (#154388)
The idea behind blasting the new colors from pre-loaded registers is that you can safely point to color 0 of any palette beforehand and as long as they were all initialized to the same value it will be like the PPU was rendering the background color. Then you can pre-load the 3 colors that will be changed in A, X and Y, and do this during hblank:

Code:
bit $2007 ;skip color 0
sta $2007 ;write color 1
stx $2007 ;write color 2
sty $2007 ;write color 3

After the last write, the VRAM address register will conveniently be pointing to color 0 of the next palette, so there will be no glitches on screen.

Then you can do the same with another palette (each palette will cost another blank scanline) or take your time with the $2005/6 trick to set the scroll and enable rendering during the next hblank.
Re: Status Bar + palette swap
by on (#154390)
tokumaru wrote:
you should definitely not be relying on FCEU alphabet soup alone to do your testing

The usual recommendation is to test this kind of stuff against nintendulator, but test it on hardware if you can, too. (If you don't have a flash cart / NES yourself, post a ROM that already works in nintendulator and ask someone here to test it for you.)
Re: Status Bar + palette swap
by on (#154392)
OK let's see if I understand everything right here.

Before updateing any color, I'd like to turn off an on rendering without glitch (screen 3)

Actually I'm doing this :

00 : load new palette
01 : switch CHR bank
02 : set BG "page" to 1
03 : write %xxxxxx01 to $2000 to switch nametable
04 : wait for not sprite 0 hit
05 : wait for sprite 0 hit
06 : wait for hblank (end of scanline?)
07 : write $00 to $2001 to turn rendering off
08 : switch CHR bank
09 : set BG "page" to 0
10 : write %xxxxxx00 to $2000 to switch nametable
11 : write %xxx11xxx to $2001 to turn rendering on

It's ok so far, everything's working.

So now I have to update palette between points 07 and 08 right ?

07.1 : write $3FC0 to $2006
07.2 : wait for hblank
07.3 : write color to $2007
07.4 : wait for hblank (???)
07.5 : write $00 to $2006-$2005-$2005-$2006

Should be ok ?

I'll test with Nintendulator and I've got an Everdrive N8 to test on real hardware.
Re: Status Bar + palette swap
by on (#154393)
glutock wrote:
09 : set BG "page" to 0
10 : write %xxxxxx00 to $2000 to switch nametable

This is already taken care of by the $2006-$2005-$2005-$2006 sequence.
Re: Status Bar + palette swap
by on (#154394)
Oh ok, thanks

Otherwise, it is correct ?
Re: Status Bar + palette swap
by on (#154395)
glutock wrote:
07.4 : wait for hblank (???)
07.5 : write $00 to $2006-$2005-$2005-$2006

You don't need to wait for hblank specifically, but it does matter where in the line you turn rendering back on. If you do it before the Y increment at dot 256, I believe you'll get a partially rendered line, and then the first full line will begin rendering at Y+1. If you do it in hblank the next line will begin rendering at Y+0. (There's some notes on this in the article I linked in the previous post.)
Re: Status Bar + palette swap
by on (#154396)
One of the previous times this came up, I marked up what the title screen for Indiana Jones and the Last Crusade was doing for its mis-screen palette changes.

Specifically on this screen, because there's no change in X fine scroll, and the positions of the gradients correspond to specific fine Y scroll values, they're able to skip the writes to $2005.
Re: Status Bar + palette swap
by on (#154399)
glutock wrote:
07.1 : write $3FC0 to $2006
07.2 : wait for hblank
07.3 : write color to $2007

If you write only one color to $2007, the VRAM address register will be pointing to a color other than 0, and this color will be rendered on the next scanline until you modify the address with the $2006/5/5/6 writes, which will look like a glitch. This is why I recommended you wrote 3 colors in succession, so that the address register would be pointing at color 0 of the next palette after all the writes.

If you want to update only 1 or 2 colors, replace the writes you're not doing with BIT or LD* instructions, to advance the VRAM address without modifying the colors. For example, to update only color 2:

Code:
bit $2007
bit $2007
sta $2007
bit $2007

This will always ensure that when the next scanline starts, the VRAM address is pointing to a color 0.
Re: Status Bar + palette swap
by on (#154425)
I'm sorry to insist but I really want to understand.

I've attached a rom and source as an example.
If you press A, it updates a color an show a status bar using sprite 0 hit.

If I uncomment lines 350 to 353 and lines 368 to 372, it's not working anymore.

Why ?
Re: Status Bar + palette swap
by on (#154442)
When you write 0 to 2006, 2005, 2005, 2006, you're resetting the full scroll to (0,0), which is why it appears to re-scroll to the top of the screen. (Is this the problem you're referring to? If so, for now, you can just use a shorter version that writes specific non-zero values to 2006 twice, since you seem to be not changing X or Y fine scroll.)

In the future, once the bottom part is scrolling independently, you'll need to keep track of the specific values that will appear in all four registers and write them instead. (see also: nesdevwiki:PPU scrolling#Split X/Y scroll)
Re: Status Bar + palette swap
by on (#154444)
lidnariq wrote:
When you write 0 to 2006, 2005, 2005, 2006, you're resetting the full scroll to (0,0), which is why it appears to re-scroll to the top of the screen. (Is this the problem you're referring to? If so, for now, you can just use a shorter version that writes specific non-zero values to 2006 twice, since you seem to be not changing X or Y fine scroll.)

Just wanted to clarify this: The reason you need the set the scroll is that changing the palette modifies the VRAM address register, so you have to reconfigure the whole address again. Ideally, just writing the new address to $2006 would work fine, but unfortunately the PPU automatically clears one bit related to the vertical scroll on the first write to $2006, so you need the $2006/5/5/6 trick to fully set all the scroll bits.

I don't see anything obviously wrong with your code, but I don't have the setup to uncomment those lines and build a new ROM for further testing. When you say "it's not working anymore", what's the expected behavior and what is actually happening? Where have you tested this behavior?
Re: Status Bar + palette swap
by on (#154469)
If you're not scrolling vertically you should just write to $2006, really. No point to doing "ticks" when those aren't even necessary in the 1st place.

Also it's up to personal opinion but in the case of a RPG, changing the colour for a text box is not always required. Many games goes well by reserving one (or even two) colour palettes to pre-defined constant values. Another approach is to not care about the colour which are used to show the text, as long as the hues are known, some games have their status bars or text boxes switching colours form level to level, and this is not always a problem.
Re: Status Bar + palette swap
by on (#154472)
It's true, you don't need the full $2006/5/5/6 combo if you're not scrolling vertically and/or the gameplay window always starts at the top half of a tile. You still have to make sure that the fine X scroll (the only part of the scroll not affected by $2006) is correct though, so you need at least one $2005 write somewhere.
Re: Status Bar + palette swap
by on (#154524)
Finally got some time to have a look at this again, and finally get it to work !!!

So thanks everyone for your help so far !

Quote:
Also it's up to personal opinion but in the case of a RPG, changing the colour for a text box is not always required. Many games goes well by reserving one (or even two) colour palettes to pre-defined constant values. Another approach is to not care about the colour which are used to show the text, as long as the hues are known, some games have their status bars or text boxes switching colours form level to level, and this is not always a problem.


Of course, and I don't know if I'll use it anyway, but since I started trying to do this , I want to understand how it works

Quote:
When you write 0 to 2006, 2005, 2005, 2006, you're resetting the full scroll to (0,0), which is why it appears to re-scroll to the top of the screen. (Is this the problem you're referring to? If so, for now, you can just use a shorter version that writes specific non-zero values to 2006 twice, since you seem to be not changing X or Y fine scroll.)


Actually it was the problem.
I used the $2005/6 trick and after tweaking and tweaking values (and understanding how it works btw), it's ok now !

The PAL version attached works well on both, emulators (FCEUX,Nintendulator,Nestopia) and hardware (tested on a flat screen and not a CRT TV, don't know if there's a difference for this, I'd say no but ? ...).

The NTSC version seems ok on emulators, but in Nintendulator there's a one pixel line jitter (but not in Nestopia nor FCEUX - I know, FCEUX is crap ;) ). If someone could test it on real hardware, it would be very nice !!

Again, thanks for your help !

If someone is interested, I can post the source code.

EDIT : NTSC version updated which seems to work ok even on PAL hardware (with a little "line-glitch" though, but totally acceptable).
Re: Status Bar + palette swap
by on (#154526)
I am too lazy to test on my Power Pak, but on emulators it appears it does not work.

Normally it should be fairly simple if you do the writes in that order :

- Poll $2002 to wait sprite zero hit
- Write $00 to $2001
- Write $3f00 (or another adress) to $2006
- Write your palette value to $2007
- Write $1e (or whathever value you're using) to $2001
- Write $0000 (or whathever adress tile you want to be shown, AND-ed with $0fff) to $2006
Re: Status Bar + palette swap
by on (#154527)
Bregalad wrote:
I am too lazy to test on my Power Pak, but on emulators it appears it does not work.

Did you press "A"?

glutock, congratulations on working hard and solving the problem. One way you can improve the jitter problem is to check the sprite 0 hit flag using BIT + BVC/BVS instead of LDA + AND + BNE/BEQ. Reducing the number of cycles in the waiting loop will reduce the gap between the minimum and maximum amount of cycles it can take to respond to the flag changing.
Re: Status Bar + palette swap
by on (#154529)
For resilience against crashes due to sprite 0 hit failure, I'd recommend a BIT/BEQ sequence:
Code:
  lda #%11000000   ; Allow either vblank or sprite 0 to break this loop
loop:
  bit $2002
  beq loop
  ; At this point, either bit 7 or bit 6 is true.
  ; If bit 6 is true, sprite 0 hit occurred.  If not,
  ; sprite 0 was missed and vblank started.
  bvc sprite_0_hit_failed
  ; At this point, you can process raster effects
  ; at the sprite 0 hit point.

If handled properly, a failure will cause flicker instead of a freeze.
Re: Status Bar + palette swap
by on (#154533)
glutock wrote:
If someone is interested, I can post the source code
I'd be interested in seeing it.
Re: Status Bar + palette swap
by on (#154544)
tepples wrote:
For resilience against crashes due to sprite 0 hit failure, I'd recommend a BIT/BEQ sequence

This is a good suggestion for general sprite 0 hit use, but when we're talking about a status bar that's handled in the NMI, only some sort of hardware glitch that corrupted sprite 0 could prevent the hit.
Re: Status Bar + palette swap
by on (#154591)
Quote:
For resilience against crashes due to sprite 0 hit failure, I'd recommend a BIT/BEQ sequence:


Thanks Tepples for the tip, I'm not using it here because I know for sure that the sprite 0 hit will happen, but it's good to know for sure !

Quote:
One way you can improve the jitter problem is to check the sprite 0 hit flag using BIT + BVC/BVS instead of LDA + AND + BNE/BEQ.


OK I've updated my code with this method, tweaked a bit the values for "scanline waiting", and add an PAL/NTSC auto-detect feature so it adjusts the values according to the mode.

I'm only changing two colors here, so it may needs to be adjusted for more.

Tested ok for PAL and NTSC on emulators and ok for PAL on hardware. (Nestopia doesn't seem to like the PAL/NTSC detection routine and gives me black screen in PAL mode)

Again, if someone would be kind enough to test it on a NTSC console it would be very nice !

Source code is in the .rar file. Not perfectly coded but hey ... it works !
Re: Status Bar + palette swap
by on (#154618)
glutock wrote:
I know for sure that the sprite 0 hit will happen


IMHO you never know for sure. In any complex program there is a potential for bugs that will break your sprite 0 hit, but more likely than that on the hardware there's all sorts of unpredictable things that can happen.

The real question in my mind is not whether it can happen (it can always happen, think about how many times you've seen temporarily corrupted CHR on an NES), but whether it's acceptable for the program to crash when it does. For something like a graphics demo, sure, that's absolutely acceptable, or if it's just on the title screen, but if you think about a game that someone's been playing for three hours and all of a sudden it hangs, you might want to do what's in your power to keep running here.
Re: Status Bar + palette swap
by on (#154625)
I've been thinking, and I don't think tepples' trick will work for a status bar handled in the NMI.

Let's think about this: say that the sprite 0 hit didn't happen because of a hardware glitch. The CPU will stay sick in the waiting loop until the next NMI fires, which will prevent the waiting loop from detecting the VBlank flag.

The new NMI will also try to detect a sprite 0 hit. If the hit happens this time, there will be some scroll problems when the previous waiting loop ends, but everything should be fine the next frame, without you having to deal with the problem directly.

If the glitch persists though, NMIs will pile up and the program will crash. The vblank flag will not do you any good.
Re: Status Bar + palette swap
by on (#154629)
I didn't really evaluate the exact details of tepples' code yet, but this is code I've been using for a "safe" sprite-0 hit:
Code:
   lda #1
   sta nmi_lock          ; prevents NMI handler from trying to do anything in failure case
   :
      bit $2002
      bvs :-             ; make sure sprite 0 is clear before polling for it (i.e. wait for end of vblank)
   :
      bit $2002
      bmi @sprite_0_fail ; if vblank is detected, we completely missed the sprite 0 hit
      bvc :-             ; sprite 0 not hit yet


For me this has worked fine. In the failure case basically I just get a rendering glitch and 50% slowdown (as vblank substitutes for the sprite 0 hit), but otherwise I haven't seen any negative consequences. Of course that bmi adds 2 more cycles of jitter to the whole operation, which you need to account for in your timing margins if you're trying to hit hblank.

edit:
I think tepples' solution is be better timing-wise, hadn't thought of masking the bit instruction. I'm not triggering this from within the NMI handler, but even in that case I don't think NMIs necessarily have to "pile up" in the failure case. It's easy to prevent that with a lockout variable like the one in my example. (Bottom line: after implementing it, test it to make sure the failure case really works.)
Re: Status Bar + palette swap
by on (#154640)
RAR has no free decompressor.

I tried the .nes file on my NTSC NES + PowerPak. I get "THIS TEXT SHOULDN T BE VISIBLE" at the top, about 16 pixels of space, then what appears to be a quarter of a head, then a horizontal strip of grass with a tree in the middle. Do I fail it?
Re: Status Bar + palette swap
by on (#154641)
Hold the A button.
Re: Status Bar + palette swap
by on (#154642)
With A, the top right corner of head becomes a red square, and the rest of the status bar becomes white except for a red and orange line across the screen behind the red square. There's some flickering in about the rightmost 4 pixels of the border. The top scanline of the tree is cut off.
Re: Status Bar + palette swap
by on (#154709)
tepples wrote:
RAR has no free decompressor.

I tried the .nes file on my NTSC NES + PowerPak. I get "THIS TEXT SHOULDN T BE VISIBLE" at the top, about 16 pixels of space, then what appears to be a quarter of a head, then a horizontal strip of grass with a tree in the middle. Do I fail it?


There is nothing wrong with his choice of using a RAR file. Most distros provide an "unrar" package. Is 7-Zip not free enough for you either?

I doubt this website is being hosted using 100% free software.
Re: Status Bar + palette swap
by on (#154711)
tepples will point out every time somebody uses a RAR file on this board, it appears to be one of his pet peeves. (I don't really know what he means either about "no free decompressor". There's plenty.)
Re: Status Bar + palette swap
by on (#154714)
True, unRAR is available without charge in source code form, but its license prohibits documenting the data format that it accepts.
Quote:
The UnRAR sources may be used in any software to handle RAR
archives without limitations free of charge, but cannot be used
to re-create the RAR compression algorithm, which is proprietary.
Distribution of modified unRAR sources in separate form or as a
part of other software is permitted, provided that it is clearly
stated in the documentation and source comments that the code may
not be used to develop a RAR (WinRAR) compatible archiver.
Re: Status Bar + palette swap
by on (#154715)
I always decompress .rar using 7zip. I do dislike .rar too, and the fact that many people stick to using a shareware program to handle that format instead of switching to 7zip. But hey, I don't complain every time I see a .rar file, I simply decompress it with 7zip.
Re: Status Bar + palette swap
by on (#154821)
Hey, I have experience with mid-frame palette updates: viewtopic.php?f=2&t=12830

If you use Blargg's NMI sync, you can achieve more accurate results stuffing the updates into the Hblank period. What you may not realize is that each time you reset/power the system, the PPU has a different sync with the CPU. So when you're testing, make sure to reset the system multiple times to make sure you're not missing potential possibilities.

There are different ways to use the NMI sync, more and less accurate. You wouldn't need a perfect sync for this project, just a good sync, which you would run at the start of Vblank.