Monday 15 July 2013

BACKGROUNDS

Direct Memory Access (DMA) functions are found in C:\devkitPro\libnds\include\nds\dma.h.

All of the functions relating to setting backgrounds (including bgInit(), bgGetGfxPtr(id), etc.) can be found in C:\devkitPro\libnds\include\nds\arm9\background.h

When you use the <background.h> function :

int bgInit(int layer, Enum BgType, Enum BgSize, int mapBase, int tileBase)

you, both, receive an id for that background which can be used later in such functions as bgGetGfxPtr(id) and bgGetMapPtr(id); while, additionally, setting the memory slots for both map and tile bases, which the compiler will subsequently utilize to give you those memory addresses required.

Therefore, store it in a variable, thus:

int bg1 = bgInit(0, BgType_Text8bpp, BgSize_T_256x256, 0,1);

  • The standard tile size used by the DS is 8*8 pixels => 256x256 is 32 tiles squared. A standard map block in VRAM memory is therefore (u16 or half-word) 2 bytes * 32*32, which equals 2kb. This is the value, starting at zero, that is required in the mapBase parameter of bgInit.
  • If you plan on using larger maps, such as BgSize_T_512x512, etc. you will be using 4 map blocks or above, and subsequent calls to bgInit() will require that you start with a mapBase value of 4 (or 8, 12, etc. )
  • If go into (or beyond) mapBase of 8, you will intrude upon the space commonly occupied by the tile Blocks...
  • Each tile Block is 16kb in length (the equivalent of 256 tiles; and bearing in mind that most good map editors will only list the tiles actually used in any one map), and they commonly start in block 1 of the VRAM memory space (as above). If, however, the map blocks exceed their normal slot, it is a simple matter of transferring the tile Blocks forward to a tileBase of 2, thus:

int bg3 = bgInit(2, BgType_Text8bpp, BgSize_T_512x512, 8,2);

The DS provides two ways to assign graphics data to the appropriate VRAM banks: swiCopy(), which uses the CPU; and dmaCopy(), which bypasses it. Their templates are very similar:
void swiCopy(const void * source, void * dest, int flags);

void dmaCopy(const void * source, void * dest, uint32 size)


  • flags, apparently represents the size of the data to copy/fill (in words or 4byte) or'd with the copy mode size (word or halfword) and type (copy or fill).    

SPRITES

All of the functions relating to sprites, including those of the Object Attribute Memory (OAM) can be found in C:\devkitPro\libnds\include\nds\arm9\sprite.h 

Sprites are primarily all about the Object Attribute Memory (OAM) functions: of which there are many necessary to complete, before you can achieve the desired results. We start with oamInit():

void oamInit(OamState* oam, SpriteMapping mapping, bool extPalette);

  • OamState can only be either &oamMain or &oamSub depending on which screen you are rendering the sprite to. This is a small (1kb) data bank with direct 32bit access to the ARM 9 processor. In every instance of it's usage it is entered into the parameters of a function as a pointer, since it is updated by each function.
  • SpriteMapping is an enumerator with one of 9 possible options. I always enter SpriteMapping_1D_128 since I don't really understand any of the explanations: “1D tile mapping 128 byte boundary between offset”...Yeah.
  • Extended palettes are only for 8bpp sprites.
  • (Very) typical example:

oamInit(&oamMain, SpriteMapping_1D_128, false);

Much less standardized, and with a very important return output, is the function oamAllocateGfx():

u16* oamAllocateGfx(OamState *oam, SpriteSize size, SpriteColorFormat colorFormat);

  • OamState, is the same as in the previous function.
  • SpriteSize is an enumerator which goes up to 64x64 pixel sprites (though, as with the maps, later, it is possible to combine several sprites together to make something larger, if necessary)
  • SpriteColorFormat is typically 256 colors, but can use, both, 16 color format and 16bit bitmaps.
  • The function returns a half-word pointer. This is crucial. It describes the address in VRAM of the allocated and bound sprite and should therefore be assigned to a pointer variable for easy recall in later functions:

sprite->sprite_gfx_mem = oamAllocateGfx(&oamMain, SpriteSize_64x64, SpriteColorFormat_256Color);

If you are using the (GRIT) GBA Raster Image Transmogrifier (which you should be), importing your image as a .h file at the top of your code will result in your importing, both, tile and palette data, of the form: filenameTiles and filenamePal.

Your palette, you merely need to bind directly to the SPRITE_PALETTE using dmaCopy, e.g.:

dmaCopy(manPal, SPRITE_PALETTE, 512)

  • dmaCopy is explained in further detail, below, but is basically in the form of dmaCopy(from, to, size-in-bytes)
  • the size, in this instance, is 512 since each color is a half-word or 2 bytes (16bit colours of the form aBBBBBGGGGGRRRRR – alpha, blue, green, red – each 0 to 32) and there are a maximum of 256 colours in any one palette.
  • It should be noted that it is perfectly possible to, otherwise, save the address of the start of the imported palette to a pointer variable, copy the address of this pointer to the SPRITE_PALETTE using dmaCopy(), and therefore, switch between several palettes by setting that variable to point at different sets when needed.

Your tiles, alternatively, you need to have a pointer variable reference their address, e.g.:

u8* frame_gfx = (u8*) manTiles;

such that you can bring in the tiles as an array and merely add the required amount of bytes to the end of the address in order to advance it to the next frame of animation:

u8* offset = sprite->frame_gfx + frame * 64*64;

then, it is entered into dmaCopy():

dmaCopy(offset, sprite->sprite_gfx_mem, 64*64);

  • Remember that sprite_gfx_mem variable? Yeah, this is the pointer variable which references the address of the allocated and bound sprite, and that is why it was so important to receive it from oamAllocateGfx().

There are a couple more OAM functions which contain information about the sprite – usually relating to special functionality, such as scaling and rotating – but which otherwise have to be completed each time, even if you do not use such aspects. These are:

void oamSet(OamState* oam, int id, int x, int y, int priority, int palette_alpha, SpriteSize size, SpriteColorFormat format, const void* gfxOffset, int affineIndex, bool sizeDouble, bool hide, bool hflip, bool vflip, bool mosaic);

Yep, that's a big one. This is pretty much all of your attributes.

  • Id is the number of this set, increasing from 0 to 127, for each new sprite.
  • x and y are positions on the screen, for animation purposes.
  • Priority is between 0 and 3. Never used it: always 0.
  • palette alpha is used in extended Palette mode and with bitmaps. Set to 0.
  • SpriteSize and ColorFormat are the same as used previously in oamAllocateGfx.
  • *gfxOffset is our favourite pointer again – the pointer variable which references the address of the allocated and bound sprite.
  • AffineIndex is used for transformations between 0 and 32. Set to -1.
  • finally we end with five bool transformations, which are fairly obvious from the names, and are typically all set to false, (except for hflip – which is very useful for turning your character around) thus:

oamSet(&oamMain, 0, sprite.x, sprite.y, 0, 0, SpriteSize_64x64, SpriteColorFormat_256Color, sprite->sprite_gfx_mem, -1, false, false,
manFlip, false, false);

Finally, we have oamUpdate():

void oamUpdate(OamState* oam);

A nice easy one to end with.


  • This needs to be called after each Vblank.   

INIT

 videoSetMode(MODE_0_2D);

vramSetBankA(VRAM_A_MAIN_BG);
vramSetBankB(VRAM_B_MAIN_SPRITE);