Homebrew Setup palette correctly for two backgrounds libnds

  • Thread starter Thread starter DerSamelt
  • Start date Start date
  • Views Views 1,355
  • Replies Replies 12

DerSamelt

Member
Newcomer
Joined
Mar 8, 2025
Messages
13
Reaction score
2
Trophies
0
XP
64
Country
Germany
Hello, so I was experimenting a little bit with backgrounds, and tried loading two at the same time on different layers. That worked partially because the colors were incorrect, and upon further inspection, it seems that every background takes its colors from the same adress in VRAM (I could be very wrong so correct me if that's not the case), wich causes weird color combinations (since you load both of the backgrounds palette into the same BG_PALETTE slot). I tried offsetting the adress where the palette is saved but that did nothing.
I wanted to ask if there is a way on how to tell the background at wich adress it should take its palette, or if there is a way to merge two palettes into one or something similar.
 
Hi, depends 16 or 256 colors backgrounds. If we talk about 16 colors for the previous example then it's about to choose next of 15 free palettes that left for rest of the backgrounds, or other efects. It's also about to tell and change oldest most significant 4bits of every single mapped tile (0x0xxx - 0xFxxx palette number).
I choose 15th offset for the map as example. You can check how it looks in debugging tools like extended version of no$gba, marked as green area. Shomehow it shares space with tileset, but there's no collision here as we don't load 32 rows of tiles but only 24 and there's no need to scrool.
Code:
    bg = bgInit(1, BgType_Text4bpp, BgSize_T_256x256, 15,0);

    dmaCopy(DS_Tiles[c], bgGetGfxPtr( bg ), DS_TilesLen[c] );
    dmaCopy(DS_Map[c] , bgGetMapPtr(bg),  DS00MapLen );
    dmaCopy(DS_Pal[c], BG_PALETTE, DS00PalLen );
   
    //set higher priority (0) for 2nd background
    bg2 = bgInit(0, BgType_Text4bpp, BgSize_T_256x256, 31,2);
    c++;
   
    dmaCopy(DS_Tiles[c], bgGetGfxPtr( bg2 ), DS_TilesLen[c] );
    //load next 16 colors palette as next one, (BG_PALETTE + 16 colors offset)
    dmaCopy(DS_Pal[c], BG_PALETTE + DS00PalLen / sizeof(short int), DS00PalLen );
    SetMapForPalette( bg2, c, 1 );

Code:
void SetMapForPalette(int bg, int c, int a){
    if (a>15) a = 0;
    a *= 0x1000;
    for(short int i=0; i < TilesPerScreen; i++)
        MapBuf[i] = DS_Map[c][i] | a;
    dmaCopy( MapBuf , bgGetMapPtr(bg),  DS00MapLen );
}
 
Could you perhaps elaborate a little the "SetMapForPalette" function? It's a little confusing with the bitwise or operator (I believe it is). Also how does the tile and mapbase affect it?

Thanks for the help!
 
Yes it's biwtise OR operator that set only those bits that are needed...
We can have 16x16-colors palettes at the BG_PALETTE same like possible number of permuations with 4 oldest bit of every tiled map. Make notation that we operate only on Map buffer, so we can use that function many times. DS_Map[c] array will not change letting us to use as many bitwise operation as we need.
"a" variable is the number of palette, here as 1 indicate second palette, as the first palette is the palette 0.
0x1000 - first digit of hexadecimal value of the single mapped tile (palette number) is exactly four bits. So if on default there's always zero ( or if is not because we can force somehow that grit will decide for the number of pallette I don't know if we can)
then we can add some bit shift operation before like 4 bits left and then 4 bits right to clear that palette space, but if grit set us up correct palette number then we don't need that function.
so let's say second mapped tile in binary was previously like this

0000000000000001 (let second mapped tile indicate some non transparent second tile in the tileset)
with "a" ( 0x1000) that equals in binary
0001000000000000
we get what we need with OR bitwise operator (unchanged mapped tile value + palette number)
0001000000000001

I hope I explained a little bit more or less

we could use just
Code:
MapBuf[i] = DS_Map[c][i] + a;
and it should give the same effect but we operate on the maxed out values and we should change MapBuf to unsigned short int type first (instead of short int like in previous example). Operating on bits is more signed/unsigned type independent and it mark the maped tile sections (first oldest 4 bits for palette number and 12 for the rest ( 2 bits for tile rotation and 10 bits for index)). Using bitwise operations seems to be more intuitive and readable I guess, but better to use map buffer as usnigned type for correct formatting, let's say if we want to control values for output in debug mode. Maybe it's also good to have "a" as unsinged short int instead of int, but it doesn't make much difference here.
 
Last edited by plasturion,
Alright I understand it little bit better. I finally got the code to work in the example. Just a few extra questions so I can understand it cuz I really don't know what I'm doing here:
-How exactly does each background variable "know" at what location in memory the corresponding palette is saved, unlike with Map and Gfx there is a function to get the corresponding adress?
-How does the 4th and 5th parameter of the bgInit function affect? Because when I put different values than of your code I got weird results.

Really appreciate your help

:)
 
You're welcome, I just like this stuff, so sure you can always ask.

ok... so first answer... because this graphic mode was designed to know where to look... ?
let's inspect some constants of libnds
https://libnds.devkitpro.org/video_8h.html
Code:
#define     BG_PALETTE   ((u16*)0x05000000)
     background palette memory
 
#define     SPRITE_PALETTE   ((u16*)0x05000200)
     sprite palette memory
 
#define     BG_PALETTE_SUB   ((u16*)0x05000400)
     background palette memory (sub engine)
 
#define     SPRITE_PALETTE_SUB   ((u16*)0x05000600)
     sprite palette memory (sub engine)
let's focus only on those ones... 0x200 is 512 in decimal, 256 x 2 (size of short int)
so one full 256 colors palette or 16 16-colors palettes.
now in 16colors mode when mapped tile have an order tie to proper palette, it can't say no because that's a hardware "have to".
... so mapped tile 0x1001 will always take palette from 0x05000020
same like tile 0x2001 take palette from 0x05000040 (if we talk about main engine for BG)
etc.. 0x20 is 32 in decimal 16x2 (short int again, one 16-colors palette)

second answer... that's good that you experiment, and the best would be to check how it look in emulators add-ons.
i mentioned previously about full version of no$gba, but you can check also in desmume...
desmume.jpg

I think it visualy present fairly ok how the VRAM is setup. (tilebase and those short vertical lines is the mapbase) 15th and 31th 2 kilobytes offset from 0x6000000, as for the tilebase one offset is much bigger - 16k.
Sure maybe it's not best place for mapbase, but it's all up to you.
If we want to use many backgrounds how about to use first 16k for map bases only? any solution can be accepted as long there's no offsets collision. (but if you like, you can force that collision and check results in this "tile view")
Opposed to palettes, backgrounds association is not stiff, so we can choose and tie any memory addr with any bank. (almost)
I think this link can be also useful https://mtheall.com/banks.html
and for the sniplet example this is how it should look like
https://mtheall.com/vram.html#T0=2&NT0=768&MB0=31&TB0=2&S0=0&T1=2&NT1=768&MB1=15&TB1=0&S1=0
 
Last edited by plasturion,
Awesome! Thanks for the help. Finally got DeSmuMe to work so I'll experiment a little with how the VRAM looks.
Post automatically merged:

Just a little extra question: The "Tile num" always shows a "mini adress" (I think). But when selecting the next "8x8" tile in only increases by 1. Why is that?
 
Last edited by DerSamelt,
"Tile num" is just a tile number of tilebase, that's only basically index counter and doesn't tell us where exactly is stored.
If you look for more detailed references I recommend to check no$gba exclusives...
test.png
 
  • Like
Reactions: DerSamelt
Okay, so I tried now multiple times to load two backgrounds in a different project with setting the correct palette, and one of the backgrounds just doesn't want to display when using the Palette index 1. The weird thing wich I don't understand is that when I view it with no$gba in VRAM under the background section it displays perfectly fine, but on the DS itself there is nothing. I also tried loading it without the other background but nothing.

(BG 0 is the white background, BG 1 is the tileset thingy I made wich is the only thing visible)


Any ideas?

backgroundproblemmesad.PNG
 
It's hard to say with no source but there are couple of things to make sure and also general about palettes...
first one is priority - background 0 will always cover background 1, same lake BG1 cover BG2,
only one of those backgrounds can display 16 colors - the one with lowest priority.. if there are BG0, BG1 and BG2, only BG2 will display color of index 0. For the rest of palettes only 15 colors are visible and color at index 0 in all of them is treated as transparency. If we see only one background I'm guessing the second one doesn't have (let's say) black or white color at index 0 so the whole layer may be covered. (Or rather BG0 is just whole transparent (because only white color) If we want white background beaneth, it should be set with lowest priority - BG1) The question is what area / color is destined to be transparent if we want to see two background simultaneously on the same screen and how it correspond with colors indexing? (just confirm in any graphical program) Some graphical programs allows to compress colors number and change thier indexes like png format... is good to make sure to disable that option before grit do any postproduce.
 
Last edited by plasturion,
Alright, so I've added a few gray dots onto the white background, and it seems that what's happening is wich you mentioned that the first color is transparent. The palette has now 2 colors, and the gray color seems to be the one transparent, and the white background displays fine. Could a potential solution be something like this?
short int WhiteBgBuf[TilesPerScreen]; //Whitebgtiles (tileset of the whitebg) has 2 tiles, one junk tile (a color like black) wich is getting transparent becos of it uses palette color at index 0 and one that is white and uses the second color of the palette wich shouldn't get transparent for(short i = 0; i < TilesPerScreen; i++){ whiteBgBuf[i] = 1 | (1 << 12); } dmaCopy(whitebgTiles, bgGetGfxPtr(bg), whitebgTilesLen); dmaCopy(whiteBgBuf, bgGetMapPtr(bg), whitebgMapLen); dmaCopy(whitebgPal, BG_PALETTE + whitebgPalLen/sizeof(short int), whitebgPalLen);

Or is there a better way?
 
Hmm.. I see, two colors so one of them must be transparent... but I think it's better to decide on it while preparing images.
It's more about graphical file preparation matter
I'm not familiar what people use nowadays... maybe aseprite or photoshop, I recommend try Jasc Paint Shop Pro 7.04
We really can have 16 different colors in the palette of png file format, while image has visible just only one. Here black
is choosed to be tansparent. So we can be sure this image will be always blank sheet of solid white on any BG.
test.png

There are other options however this is not necessary as long you have control of indexes in software like above.
You can try add to .grit parameters something like this
# transparency color
-gT00FF00
but it doesn't make much difference here as long this color is not present on the image (it makes some index swap when they are mixed or we don't know / care where they are). Not useful here.
 

Site & Scene News

Popular threads in this forum