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

Define a Pallete Only For The Background

Define a Pallete Only For The Background
by on (#58742)
Hello,
I'm using a background, but I want to use a different pallete for it, because at the time I only have one pallete assigned, but on the source folder I have another one called bkg.pal, that is a pallete for the background. But my code is like this now:
Code:
   .inesprg 1   ; 1 bank of code
   .ineschr 1   ; 1 bank of spr/bkg data
   .inesmir 1   ; something always 1
   .inesmap 0   ; we use mapper 0

   .bank 0   ; bank 0 - our place for code.
   .org $0000
addrLO = $10
addrHI = $11
Sprite1_X   .db 50       ; a X position for our sprite, start at 20
Sprite1_Y   .db 20       ; a Y position for our sprite, start at 20
;Sprite2_X   .db 50       ; a X position for our sprite, start at 20
;Sprite2_Y   .db 20       ; a Y position for our sprite, start at 20
   .org $8000  ; code starts at $8000
   
Start: 
   lda #%10001000  ; do the setup of PPU
   sta $2000       ; that we

   ldx #$00    ; clear X

   lda #$3F    ; have $2006 tell
   sta $2006   ; $2007 to start
   lda #$00    ; at $3F00 (pallete).
   sta $2006

loadpal:                ; this is a freaky loop
   lda tilepal, x  ; that gives 32 numbers
   sta $2007       ; to $2007, ending when
   inx             ; X is 32, meaning we
   cpx #32         ; are done.
   bne loadpal     ; if X isn't =32, goto "loadpal:" line.

   lda #$20
   sta $2006 ; give $2006 both parts of address $2020.
   sta $2006
   ldx #$00

Map_Load:
     LDA $2002             ; read PPU status to reset the high/low latch
     LDA #$20
     STA $2006             ; write the high byte of $2000 address
     LDA #$00
     STA $2006             ; write the low byte of $2000 address
     lda #low(ourMap)
     sta addrLO
     lda #high(ourMap)              ; start out at 0
     sta addrHI

     ldx #4
     ldy #0

Map_Load_loop:
     LDA [addrLO], y     ; load data from address (background + the value in x)
     STA $2007             ; write to PPU
     INy                   ; X = X + 1
     BNE Map_Load_loop  ; Branch to LoadBackgroundLoop if compare was Not Equal to zero
     inc addrHI
     dex
     bne Map_Load_loop

waitblank1:         
   lda $2002  ; these 3 lines wait for VBlank, this loop will actually miss VBlank
   bpl waitblank1 ; alot, in a later Day, I'll give a better way
   txa
clrmem:
   sta $000,x
   sta $100,x
   sta $200,x
   sta $300,x
   sta $400,x
   sta $500,x
   sta $600,x
   sta $700,x  ; Remove this if you're storing reset-persistent data
   inx
   bne clrmem
   
waitblank2:         
   lda $2002  ; these 3 lines wait for VBlank, this loop will actually miss VBlank
   bpl waitblank2 ; alot, in a later Day, I'll give a better way
   lda #%00011110
   sta $2001
   
loop:
   jmp loop
   
infinite:  ; a label to start our infinite loop
nmi:

   ; Load Sprite1
   lda Sprite1_Y
   sta $0300
   lda #$11
   sta $0301
   lda #$00
   sta $0302
   lda Sprite1_X
   sta $0303
   
   ; Load Sprite2
   ;lda Sprite2_Y
   ;sta $0304
   ;lda #$12
   ;sta $0305
   ;lda #$00
   ;sta $0306
   ;lda Sprite2_X
   ;sta $0307
   
   lda #$01   ; these
   sta $4016  ; lines
   lda #$00   ; setup/strobe the
   sta $4016  ; keypad.

   lda $4016  ; load Abutton Status ; note that whatever we ain't interested
   lda $4016  ; load Bbutton Status ; in we just load so it'll go to the next one.
   lda $4016  ; load Select button status
   lda $4016  ; load Start button status
   lda $4016  ; load UP button status
   and #1     ; AND status with #1
   bne UPKEYdown  ; for some reason (not gonna reveal yet), need to use NotEquals
   ;with ANDs. So it'll jump (branch) if key was down.
   
   lda $4016  ; load DOWN button status
   and #1     ; AND status with #1
   bne DOWNKEYdown

   lda $4016  ; load LEFT button status
   and #1     ; AND status with #1
   bne LEFTKEYdown

   lda $4016  ; load RIGHT button status
   and #1     ; AND status with #1
   bne RIGHTKEYdown
   jmp NOTHINGdown  ; if nothing was down, we just jump (no check for conditions)
   ; down past the rest of everything.

UPKEYdown:
   lda Sprite1_Y ; load A with Y position
   sbc #1  ; subtract 1 from A. Only can do math on A register. SBC (Subtract with Borrow)
   sta Sprite1_Y ; store back to memory
   ;lda Sprite2_Y ; load A with Y position
   ;sbc #1  ; subtract 1 from A. Only can do math on A register. SBC (Subtract with Borrow)
   ;sta Sprite2_Y ; store back to memory
   jmp NOTHINGdown  ; jump over the rest of the handling code.

DOWNKEYdown:
   lda Sprite1_Y
   adc #1  ; add 1 to A. ADC (Add with Carry)((to A register))
   sta Sprite1_Y
   jmp NOTHINGdown ; jump over the rest of handling code.

LEFTKEYdown:
   lda Sprite1_X
   sbc #1 
   sta Sprite1_X
   jmp NOTHINGdown
;the left and right handling code does the same as UP and Down except.. well.. with
; left and right. :)

RIGHTKEYdown:
   lda Sprite1_X
   adc #1
   sta Sprite1_X
   ; don't need to jump to NOTHINGdown, it's right below. Saved several bytes of
   ; PRG-Bank space! :)
   
NOTHINGdown:
   lda #$03
   sta $4014
   rti

tilepal:
   .incbin "our.pal" ; include and label our pallete
   
ourMap:
   .incbin "our.map" ; assuming our.map is the binary map file.
   .bank 1   ; following goes in bank 1
   .org $FFFA  ; start at $FFFA
   .dw nmi    ; dw stands for Define Word and we give 0 as address for NMI routine
   .dw Start ; give address of start of our code for execution on reset of NES.
   .dw 0   ; give 0 for address of VBlank interrupt handler, we tell PPU not to
   ; make an interrupt for VBlank.
   
   .bank 2   ; switch to bank 2
   .org $0000  ; start at $0000
   .incbin "our.bkg"  ; background data
   .incbin "our.spr"  ; our sprite pic data

Then I want to know how can I assign a pallete only for the background.

Best Regards,
Nathan Paulino Campos

by on (#58743)
To assign a palette for the background, write to PPU $3F01-$3F0F.

by on (#58745)
Your code is based on GBAGuy's tutorial isn't it? It's full of errors. Before talking about the palette I'm gonna talk about those errors, or else your program is never going to run on a real NES.

First, there are several initializations steps necessary for your program to be stable that your code just doesn't do. Take a look at the init code from the wiki. All your programs should start with code like that, because all those steps have a reason to be there. I see that you have part of it in your code, but it's erroneously placed after several PPU operations. The initialization must be before everything. clearing the memory is optional though, and not necessary if you are careful with each variable you use (never use memory without initializing it first).

Only after that initial code, after waiting 2 VBlanks, the PPU is ready for being used. See, the PPU needs some time to warm up, this is why we must wait a while before using it. But your program starts using it right away, so it would certainly fail on a real NES.

OK, the palette then: You are currently loading 32 bytes of palette data, and that corresponds to 8 whole palettes (4 for the background and 4 for the foreground). Instead of using that loop that copies 32 bytes to $3F00 you can code a loop to copy any number of bytes you want to any address you want.

If you want to update only the background palettes (the 4 of them), copy 16 bytes to $3F00. Want to update only the sprite palettes? Then copy 16 bytes to $3F10. It all depends of which palettes you want to update, you are not forced to update them all at once.

by on (#58754)
Yeah, I was using many parts of the GBAGuy's tutorial, but before I start correcting the Init code things, let's do the pallete thing. I've added a new label called bkgpal after the tilepal label, like this:
Code:
tilepal:
   .incbin "our.pal" ; include and label our sprite pallete
   
bkgpal:
   .incbin "bkg.pal" ; include and label our background pallete

And also, changed some things at the lines 22 to 49, now they are like this:
Code:
   lda #$3F    ; have $2006 tell
   sta $2006   ; $2007 to start
   lda #$10    ; at $3F00 (pallete).
   sta $2008

loadsprpal:                ; this is a freaky loop
   lda tilepal, x  ; that gives 16 numbers
   sta $2007       ; to $2007, ending when
   inx             ; X is 16, meaning we
   cpx #16         ; are done.
   bne loadsprpal     ; if X isn't =16, goto "loadpal:" line.

   lda #$20
   sta $2006 ; give $2006 both parts of address $2020.
   sta $2006
   ldx #$00
   
loadbkgpal:                ; this is a freaky loop
   lda bkgpal, x     ; that gives 16 numbers
   sta $2009       ; to $2007, ending when
   inx             ; X is 16, meaning we
   cpx #16         ; are done.
   bne loadbkgpal  ; if X isn't =16, goto "loadpal:" line.

   lda #$20
   sta $2008 ; give $2006 both parts of address $2020.
   sta $2008
   ldx #$00

But when I've started to emulate, I just displays a entire black screen with nothing.

What's wrong now?
PS: At least now I've tried to do it myself, without asking for code. :)

Best Regards,
Nathan Paulino Campos

by on (#58759)
You're pretty much just guessing, right...? OK, I'll try to help.

The palette is just a group of 32 bytes at address $3F00-$3F1F of VRAM. In order to modify the palette you have to write the colors you want to use to that location.

The code you copied from GBAGuy copies all 32 bytes from the "our.pal" file to that location. The "our.pal" file is nothing more than a list of colors. If you want the quickest and cleanest solution, just edit the "our.pal" file and change the first 16 values to those you want. That's all you have to do.

Now let me point out some things in your code that don't make sense. You had the VRAM address set to $2020 before trying to write your palette. That address is for a name table, you can't write your palette there. The palettes stay at $3F00.

Then you try to write the data to $2009. Where did you get the $2009 from? VRAM should always be written to through the register mapped at $2007.

The code you used should work if you fix it to set the VRAM address to $3F00 instead of $2020 and use register $2007 to write the data instead of $2009. But you should understand that the original 32 colors are still being used, you'll just overwrite the first 16 by doing it like that. The clean solution is to just modify the .pal file, like I said above.

Also, you don't have to use external files for palettes. You can very well write the colors you want directly in the source file, like this (which I find easier to work with than an external binary file):

Code:
bkgpal:
   .db $0f, $11, $21, $31 ;palette 0
   .db $0f, $14, $24, $34 ;palette 1
   .db $0f, $18, $28, $38 ;palette 2
   .db $0f, $19, $29, $39 ;palette 3

by on (#58777)
Thanks very much! Now all is working. :)

Also, now I'm going to take a look on the Init code as you've suggested for me. ;)

by on (#58799)
also note that $2008,$2009 are not registers (they mirror $2000,$2001). So I'm not sure why you're writing to them.

by on (#58811)
Disch wrote:
also note that $2008,$2009 are not registers (they mirror $2000,$2001). So I'm not sure why you're writing to them.


- Uh, yeah. Weird. o.O

by on (#58814)
$2008 and $2009 are the registers used in the GBAguy tutorial. It left me confused as well.

by on (#58817)
One more reason to get rid of his tutorials.

by on (#58872)
Oh! :o
Wait, now I've tried to change the code as you said, but I still got the same black and empty screen. My code now:
Code:
   lda #$3F    ; have $2006 tell
   sta $2006   ; $2007 to start
   lda #$10    ; at $3F00 (pallete).
   sta $2008

loadsprpal:                ; this is a freaky loop
   lda tilepal, x  ; that gives 16 numbers
   sta $2007       ; to $2007, ending when
   inx             ; X is 16, meaning we
   cpx #16         ; are done.
   bne loadsprpal     ; if X isn't =16, goto "loadpal:" line.

   lda #$20
   sta $2006 ; give $2006 both parts of address $2020.
   sta $2006
   ldx #$00
   
loadbkgpal:                ; this is a freaky loop
   lda bkgpal, x     ; that gives 16 numbers
   sta $2007       ; to $2007, ending when
   inx             ; X is 16, meaning we
   cpx #16         ; are done.
   bne loadbkgpal  ; if X isn't =16, goto "loadpal:" line.

   lda #$3F
   sta $2006 ; give $2006 both parts of address $3F00.
   lda #$00
   sta $2006
   ldx #$00


This is making me crazy! :?

by on (#58874)
You are doing things in the wrong order.

FIRST you have to set the destination address for the palette (through register $2006), THEN you write the data to register $2007. There is no reason for you to use the address $2020 (that address points to a name table, it has nothing to do with palettes) there in the middle, and your use of X is all messed up. You forgot to initialize it before the first loop, and you needlessly initialize it at the end.

I don't like to give out code (because people tend to just copy&paste it rather than study it), but this is the correction to the code you just posted:

Code:
   ;set the destination address for sprite palettes ($3F10)
   lda #$3F
   sta $2006
   lda #$10
   sta $2006

   ;copy 16 colors
   ldx #$00
LoadSpritePalette:
   lda SpritePalette, x
   sta $2007
   inx
   cpx #16
   bne LoadSpritePalette

   ;set the destination address for background palettes ($3F00)
   lda #$3F
   sta $2006
   lda #$00
   sta $2006

   ;copy 16 colors
   ldx #$00
LoadBackgroundPalette:
   lda BackgroundPalette, x
   sta $2007
   inx
   cpx #16
   bne LoadBackgroundPalette

This will write the palettes to the correct locations. However, this is just a waste of space, you don't need 2 loops for that, you could very well just store the 2 palettes together (one after the other) and copy all 32 bytes using a single loop, like the original code did.

Nathan, let me give you some advice: You are clearly just guessing things, instead of trying to understand them. Guessing things until the program works is a very bad way of learning things, because in the end you don't know what you did right, it was pure luck. At this point, you should already have understood what $2006 and $2007 do, and you should already know where the palette and the name tables are in VRAM, so you shouldn't be making these silly mistakes of setting the wrong addresses.

If you don't know that stuff, then you should go back and read a bit more about the architecture of the NES before trying to code again. Knowing the memory map well and knowing how to write data to VRAM are things that every newbie has to understand before writing code, otherwise we'll keep being part of this frustrating cycle where you have no idea of what's happening and need others to fix the most basic things in your code.

If english is a barrier for you, you can always PM me or use the international forum and I'll try to explain stuff in portuguese, if you think that will help. But you have to do your part and study these things, because I will not have time to teach you about every little thing. I can help with stuff you don't get, but for this to work you must get some of it on your own.

by on (#58875)
Code:
   
   ldx #$00
   lda $2002  ; clears the flip flop, you probably already did it before, no?
   lda #$3F
   sta $2006
   lda #$10   ; sprite color RAM start
   sta $2006

loadsprpal:                ; this is a freaky loop
   lda tilepal, x  ; that gives 16 numbers
   sta $2007       ; to $2007, ending when
   inx             ; X is 16, meaning we
   cpx #16         ; are done.
   bne loadsprpal     ; if X isn't =16, goto "loadpal:" line.

   lda #$3F
   sta $2006
   lda #$00
   sta $2006
   
loadbkgpal:                ; this is a freaky loop
   lda bkgpal, x     ; that gives 16 numbers
   sta $2007       ; to $2007, ending when
   inx             ; X is 16, meaning we
   cpx #16         ; are done.
   bne loadbkgpal  ; if X isn't =16, goto "loadpal:" line.


EDIT: Meh, Tokumaru was faster. :)

by on (#58876)
Zepper, you forgot to initialize X for the second loop.

by on (#58877)
:lol: Dois brazucas vendo quem vai ser o primeiro a ajudar. :P
tokumaru Wins! :P

Thanks very much guys, now I promise that I'm going to study the code. ;)

by on (#58878)
Zepper wrote:
Meh, Tokumaru was faster. :)

Yeah, but the truth is that there isn't a single way to do this. Your code is slightly different from mine, but it's just as good. You also included the resetting of the $2006/$2005 latch, which I didn't because I'm very organized with my PPU operations and always use $2006/$2005 in pairs.

There are a thousand different ways to set the palettes, and if he keeps using the trial and error method of finding something that works he'll not find which of those thousand ways is best for him. He'll only find the best way if he understands what is going on in the code.

I remember that in high school and in college a lot of teachers just gave the students blocks of code without actually explaining why they did what they were supposed to do. This is a very poor way to teach programming, and the tests were basically cut&paste-fests, and if anything slightly different from what was seen in class was asked, the students weren't able to change a freaking variable in the code in order to adapt it to a new situation.

Some people on the internet end up doing the exact same thing. They just want a block of code to achieve a task, rather than deeply understand each step of that block of code. I just don't want Nathan to follow that route, or he won't get very far.

by on (#58891)
nathanpc wrote:
:lol: Dois brazucas

Just wait; a brazilian more will pop in at any moment ;-)

tokumaru wrote:
Some people on the internet end up doing the exact same thing. They just want a block of code to achieve a task, rather than deeply understand each step of that block of code. I just don't want Nathan to follow that route, or he won't get very far.

Sometimes what they want is called a "library". For example, if you want to parse JSON, you don't want to reinvent the wheel and understand the parser; you just want to put a JSON string in and get a dict out.