[4.5.9] Change Palette Macros

JamesNES

Well-known member
This is something I wanted a while back but didn't know how to do. It's for if you want to say, change the palette of a monster block after it's disappeared to match the grass or something like that.

I don't have any experience at all with NES Maker's scrolling modules so I'm not sure how this'd work on them, but for single screen games it's all good.

I've made two versions, one which change all four palettes in an attribute grid, and the second one will change just one of the four. They take an X and Y argument, and the palette you want to switch it to (ie 0 to 3).

You might want to check this out first for an idea on how attributes work:

NESdev PPU attribute tables

So the first one works like this, and can be run from any bank:

Code:
ChangeFourPalettes #$08, #$06, #%01010000

So this changes the top two to be palette 0 and the bottom two to be palette 1.

changefour.gif

Code:
MACRO ChangeFourPalettes arg0, arg1, arg2

;;arg0 = x
;;arg1 = y
;;arg2 = new palettes for this quadrant
;;            7654 3210
;            |||| ||++- Color bits 3-2 for top left quadrant of this byte
;            |||| ++--- Color bits 3-2 for top right quadrant of this byte
;            ||++------ Color bits 3-2 for bottom left quadrant of this byte
;            ++-------- Color bits 3-2 for bottom right quadrant of this byte
    LDA camScreen
    AND #%00000001
    BEQ +leftNametable
        LDA #$27
        STA temp
        JMP +gotNT
    +leftNametable
        LDA #$23
        STA temp
   
    +gotNT
    LDA arg1
    LSR
    ASL
    ASL
    ASL
    STA tempB

    

    LDA arg0
    lsr

    CLC
    ADC tempB
    CLC
    ADC #$C0
    STA tempC
   
    LDY maxScrollOffsetCounter
    LDA temp
    STA scrollUpdateRam,y
    INY
    LDA tempC
    STA scrollUpdateRam,y
    INY
    LDA arg2
    STA scrollUpdateRam,y
    INY
    STY maxScrollOffsetCounter
            LDA updateScreenData
        ORA #%0000100
        STA updateScreenData   
   
ENDM

The second one has to be run from the static bank as it needs to look up from bank 16 which palettes the other tiles have.

Code:
ChangeSinglePalette #$08, #$06, #$01

This changes tile 8,6 to use palette 1.

changeone.gif

Code:
MACRO ChangeSinglePalette arg0, arg1, arg2
;arg0 = x
;arg1 = y
;arg2 = palette to swap to, 0-3

LDA camScreen
AND #%00000001
BEQ +leftNametable
    LDA #$27
    STA temp
    JMP +gotNT
+leftNametable
    LDA #$23
    STA temp

+gotNT

LDA arg1
LSR
ASL
ASL
ASL
STA tempB

 

LDA arg0
lsr

CLC
ADC tempB
sta tempA
CLC
ADC #$C0
STA tempC

;;now to get the original attribute value for this
    SwitchBank #$16
        LDA camScreen
        LSR
        LSR
        LSR
        LSR
        LSR   
       
        sta temp1
        LDA camScreen
        tay
        LDA warpMap
        AND #%00000001
        BEQ +loadAttFromMap1table
            ;;;load from map 2 table
            LDA AttributeTables_Map2_Lo,y
            STA temp16
            LDA AttributeTables_Map2_Hi,y
            STA temp16+1
                LDA arg0_hold
                CLC
                ADC #$08
                STA arg0_hold
            JMP +GotAttLoadPointer
        +loadAttFromMap1table:
            LDA AttributeTables_Map1_Lo,y
            STA temp16
            LDA AttributeTables_Map1_Hi,y
            STA temp16+1
        +GotAttLoadPointer:
            ;;now (temp16) holds the address of the nametable to be loaded.
    ReturnBank

    SwitchBank temp1
    LDA tempA
   
    tay
    LDA (temp16),y ;;got the attribute value
   
   
    STA tempD
   
    ;;now which quadrant is it
    LDA arg0
    AND #%00000001
    BNE +rightHand
        ;;left side
        ;;top or bottom
        LDA arg1
        AND #%00000001
        BNE +bottom
            ;;top left
            LDA tempD
            AND #%11111100
            ORA arg2
            STA tempD
            JMP +done
        +bottom
            ;bottom left
            LDA arg2
            ASL
            ASL
            ASL
            ASL
            ASL
            ASL
            STA arg2_hold
            LDA tempD
            AND #%11001111
            ORA arg2_hold
            STA tempD
            JMP +done
   
    +rightHand
        LDA arg1
        AND #%00000001
        BNE +bottom
            ;;top right
            LDA arg2
            ASL
            ASL
            STA arg2_hold
            LDA tempD
            AND #%11110011
            ORA arg1_hold
            STA tempD
            JMP +done
        +bottom
            ;bottom right
            LDA arg2
            ROR
            ROR
            ROR
            STA arg2_hold
            LDA tempD
            AND #%00111111
            ORA arg2_hold
            STA tempD
   
    +done
    LDY maxScrollOffsetCounter
    LDA temp
    STA scrollUpdateRam,y
    INY
    LDA tempC
    STA scrollUpdateRam,y
    INY
    LDA tempD
    STA scrollUpdateRam,y
    INY
    STY maxScrollOffsetCounter
            LDA updateScreenData
        ORA #%0000100
        STA updateScreenData

ENDM

There's bound to be a less wordy way for this one, and it'll be better as a subroutine, but it works. Hope this helps someone!
 
Just saw this, does this work with tile pallettes for my obvious reasons?
Feel stupid for posting this about a month ago... of course it's tile palletes! Thank you!
 
Last edited:

dale_coop

Moderator
Staff member
I noticed some mistakes in the ChangeSinglePalette macro (about the usage of arg2_hold), here's a modified one that should work a better (I hope):
Code:
MACRO ChangeSinglePalette arg0, arg1, arg2
;arg0 = x
;arg1 = y
;arg2 = palette to swap to, 0-3

TYA
PHA

LDA camScreen
AND #%00000001
BEQ +leftNametable
    LDA #$27
    STA temp
    JMP +gotNT
+leftNametable
    LDA #$23
    STA temp

+gotNT

LDA arg1
LSR
ASL
ASL
ASL
STA tempB

 

LDA arg0
lsr

CLC
ADC tempB
sta tempA
CLC
ADC #$C0
STA tempC

;;now to get the original attribute value for this
    SwitchBank #$16
        LDA camScreen
        LSR
        LSR
        LSR
        LSR
        LSR  
     
        sta temp1
        LDA camScreen
        tay
        LDA warpMap
        AND #%00000001
        BEQ +loadAttFromMap1table
            ;;;load from map 2 table
            LDA AttributeTables_Map2_Lo,y
            STA temp16
            LDA AttributeTables_Map2_Hi,y
            STA temp16+1
                LDA arg0_hold
                CLC
                ADC #$08
                STA arg0_hold
            JMP +GotAttLoadPointer
        +loadAttFromMap1table:
            LDA AttributeTables_Map1_Lo,y
            STA temp16
            LDA AttributeTables_Map1_Hi,y
            STA temp16+1
        +GotAttLoadPointer:
            ;;now (temp16) holds the address of the nametable to be loaded.
    ReturnBank

    SwitchBank temp1
    LDA tempA
 
    tay
    LDA (temp16),y ;;got the attribute value
 
 
    STA tempD
 
    ;;now which quadrant is it
    LDA arg0
    AND #%00000001
    BNE +rightHand
        ;;left side
        ;;top or bottom
        LDA arg1
        AND #%00000001
        BNE +bottom
            ;;top left
            LDA tempD
            AND #%11111100
            ORA arg2
            STA tempD
            JMP +done
        +bottom
            ;bottom left
            LDA arg2
            ASL
            ASL
            ASL
            ASL
            STA arg2_hold
            LDA tempD
            AND #%11001111
            ORA arg2_hold
            STA tempD
            JMP +done
 
    +rightHand
        LDA arg1
        AND #%00000001
        BNE +bottom
            ;;top right
            LDA arg2
            ASL
            ASL
            STA arg2_hold
            LDA tempD
            AND #%11110011
            ORA arg2_hold
            STA tempD
            JMP +done
        +bottom
            ;bottom right
            LDA arg2
            ASL
            ASL
            ASL
            ASL
            ASL
            ASL
            STA arg2_hold
            LDA tempD
            AND #%00111111
            ORA arg2_hold
            STA tempD
    +done
   
    LDY maxScrollOffsetCounter
    LDA temp
    STA scrollUpdateRam,y
    INY
    LDA tempC
    STA scrollUpdateRam,y
    INY
    LDA tempD
    STA scrollUpdateRam,y
    INY
    STY maxScrollOffsetCounter
   
        LDA updateScreenData
        ORA #%0000100
        STA updateScreenData

PLA
TAY

ENDM
 
Last edited:

JamesNES

Well-known member
Thanks Dale! I thought something was up with it when I used it a couple of weeks ago, but no one said anything so I hadn't got to it yet. Will check it out!
 

baardbi

Well-known member
Amazing macro. Thank you!

It might be a good idea to put this at the top of the macro:
TYA
STA tempy

And this at the bottom:
LDY tempy

I used this macro in a loop and it didn't work until I realized that the macro uses the Y register.
 

abenjack

New member
Hi guys, I need to change the color palette of some background tile when I hit them with a projectile, is it the correct way to do it? I'm a bit lost ^_^, quite newbie with nesmaker... Thanks in advance
 

DocNES

Member
@abenjack I'm still new to this but I wonder if the changeTile would work. I recently played around with this tutorial. It did change background tiles with non Player collisions. ie Projectiles too. You can change the background tile to a different one, and I believe different color pallet. Let me know how it turns out.

LINK
 

abenjack

New member
Hi DocNES, thanks for your reply. I'm able to change the tile with the macro ChangeTileAtCollision (collision with projectile, for example) but the color palette is the same. My desire is to have a background object with greyscale palette and when a projectile hit the object it will be colorfull. I tried the macro suggested by @JamesNES and @dale_coop but maybe I goes wrong with something because it doesn't work for me. Maybe because I'm working with a single screen game, but I'm not sure. Thanks a lot for your suggestion, I'll get an eye on your link :)
 
Last edited:
I think I am on the same page with this one.
I have a "torch" tile that used to make flame sprites but was slowing my game down.
I was just experimenting with changing to an animated tile instead.
So far so good except I need a way to change the what palette the tile is drawing from.
Which I think is the same thing abenjack is trying to do, "ChangePaletteAtCollision".
Not necessarily load a new palette to the room but change what palette the tile is using.
Can anyone get me pointed in the right direction?
 
Top Bottom