It is currently Fri Oct 19, 2018 4:38 pm

All times are UTC - 7 hours



Forum rules





Post new topic Reply to topic  [ 23 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: Palette Checker
PostPosted: Thu Mar 26, 2015 5:24 pm 
Offline
Formerly Espozo
User avatar

Joined: Mon Sep 15, 2014 4:35 pm
Posts: 3377
Location: Richmond, Virginia
Well, I've been trying to make a code that looks for an object's palette bits based on what each palette is. I've made a 64 byte "palette table" in ram where the first 32 bytes contain individual numbers that correspond to different palettes and where the second 32 bytes say how many objects are currently using the palette. In the code, it first looks at the first 32 bytes to see if any of the existing palettes match the one you are trying to add, and if none of them do, you go to the last 32 bytes that if 0, you can create a new palette in the area in the first 32 bytes that corresponds to the last 32 bytes and also add 1 to the spot you found the empty palette slot in in the last 32 bytes to say that the slot is being occupied. I think I commented pretty good, but If you're unsure about something or just don't get it, you can ask me to better explain. There are a few thing's I'm not sure about that I commented on, and if you have a really good idea for a better way to check for palettes, you can also say that.

This code is for ca65, but I'm sure you can understand it if you don't have it.
Code:
.proc start_palette_checker
  rep #$30    ; A=16, X/Y=16
  ldx #$0000

palette_checker_loop1:
  lda PaletteTable,x         ;first 32 bytes specify what palette is in the palette slot
  cmp PaletteRequest         ;sees if the color in the palette slot matches with the one you are trying to add
  bne prepare_for_palette_checker_loop

done:
  lda PaletteTable+32,x      ;the last 32 bytes say how many objects are using that palette
  inc            ;so if it is 0, it is safe to overwrite it. a new object is using that palette,
  sta PaletteTable+32,x      ;so increase it by 1.

  sep #$20

  ldx
  ora Attributes+1   ;this will be direct paged for object slots, I just forgot how to do it without having to do a:.
  sta Attributes+1   ;Attributes is the exact same thing as bytes 3 and 4 in oam.
  rts

prepare_for_palette_checker_loop:
  inx               ;we're using 16 bits, so you increase x by 2.
  inx
  cpx #$0020            ;sees if we're at the second half of the palette table.
  bcc palette_checker_loop1

palette_checker_loop2:
  lda PaletteTable,x
  bne prepare_for_palette_checker_loop   ;0 means that no objects are using the palette slot,
               ;so it is safe to overwrite it.

done2:
  ldx
  ror         ;same thing as dividing by 2? How is asl any different than rol then?
  ora Attributes+1   ;this will be direct paged for object slots.
  sta Attributes+1   ;Attributes is the exact same thing as bytes 3 and 4 in oam.

  lda PaletteRequest
  sta PaletteTable-32,x      ;remember, x is twice as much here as it would be in done1 (64/2 = 32)
  lda PaletteTable,x      ;the last 32 bytes say how many objects are using that palette
  inc            ;so if it is 0, it is safe to overwrite it. a new object is using that palette,
  sta PaletteTable,x      ;so increase it by 1.

  sep #$20   ;how do you get an 8 bit x, because I'm pretty sure that's what you need here
  ldx      ;(by the way, this next section is going to be pretty rough, because I've never directly worked with DMA...)
  asl      ;since a palette is 32 bytes and we already have the palette number times 4,
  asl      ;we multiply it by 8 for the result.
  asl
  asl
  stx $2121   ;says which color to start at

  lda PaletteRequest
  sta $4302   ; Store data offset into DMA source offset (PaletteRequest is really an adress,
  lda #$01   ; and because I'm only using 1 bank for palettes, every number can be its own adress)
  sta $4304   ; Store data bank into DMA source bank (bank 1)
  lda #$10
  sta $4305   ; Store size of data block (32 bytes = 1 16 color palette)

  stz $4300   ; Set DMA Mode (byte, normal increment)
  lda #$22   ; Set destination register ($2122 - CGRAM Write)
  sta $4301
  lda #$01   ; Initiate DMA transfer
  sta $420B
  rts

.endproc


Top
 Profile  
 
 Post subject: Re: Palette Checker
PostPosted: Fri Mar 27, 2015 10:27 am 
Offline
Formerly Espozo
User avatar

Joined: Mon Sep 15, 2014 4:35 pm
Posts: 3377
Location: Richmond, Virginia
You know, I just realized something. If I have about 20 bullets that are the exact same (including color) onscreen, then you just lost a good deal of time doing the exact same thing over again. Do you think you could have it to where a table of registers hold a value for how many objects are using that palette, and where the palette is in the palette table? You would have something like 2 registers (4 in 16 bit) for an explosion color, where the first says how many objects are using the palette, and another that has where the palette is in the palette table. If the first register is 0 and you are adding a new object that uses that palette, you look at the palette table for a an open spot. the 2nd register in the explosion palette registers would be set exactly like the palette bits in oam, (These: %00001110) so you only need to convert the number once.

Does this make any sense? How does everyone else switch palettes?

(I'll try to make the code soon.)


Top
 Profile  
 
 Post subject: Re: Palette Checker
PostPosted: Fri Mar 27, 2015 11:16 am 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 6893
Location: Canada
Well, this is treating it like a very generic problem, but every game has a different use case for palettes. Maybe a specific solution would work better?

You could write special code for drawing bullets that fetches the palette from your generic system once, then reuses that for all bullets.

You could also just reserve specific palettes for specific objects (at specific times).

There's a million ways to solve the problem. What works best depends entirely on the situation.


Top
 Profile  
 
 Post subject: Re: Palette Checker
PostPosted: Fri Mar 27, 2015 11:21 am 
Offline

Joined: Wed May 19, 2010 6:12 pm
Posts: 2760
This is going to be a last resort right? Doing dynamic animation on individual sprites, and organizing them into screen/vram regions, while making sure the metasprite animates as a whole while keeping track of dma usage, and having the ability to sync up explosion sprites, except when it can't due to screen/vram regions is complicated enough without adding color palettes to the mix.


Top
 Profile  
 
 Post subject: Re: Palette Checker
PostPosted: Fri Mar 27, 2015 1:57 pm 
Offline
User avatar

Joined: Thu Dec 25, 2014 10:26 pm
Posts: 311
Location: Canada
I would personally think most games would favor a mixed approach - reserve a few palettes for objects that will be on screen always or frequently (like bullets), and then swap out the rest as needed. That or plan ahead through smart level design to just not overflow your palette space by segregating certain types of enemies, so you can just load the whole block of palettes and update it as needed when you approach different sets of enemies

My gut feeling is this approach may be a bit more processing than it's worth at the SNES level.


Top
 Profile  
 
 Post subject: Re: Palette Checker
PostPosted: Fri Mar 27, 2015 2:39 pm 
Offline
Formerly Espozo
User avatar

Joined: Mon Sep 15, 2014 4:35 pm
Posts: 3377
Location: Richmond, Virginia
psycopathicteen wrote:
This is going to be a last resort right?

It wasn't going to be. :oops:

psycopathicteen wrote:
and organizing them into screen/vram regions

I'm actually not going to do that, but I thought it would have been a cool idea, but yeah, maybe I'm being a bit over ambitious. I haven't run into slowdown yet, but I really haven't done much of anything. I wonder how much I should be worrying... (I'm most definitely going to use fast rom, but I don't know how much it would help.)

Khaz wrote:
I would personally think most games would favor a mixed approach - reserve a few palettes for objects that will be on screen always or frequently

Yeah, that's true. I think I'll reserve palettes for player 1 and 2, so at least that's a little less to worry about.

Does anyone know how DKC handles changing palettes? It seems pretty accurate and it definitely isn't hardcoded to switch palettes halfway through the level or anything. One weird thing about how DKC handles palettes is that if there are too many objects with unique palettes on screen, the game will sometimes mess up the hud and banana palette for an enemy, and it will stay messed up even when all the objects disappear. I would have thought they would make it to where it checks if all the palettes are used up and just doesn't write anything then, (meaning it doesn't write over palette 1) but I guess not.


Top
 Profile  
 
 Post subject: Re: Palette Checker
PostPosted: Fri Mar 27, 2015 4:49 pm 
Offline
User avatar

Joined: Thu Dec 25, 2014 10:26 pm
Posts: 311
Location: Canada
Espozo wrote:
Does anyone know how DKC handles changing palettes? It seems pretty accurate and it definitely isn't hardcoded to switch palettes halfway through the level or anything. One weird thing about how DKC handles palettes is that if there are too many objects with unique palettes on screen, the game will sometimes mess up the hud and banana palette for an enemy, and it will stay messed up even when all the objects disappear. I would have thought they would make it to where it checks if all the palettes are used up and just doesn't write anything then, (meaning it doesn't write over palette 1) but I guess not.

I too would be thrilled to hear more about how official games handled this particular issue.


Top
 Profile  
 
 Post subject: Re: Palette Checker
PostPosted: Sat Mar 28, 2015 11:49 am 
Offline

Joined: Wed May 19, 2010 6:12 pm
Posts: 2760
Espozo wrote:
psycopathicteen wrote:
and organizing them into screen/vram regions

I'm actually not going to do that, but I thought it would have been a cool idea, but yeah, maybe I'm being a bit over ambitious. I haven't run into slowdown yet, but I really haven't done much of anything. I wonder how much I should be worrying... (I'm most definitely going to use fast rom, but I don't know how much it would help.)


That simplifies it a lot. Metasprites would only need to be updated as a whole, instead of checking for each and every sprite. The screen/vram region thing sounds like it could be taxing on the CPU, I don't know how much though. I would only go that route if I absolutely need to.

The thing about the color palette doesn't sound CPU taxing at all. There are only 8 palettes to look at a time.

Some things to fix in your code:
- the whole thing can just be done in 8-bit mode, since your dealing with just 8-bit numbers
- there are 8 palettes, not 32 palettes
- replace "ldx" with "txa"
- palette value starts at bit-9 of the attributes word, so you need an "asl" before storing to "attributes+1"
- replace "ror" with "and #$03"
- DMA should be done in another routine, during vblank


Top
 Profile  
 
 Post subject: Re: Palette Checker
PostPosted: Sat Mar 28, 2015 8:10 pm 
Offline
Formerly Espozo
User avatar

Joined: Mon Sep 15, 2014 4:35 pm
Posts: 3377
Location: Richmond, Virginia
Well, I actually got around to trying to make it to where I have a giant table in ram that has information for all the object palettes that are going to be used in the game, (Each slot in the table consists of 2 bytes: one that says where the palette is in cgram, and one that says how many objects are using that palette. When the second number is 0, you remove the palette from cgram.) and I have the table that sees which of the 8 onscreen sprite palettes are occupied. Because the DMA routine needs to be during vblank, I made another table which has the addresses of any new palettes. (By the way, the address cannot be 0. Otherwise, the code thinks that there is no palette and quits.) I'm feeling rather lazy right now, so nothing is commented, but I can change that latter. (Oh yeah, one thing, how do you get both an 8 bit accumulator and index registers?)

Here's the beginning routine:

Code:
.proc start_palette_checker
  rep #$30
  sep #$20   
  ldx #$00

prepare_for_palette_checker_loop:
  inx
  cpx #$08
  bcs palette_slot_not_found

palette_checker_loop:
  lda OnscreenPaletteTable,x
  bne prepare_for_palette_checker_loop

done:
  ldx
  asl
  stx TotalPalettesTable,y

  lda #$01
  sta OnscreenPaletteTable,x

  lda PaletteAddressTable,y
  sta DMAPaletteRequestTable,x
  inx
  sta DMAPaletteRequestCounter

palette_slot_not_found:
  rts

.endproc

PaletteAddressTable:
  .byte $??,$??,$??,$??,etc...

Here's the DMA routine:

Code:
.proc start_palette_uploader
  rep #$30
  sep #$20
  ldx #$00
  ldy #$00
  stz DMAPaletteRequestCounter
 
palette_uploader_loop:
  sty $2121

  lda DMAPaletteTable,x
  beq done:
  sta $4302
  lda #$01
  sta $4304
  lda #$10
  sta $4305

  stz $4300
  lda #$22
  sta $4301
  lda #$01
  sta $420B

  tya
  clc
  adc #$10
  tay
  inx
  cpx #$08
  bne palette_uploader_loop

done:
  rep #$30
  stz DMAPaletteTable
  stz DMAPaletteTable+2
  stz DMAPaletteTable+4
  stz DMAPaletteTable+6
  rts

.endproc


Top
 Profile  
 
 Post subject: Re: Palette Checker
PostPosted: Sat Mar 28, 2015 9:33 pm 
Offline

Joined: Fri Jul 04, 2014 9:31 pm
Posts: 966
Espozo wrote:
Oh yeah, one thing, how do you get both an 8 bit accumulator and index registers?

sep #$30

...or sep #%00110000, if you want it to be obvious what it's doing to the P register.


Top
 Profile  
 
 Post subject: Re: Palette Checker
PostPosted: Sun Mar 29, 2015 10:26 pm 
Offline
Formerly Espozo
User avatar

Joined: Mon Sep 15, 2014 4:35 pm
Posts: 3377
Location: Richmond, Virginia
Well, I'm 99.9% percent sure that this is good, because I wrote

Code:
  ldy #$00
  jsr start_palette_checker

and it showed the correct palette in the palette 0 (for sprites). I then wrote

Code:
  ldy #$00
  jsr start_palette_checker
  ldy #$02
  jsr start_palette_checker

and it also worked, having the second palette going to palette 1 because the first one was already occupied. Next, I copied this about 12 times or something and even though that is too many palettes for the SNES to handle for sprites, it didn't seem to mess anything up and instead just refused to try to load the palettes, like I want it to. I haven't linked the code with any of the objects yet, but I doubt it will do anything unexpected. Anyway, I have the code made, and I feel like sharing it, do feel free to use it. I can answer questions if anyone has any.

Here's the first part:

Code:
.setcpu "65816"
.smart

; See Game.asm
.importzp DMAPaletteRequestCounter
.import TotalPalettesTable
.import OnscreenPaletteTable
.import DMAPaletteTable
.import Player1Palette, BulletPalette

; Used by Game.asm
.export start_palette_checker

.segment "CODE"

;
; start_palette_checker
;
.proc start_palette_checker
  rep #$30
  sep #$10
  ldx #$00

palette_checker_loop:
  lda OnscreenPaletteTable,x
  beq done

prepare_for_palette_checker_loop:
  inx
  cpx #$08
  bcs palette_slot_not_found
  bra palette_checker_loop     ;Bug fix by UnDisbeliever

done:
  txa
  sta TotalPalettesTable,y
 
  lda #$01
  sta OnscreenPaletteTable,x

  rep #$30
  ldx DMAPaletteRequestCounter
  lda PaletteAddressTable,y
  sta DMAPaletteTable,x
  inx
  inx
  stx DMAPaletteRequestCounter

palette_slot_not_found:
  rts

.endproc

PaletteAddressTable:
  .word Player1Palette,BulletPalette (These can be whatever, of course.)

And here's the DMA part: (This goes under the vblank routine.)

Code:
.setcpu "65816"
.smart

; See Game.asm
.importzp DMAPaletteRequestCounter
.import DMAPaletteTable

Player1Palette = 0
BulletPalette = 2

; Used by Game.asm
.export start_palette_uploader

.segment "CODE"

.proc start_palette_uploader
  sep #$30
  ldx #$00
  ldy #$80
  stz DMAPaletteRequestCounter
 
palette_uploader_loop:
  sty $2121

  rep #$30 
  lda DMAPaletteTable,x
  beq done
  sta $4302
  sep #$30
  lda #$00
  sta $4304
  lda #$20
  sta $4305

  stz $4300
  lda #$22
  sta $4301
  lda #$01
  sta $420B

  tya
  clc
  adc #$10
  tay
  inx
  inx
  cpx #$10
  bne palette_uploader_loop
  rep #$30

done:
  stz DMAPaletteTable
  stz DMAPaletteTable+2
  stz DMAPaletteTable+4
  stz DMAPaletteTable+6
  stz DMAPaletteTable+8
  stz DMAPaletteTable+10
  stz DMAPaletteTable+12
  stz DMAPaletteTable+14
  rts

.endproc


Last edited by Drew Sebastino on Mon Mar 30, 2015 5:03 am, edited 1 time in total.

Top
 Profile  
 
 Post subject: Re: Palette Checker
PostPosted: Mon Mar 30, 2015 1:55 am 
Offline
User avatar

Joined: Mon Mar 02, 2015 1:11 am
Posts: 76
Location: Australia (PAL)
Espozo wrote:
Well, I'm 99.9% percent sure that this is good, because I wrote

...


Here's the first part:


Umm I don't want to sound like a smarty-pants, but I think you kinda forgot to loop back to palette_checker_loop. Running your code in my head, the first time it executes it branches to done. The second time it falls into done with X = 1, because there is no loop.

I trust you when you said it works, did you submit the wrong code?

Also, you appear to be preforming a 16 bit check on OnscreenPaletteTable, even though it appears to use 8 bit values. This will cause problems when slot 2 is clear, but slot 3 is allocated.


Espozo wrote:
Well, I actually got around to trying to make it to where I have a giant table in ram that has information for all the object palettes that are going to be used in the game


You appear to have TotalPalettesTable inside shadow RAM. I'm not sure how big it is, but I guess it would be 2*Number of Palettes and could easily grow to 256 bytes. Shadow RAM is precious, you only have ~7.5KiB of the stuff to play with.

May I recommend you move TotalPalettesTable to WRAM bank $7E or $7F and access it through long, X addressing. There would be some overhead, but as the start_palette_checker/palette_cleanup routines would only be called on object spawn and destroy it is well worth the RAM savings.


Top
 Profile  
 
 Post subject: Re: Palette Checker
PostPosted: Mon Mar 30, 2015 5:01 am 
Offline
Formerly Espozo
User avatar

Joined: Mon Sep 15, 2014 4:35 pm
Posts: 3377
Location: Richmond, Virginia
UnDisbeliever wrote:
but I think you kinda forgot to loop back to palette_checker_loop. Running your code in my head, the first time it executes it branches to done. The second time it falls into done with X = 1, because there is no loop. I trust you when you said it works, did you submit the wrong code?

No, that was the one I used, and I even assembled the game just now using it. (I'm really not even sure how it worked.)

UnDisbeliever wrote:
Also, you appear to be preforming a 16 bit check on OnscreenPaletteTable, even though it appears to use 8 bit values. This will cause problems when slot 2 is clear, but slot 3 is allocated.

Where? I'm pretty sure the only place where I check onscreen palette table is here, and the accumulator is set to 8 bit.
Code:
.proc start_palette_checker
  rep #$30
  sep #$10
  ldx #$00

palette_checker_loop:
  lda OnscreenPaletteTable,x
  beq done


Quote:
You appear to have TotalPalettesTable inside shadow RAM. I'm not sure how big it is, but I guess it would be 2*Number of Palettes and could easily grow to 256 bytes. Shadow RAM is precious, you only have ~7.5KiB of the stuff to play with.

May I recommend you move TotalPalettesTable to WRAM bank $7E or $7F and access it through long, X addressing. There would be some overhead, but as the start_palette_checker/palette_cleanup routines would only be called on object spawn and destroy it is well worth the RAM savings.

You're probably going to hate me for saying this, but what exactly is shadow ram? :oops:


Top
 Profile  
 
 Post subject: Re: Palette Checker
PostPosted: Mon Mar 30, 2015 6:20 am 
Offline
User avatar

Joined: Mon Mar 02, 2015 1:11 am
Posts: 76
Location: Australia (PAL)
Espozo wrote:
Where? I'm pretty sure the only place where I check onscreen palette table is here, and the accumulator is set to 8 bit.


No, SEP #$10 sets the Index Register to 8 bits. SEP #$20 sets the accumulator to 8 bit.

Espozo wrote:
You're probably going to hate me for saying this, but what exactly is shadow ram? :oops:


Shadow RAM one of the names for the WRAM[1] mirror located in addresses $0000-$1FFF of Banks $00-$3F & $80-$BF. It contains a shadowed mirror of WRAM addresses $7E:0000 - $7E:1FFFF.

It contains the first 8KiB of WRAM and is the area Direct Page and the Stack are processed in. I usually allocate 512 128 bytes at $1F80-$1FFF for the stack leaving me with 7.8KiB to play with.

For most ca65 configs shadow RAM is the BSS segment.

[1] Work RAM.

EDIT: Changed stack size from 512 bytes to 128 based on Khaz's post


Last edited by UnDisbeliever on Mon Mar 30, 2015 9:09 am, edited 3 times in total.

Top
 Profile  
 
 Post subject: Re: Palette Checker
PostPosted: Mon Mar 30, 2015 8:28 am 
Offline
User avatar

Joined: Thu Dec 25, 2014 10:26 pm
Posts: 311
Location: Canada
UnDisbeliever wrote:
It contains the first 8KiB of WRAM and is the area Direct Page and the Stack are processed in. I usually allocate 512 bytes at $1E01-$1FFF for the stack leaving me with 7.5KiB to play with.

My god man what do you DO to your stack? I mean I have a mostly-functional game on my hands at this point and I don't think my stack ever gets lower than about $1FD0ish. I try to avoid using it at all except to preserve registers and occasionally quickly store a value I'm going to use again in a second...


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 23 posts ]  Go to page 1, 2  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 4 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group