What follows is a very detailed look at the sprite hardware on the DS including memory and register layout and a listing of basic libnds functions dealing with sprites. This post is a bit technical and does not need to be understood before moving on to the sprite examples (in fact, an argument can be made that it is best to skip all but the overview until you have gotten a sprite or two to display).
Keep in mind the DS has two distinct 2D graphics cores which each handle one screen. The main engine and the sub engine have few differences. We will focus on the main engine in this discussion and point out any differences we encounter along the way. Recall, that the engines do not imply a specific physical screen as either engine can be used to control either the top or bottom screen.
To begin, each core is capable of controlling 128 independent sprite objects which share a common graphics and palette memory.
Each object can have a rotation, blend, or mosaic applied (although not strictly independently as we shall see).
Each sprite can be one of 3 color formats: 256-color paletted, 16-color paletted, or 16-bit direct color bitmap.
Each sprite can be one of the following pixel sizes:
Sprites can contain transparent pixels allowing sprites of any size or shape to be represented as long as they can fit inside one of the above boxes. If you need even larger sprites we will learn a few tricks to stretch them and combine them to create sprites of any size.
OAM Attributes (1KB): 0x07000000
Palette Memory (1KB): 0x05000200
Graphics Memory (256KB): 0x6400000
Extended Palette Memory: (Memory mapped so varies)
OAM Attributes (1KB): 0x07000400
Palette Memory (1KB): 0x05000400
Graphics Memory (128KB): 0x6600000
Extended Palette Memory: (Memory mapped so varies)
Object Attribute Memory:
Each core has what is known as Object Attribute Memory which contains the state for our 128 sprites. This state is stored in a set of 3 16-bit attributes (one set for each sprite) which control nearly every aspect of the sprite.
|0-7||The Y coordinate of the top of the sprite|
|8||The rotation scaling flag which determines if a scale/rotation is to be applied. The setting of this flag controls how several of the bits below are interpreted.|
|9||Size double / hide sprite flag: When the rotation and scaling flag is set this flag will allow the sprite to double in size when a scaling or rotation is applied. When the rotation scaling flag is not set this bit will hide the sprite if set (very useful).|
|10-11||Display mode: 0 is a normal paletted sprite, 1 is a blended sprite, 2 means the sprite is acting as an window mask, and 3 is used to specify a bitmap sprite.|
|12||Mosaic flag: when set the mosaic will be applied|
|13||Color depth: 0 = 16 color 1 = 256 color|
|14-15||Object shape: 0 = Square, 1= Wide, 2 = Tall|
|0-8||The X coordinate of the top of the sprite (one more bit than Y as the screen is wider)|
|9-13||Bits 9-13 are used to store the 5 bit index into the 32 possible rotation/scaling attributes to apply to the sprite. When the rotation and scaling flag is not set bits 9-11 are unused and bits 12 and 13 can be used to flip the sprite.|
|12||When the rotation and scalling flag is NOT set this bit sets the horizontal flip flag, when set the sprite will be flipped horizontally. When the rotation scaling flag is set this bit is used as part of the rotation attribute selection|
|13||When the rotation and scalling flag is NOT set this bit sets the vertical flip flag, when set the sprite will be flipped vertically. When the rotation scaling flag is set this bit is used as part of the rotation attribute selection|
|14-15||Selects the object size (see table above)|
|0-9||Selects the offset of the start of the sprites graphics|
|10-11||The sprite priority (what order it is drawn with respect to other sprites and backgrounds, two sprites with the same priority will be drawn in order of OAM number)|
|12-15||Palette number when in 16-color mode or when using extended palettes in 256-color mode. These bits control the alpha blend when in bitmap mode.|
Rotation and Scaling Attributes:
Following each set of 3 OAM attributes is a single 16-bit rotation and scale attribute. These 16-bit values are what is used to control the rotation and scale of a sprite. It actually takes 4 rotation attributes to describe the rotation and scale which means there are only 32 available rotations which can be applied (128 sets of oam attributes, 4 sets per set of rotation attributes, 128/4 = 32).
The layout in memory is as follows:
|Sprite 0||0x07000000||Attribute 0|
|0x07000006||Rotation 0 PA|
|Sprite 1||0x07000008||Attribute 0|
|0x0700000E||Rotation 0 PB|
|Sprite 2||0x07000010||Attribute 0|
|0x07000016||Rotation 0 PC|
|Sprite 3||0x07000018||Attribute 0|
|0x0700001E||Rotation 0 PD|
|Sprite 126||0x070003F0||Attribute 0|
|0x070003F6||Rotation 31 PC|
|Sprite 127||0x070003F8||Attribute 0|
|0x070003FE||Rotation 31 PD|
As you may notice, each rotation attribute contains 4 values named PA, PB, PC, PD which form an affine matrix. For a complete and rather detailed description of this matrix please read the link above. Don’t pay much attention to any code presented in it, we are not going to use it as we have our own code for such things (although its perfectly fine for consumption of you so chose).
By selecting the appropriate bits in the attributes described above you can select either 16 color or 256 color palettes. A 16 color sprite uses half the graphics memory of a 256 color sprite which uses half the memory of a direct color bitmap sprite. By trading the number of colors for memory consumption we can make some smart decisions about the usage of DS graphics resources.
As we alluded to above, the sprite palettes are shared and we can control per sprite what color format it is in (sort of).
To display a sprite we simply load some colors into our palette then load the bitmap into sprite memory…unfortunately there is one small item left to discus and that is how the pixel data is actually stored.
Sprite pixel graphics, much like background graphics are constructed of tiles. What that means is that if you have a 16×16 sprite it is actually stored internally as 4 8×8 tiles. To translate your sprite from your graphics program to something the DS can understand we will need a tool. That tool is grit.
The tiles themselves can be stored in memory either linearly for each sprite (known as 1D mapping) or they can be stored in a 2D grid of 32×32 tiles in 16-color mode or 16×32 tiles in 256-color mode (2D mapping). There are few uses for 2D mapping and it makes it rather challenging to organize your graphics so we are pretty much going to let it go at that and stick to 1D mapping.
The offset from attribute 2 is used to specify where in memory the first tile of the sprite graphics reside. How we calculate that depends on another setting in the main video display control register. This setting determines the stride between starting tiles for sprites and can be 32 bytes, 64, 128 or 256. This setting allows you to reach all of sprite graphics memory by a single 9 bit offset stored in attribute 2. Location will be: Start of graphics memory + offset * stride.
You can enable extended palette memory for both backgrounds and sprites globally (for each engine). Once this is done you will have access to 16 256-color palettes for your sprite. Normal sprite palette memory will be ignored in this case. Extended palettes take a bit more work to set up and manipulate but it is enough for now to know we have to allocate a VRAM bank to them and we can only load palette data into that bank when it is unmapped.
The DS also allows for direct color bitmap sprites. These are very useful not only because they allow the full range of color in your sprite but also because they allow you to specify an alpha blend value in the unused bits that normally specify the palette index to use. In this mode each pixel is a 15 bit color value with the most significant bit an alpha flag. This alpha flag is how you specify a transparent pixel (0 means transparent, 1 means draw it).
There is no palette so the only issue is how to store the sprite graphics data in memory. In bitmap mode sprite graphics are not tiled but stored as a linear bitmap. When in 1D mapping mode each sprite graphic is stored sequentially and the offset is simply offset * stride (where stride is 128 or 256).
In 2D mode the sprite graphics memory is treated as a single large bitmap that is 128 or 256 pixels wide. The offset gets split into a 5 bit X and a 4 bit Y value (or 4 bit X and 5 bit Y depending on the chosen stride). You specify the x and y of the top left corner of your sprite in the large bitmap (divided by 16).
That is it for our sprite technical overview. For a more in-depth look at these features be sure to check out the examples.