Tutorial  Updated

DS Programming for Newbies!

3Lwpv.png

Table of Contents:

Introductory Chapters:
  1. Preparing the environment
  2. Variables!
  3. Functions!
  4. Operators in C
  5. Conditions - if/else Statements and switches
  6. Looping - for() and while() Loops
  7. Containers of Variables - Arrays and Structures
Introduction to DS Hardware:
  1. RAM and VRAM
  2. OAM and 2D Sprites
Practical use of libnds:
  1. Input: Keys and the Touchscreen
Practical Use of NightFox Lib:
  1. NightFox Lib Integration
  2. 2D MODE-0 Part 1 - Tiled Backgrounds
  3. 2D MODE-0 Part 2 - Tiled Sprites
Excercises:
  1. Your first program!
  2. MODE-0 Tiled Backgrounds Example
  3. MODE-0 Tiled Sprites Example
  4. Our very first game: Tic Tac Toe!
Additional Utilities:
  1. GRIT


:download: PDF Version maintained by CannonFoddr available on FileTrip HERE!

:download: PDF Version maintained by Pomegrenade GBAtemp Mirror HERE!




Preface


Hello and welcome! If you are reading this then it’s likely that you’re interested in getting to know more about programming for the Nintendo DS! If you are not, then you likely took the wrong turn, but let’s not get into that. Let’s also start with establishing one important thing – as the title suggests, this is a “From Zero to Hero” guide. If you are an experienced programmer then it is likely that you will not benefit from it much, if at all. It is going to introduce the very basics to users who have never even seen a compiler before and never coded in their life – stuff that you probably already know and aren’t interested in anymore. You are however still welcome as this is my first tutorial and will likely require a certain degree of proof-reading, plus, you may of course have useful suggestions! Keep in mind the target audience though, I’m doing my best not to introduce complicated concepts early on. If you’re not an experienced programmer or never programmed at all, this is a great place to start!

I’ve seen many guides approaching this subject – some were more helpful, some were rather vague, but there is one thing that was common in all of them, and it became apparent to me that something has to be done about it. The guides I’ve seen so-far are dedicated to users who are familiar with programming and only require an introduction to the DS environment, none of them are actually “tutorials” from the ground up. Does this mean that a non-experienced user simply cannot program for the DS or should not begin his adventure with programming on this exact platform? No, it does not! In fact, the DS is likely the easiest platform to program for when it comes to consoles – libnds is really not that hard to wrap your mind around and there are numerous libraries out there that facilitate programming for it even further. You probably want to ask: “If it’s so easy, why do You think it requires some sort of an explanation? The libraries are well-documented, do you expect the readers to be dill-wits who can’t follow simple examples?” and the answer to that is “No, in fact, I do believe that everybody is capable of programming, however one has to learn and acquire some basic programming habits and have some practice in C to be successful at it” and this is exactly the main goal of this tutorial. Depending on the interest shown by users and my workload at Uni this may or may not be a full-featured guide, however I promise that I will at least try to keep it up-to-date and expand upon it from time to time.

Now that the purpose is established, let’s move on to the juicy parts! I hope you will enjoy learning together and in case of any questions or suggestions, do write! Dear readers, keep in mind that the first few tutorials will be an incredibly rapid course in C, applicable to any type of programming, not just for the DS! We won’t be compiling much until this material is covered and thoroughly understood! So… Let’s get it on!
 
Last edited by Foxi4,

ocai0

Member
Newcomer
Joined
Aug 22, 2020
Messages
6
Trophies
0
XP
81
Country
Brazil
So, i've made an other type of code that makes squares appear on the screen and this was the result:

clip0004.gif

I've put the source code below:
Code:
#include <nds.h>
#include <stdio.h>
#include <vector>
//#include <algorithm>

class Square {
    public:
        int id;
        int x, y;
        u16* gfx;
        int color;
};

OamState* screen = &oamMain;
std::vector<Square> all;

void render(Square a);
void add(std::vector<Square>*);
void remove(std::vector<Square>*);

int main(void) {
    
    //Init Engine
    videoSetMode(MODE_0_2D);
    vramSetBankA(VRAM_A_MAIN_SPRITE);
    consoleDemoInit();
    oamInit(screen, SpriteMapping_1D_128, false);
    //--
    
    while(1) {
    scanKeys();
    switch(keysDown()) {
        case KEY_A:
            add(&all);
            //Sub Screen Counter (works because of the consoleDemoInit() )
            consoleClear();
            iprintf("Squares counter: %d \n", all.size());
        break;
        case KEY_B:
            remove(&all);
            //Sub Screen Counter (works because of the consoleDemoInit() )
            consoleClear();
            iprintf("Squares counter: %d \n", all.size());
        break;
    }
    
    //Main Screen
    for(int i = 0; i < (int) all.size(); i++) {
        render(all[i]);
    }
    
    //Update
    swiWaitForVBlank();
    oamUpdate(screen);
    //--
    }
    return 0;
}

void remove(std::vector<Square>* all) {
    if(all->size() > 0) {
        all->pop_back();
    }
}

void add(std::vector<Square>* all) {
    const int MAX = 10;
    if(all->size() < MAX) {
    
        int _id = all->size() + 1;
        int _x = (rand() % SCREEN_WIDTH + 1);
        _x -= _x % 16;
        int _y = (rand() % SCREEN_HEIGHT + 1);
        _y -= _y % 16;
        u16* _gfx = oamAllocateGfx(&oamMain, SpriteSize_16x16, SpriteColorFormat_Bmp);
        int _color = ARGB16(1, rand() + 31, rand() + 31, rand() + 31);
        
        Square n = {_id, _x, _y, _gfx, _color};
        all->push_back(n);
    }
}

void render(Square a) {
    dmaFillHalfWords(a.color, a.gfx, 16*16*2);

    oamSet(
        &oamMain, // sub display
        a.id, // oam entry to set
        a.x, a.y, //position
        0, // priority
        15, // palette for 16 color sprite or alpha for bmp sprite
        SpriteSize_16x16,
        SpriteColorFormat_Bmp,
        a.gfx,
        0, // rotation
        false, // double the size of rotated sprites
        false, // don't hide the sprite
        false, false, // vflip, hflip
        false // apply mosaic
    );
}

I think is something related to the 'swiWaitForVBlank()', because at the squares will be fulled in colors after some updates, and each of the stripes that appears on each of the squares are of the same color.
Just thinking here but not sure. I'm new to this stuff.
 

Foxi4

Endless Trash
OP
Global Moderator
Joined
Sep 13, 2009
Messages
30,825
Trophies
3
Location
Gaming Grotto
XP
29,840
Country
Poland
So, i've made an other type of code that makes squares appear on the screen and this was the result:


I've put the source code below:
Code:
#include <nds.h>
#include <stdio.h>
#include <vector>
//#include <algorithm>

class Square {
    public:
        int id;
        int x, y;
        u16* gfx;
        int color;
};

OamState* screen = &oamMain;
std::vector<Square> all;

void render(Square a);
void add(std::vector<Square>*);
void remove(std::vector<Square>*);

int main(void) {
  
    //Init Engine
    videoSetMode(MODE_0_2D);
    vramSetBankA(VRAM_A_MAIN_SPRITE);
    consoleDemoInit();
    oamInit(screen, SpriteMapping_1D_128, false);
    //--
  
    while(1) {
    scanKeys();
    switch(keysDown()) {
        case KEY_A:
            add(&all);
            //Sub Screen Counter (works because of the consoleDemoInit() )
            consoleClear();
            iprintf("Squares counter: %d \n", all.size());
        break;
        case KEY_B:
            remove(&all);
            //Sub Screen Counter (works because of the consoleDemoInit() )
            consoleClear();
            iprintf("Squares counter: %d \n", all.size());
        break;
    }
  
    //Main Screen
    for(int i = 0; i < (int) all.size(); i++) {
        render(all[i]);
    }
  
    //Update
    swiWaitForVBlank();
    oamUpdate(screen);
    //--
    }
    return 0;
}

void remove(std::vector<Square>* all) {
    if(all->size() > 0) {
        all->pop_back();
    }
}

void add(std::vector<Square>* all) {
    const int MAX = 10;
    if(all->size() < MAX) {
  
        int _id = all->size() + 1;
        int _x = (rand() % SCREEN_WIDTH + 1);
        _x -= _x % 16;
        int _y = (rand() % SCREEN_HEIGHT + 1);
        _y -= _y % 16;
        u16* _gfx = oamAllocateGfx(&oamMain, SpriteSize_16x16, SpriteColorFormat_Bmp);
        int _color = ARGB16(1, rand() + 31, rand() + 31, rand() + 31);
      
        Square n = {_id, _x, _y, _gfx, _color};
        all->push_back(n);
    }
}

void render(Square a) {
    dmaFillHalfWords(a.color, a.gfx, 16*16*2);

    oamSet(
        &oamMain, // sub display
        a.id, // oam entry to set
        a.x, a.y, //position
        0, // priority
        15, // palette for 16 color sprite or alpha for bmp sprite
        SpriteSize_16x16,
        SpriteColorFormat_Bmp,
        a.gfx,
        0, // rotation
        false, // double the size of rotated sprites
        false, // don't hide the sprite
        false, false, // vflip, hflip
        false // apply mosaic
    );
}

I think is something related to the 'swiWaitForVBlank()', because at the squares will be fulled in colors after some updates, and each of the stripes that appears on each of the squares are of the same color.
Just thinking here but not sure. I'm new to this stuff.
That would make sense, if the square isn't filled up before the screen blanks, it will not be fully displayed. You wouldn't normally see that problem as sprites are usually pre-made rather than generated assets. You could achieve the same effect by making a square sprite with any color and adjust it by adjusting its palette.
 
  • Like
Reactions: Coto

ocai0

Member
Newcomer
Joined
Aug 22, 2020
Messages
6
Trophies
0
XP
81
Country
Brazil
That would make sense, if the square isn't filled up before the screen blanks, it will not be fully displayed. You wouldn't normally see that problem as sprites are usually pre-made rather than generated assets. You could achieve the same effect by making a square sprite with any color and adjust it by adjusting its palette.

Yeah, i'm afraid that because of the way this turned out, this could be an sign of some kind of bug that i can't solve when i'll jump to sprites, i'm expecting something like broken sprites in the worst case... Anyway, i will jump to the sprite examples of libnds and hope that this will not be such a big deal to handle.
 

Foxi4

Endless Trash
OP
Global Moderator
Joined
Sep 13, 2009
Messages
30,825
Trophies
3
Location
Gaming Grotto
XP
29,840
Country
Poland
Yeah, i'm afraid that because of the way this turned out, this could be an sign of some kind of bug that i can't solve when i'll jump to sprites, i'm expecting something like broken sprites in the worst case... Anyway, i will jump to the sprite examples of libnds and hope that this will not be such a big deal to handle.
I wouldn't worry too much - chances are that the system was trying to display the sprite before it was fully generated. You can avoid that issue in a variety of ways.
 
  • Like
Reactions: Coto

TaruDev_

Active Member
Newcomer
Joined
Nov 18, 2019
Messages
28
Trophies
0
Age
25
XP
212
Country
Belgium
hey I was wondering how would I get a anim sprite? Like how do I convert *given img* to a 16x16 sprite with 4 anim frames?
 

Attachments

  • car_enemy.bmp
    630 bytes · Views: 228

Foxi4

Endless Trash
OP
Global Moderator
Joined
Sep 13, 2009
Messages
30,825
Trophies
3
Location
Gaming Grotto
XP
29,840
Country
Poland
hey I was wondering how would I get a anim sprite? Like how do I convert *given img* to a 16x16 sprite with 4 anim frames?
Sprite animation is covered in the Tic Tac Toe example, and in more detail in the Sprites chapter. Basically you need to create a vertical strip of frames (16x64), each 16x16 block corresponding to sprites 0-3. I like to add an extra "blank" sprite as well for the purposes of hiding it, but you can use a hide sprite function instead to the same effect.
 

TaruDev_

Active Member
Newcomer
Joined
Nov 18, 2019
Messages
28
Trophies
0
Age
25
XP
212
Country
Belgium
oh god I'm stupid, I was stuck for an hour on why it didn't work.
the asset was 16bit instead of 256 color even tho I was loading it in as a 256.
So it tried to read pallet data it did not have
 
  • Like
Reactions: Foxi4

Rarz

New Member
Newbie
Joined
Dec 10, 2020
Messages
2
Trophies
0
Age
22
XP
37
Country
United Kingdom
Introductory Chapter 0: Preparing the Environment



We will also need Drunken Coders’s Wizard to add a Nintendo DS application (among others!) as a project type:

I wanted to ask about the link given here, I have clicked on it to download it but it seems to be broken. Is it an issue on my end or is the link not functioning anymore?
 

Foxi4

Endless Trash
OP
Global Moderator
Joined
Sep 13, 2009
Messages
30,825
Trophies
3
Location
Gaming Grotto
XP
29,840
Country
Poland
I wanted to ask about the link given here, I have clicked on it to download it but it seems to be broken. Is it an issue on my end or is the link not functioning anymore?
The wizard has been dead for quite a while now, but no worries - DevkitPro and libnds still work exactly the same. The easiest way to compile nowadays is to simply use one of the templates that come with it. :)
 
  • Like
Reactions: MrMcTiller

TaruDev_

Active Member
Newcomer
Joined
Nov 18, 2019
Messages
28
Trophies
0
Age
25
XP
212
Country
Belgium
hmmpfh nf lib seems to have simple funtions for sound as well, but how do I get the .raw files? Is there a converter you would recommend?
 

Foxi4

Endless Trash
OP
Global Moderator
Joined
Sep 13, 2009
Messages
30,825
Trophies
3
Location
Gaming Grotto
XP
29,840
Country
Poland
hmmpfh nf lib seems to have simple funtions for sound as well, but how do I get the .raw files? Is there a converter you would recommend?
.raw is just that - raw, uncompressed audio. You can use any converter or editor to save in that format, just don't go crazy and use higher frequencies than the DS can handle, or effectively output, or you'll be wasting space for nothing. For larger files you can use streaming mp3's from NitroFS/FAT, or better yet, the maxmod library and tracker music which is a bit easier on the CPU.
 

TaruDev_

Active Member
Newcomer
Joined
Nov 18, 2019
Messages
28
Trophies
0
Age
25
XP
212
Country
Belgium
.raw is just that - raw, uncompressed audio. You can use any converter or editor to save in that format, just don't go crazy and use higher frequencies than the DS can handle, or effectively output, or you'll be wasting space for nothing. For larger files you can use streaming mp3's from NitroFS/FAT, or better yet, the maxmod to use tracker music, which is a bit easier on the CPU.
oh didn't know that... thank you.
 

JuanR4140

New Member
Newbie
Joined
Mar 29, 2021
Messages
3
Trophies
0
Age
16
XP
37
Country
United States
Hey, Hey, Hey! I'm the dude who messaged you recently! To me, it's still a miracle that you're still on gbatemp and even helping people out! ^^ It really means a lot, because as I've said there is little to no documentation on the internet for DS programming. I apologize if I posed the reply in the wrong section, as clicking the reply button brings me all the way to the bottom of the page and just enters the entire text of the section. I still don't know how to use this website properly, I'm new. Now, for the problem I'm facing. I followed your tutorial on sprites up to now, tried making a simple 16 x 16 256 color palette with magenta background sprite, passed it through GRIT, gave me the img and pal fie, I put them in nitrofiles and wrote this as the code:


#include <gl2d.h>
#include <nds.h>
#include <nf_lib.h>
#include <stdio.h>

int main(int argc, char **argv){
NF_Set2D(0, 0);
NF_SetRootFolder("NITROFS");

NF_InitSpriteBuffers();
NF_InitSpriteSys(0);

NF_LoadSpriteGfx("stickman", 0, 16, 16);
NF_LoadSpritePal("stickman", 0);

NF_VramSpriteGfx(0, 0, 0, false);
NF_VramSpritePal(0, 0, 0);
while(1) {
NF_SpriteOamSet(0);
swiWaitForVBlank();
oamUpdate(&oamMain);
}
return 0;
}


the result on my physical DS is just the top screen to be black, displaying nothing, and the bottom screen to be white, displaying nothing as well. Desmume throws a "ROM header is invalid" warning, and just has a white screen.

Any suggestions?
 
  • Like
Reactions: Foxi4

Foxi4

Endless Trash
OP
Global Moderator
Joined
Sep 13, 2009
Messages
30,825
Trophies
3
Location
Gaming Grotto
XP
29,840
Country
Poland
Hey, Hey, Hey! I'm the dude who messaged you recently! To me, it's still a miracle that you're still on gbatemp and even helping people out! ^^ It really means a lot, because as I've said there is little to no documentation on the internet for DS programming. I apologize if I posed the reply in the wrong section, as clicking the reply button brings me all the way to the bottom of the page and just enters the entire text of the section. I still don't know how to use this website properly, I'm new. Now, for the problem I'm facing. I followed your tutorial on sprites up to now, tried making a simple 16 x 16 256 color palette with magenta background sprite, passed it through GRIT, gave me the img and pal fie, I put them in nitrofiles and wrote this as the code:


#include <gl2d.h>
#include <nds.h>
#include <nf_lib.h>
#include <stdio.h>

int main(int argc, char **argv){
NF_Set2D(0, 0);
NF_SetRootFolder("NITROFS");

NF_InitSpriteBuffers();
NF_InitSpriteSys(0);

NF_LoadSpriteGfx("stickman", 0, 16, 16);
NF_LoadSpritePal("stickman", 0);

NF_VramSpriteGfx(0, 0, 0, false);
NF_VramSpritePal(0, 0, 0);
while(1) {
NF_SpriteOamSet(0);
swiWaitForVBlank();
oamUpdate(&oamMain);
}
return 0;
}


the result on my physical DS is just the top screen to be black, displaying nothing, and the bottom screen to be white, displaying nothing as well. Desmume throws a "ROM header is invalid" warning, and just has a white screen.

Any suggestions?
The reason why the screens have different colours for you is because you've only initialised one of them - it's NF_Set2D(Screen, Mode). You need to use this function twice, one for each screen, screen 0 and screen 1.

The reason why you can't see the sprite is because you haven't created it yet using
NF_CreateSprite(Screen, Sprite ID, Graphics slot in VRAM, Palette slot in VRAM, Position X, Position Y).

Keep in mind that you also have no background loaded on either screen, so depending on your sprite it may or may not be visible - I like to load a template background first so that I can see my loaded assets more clearly, you can replace it later.

Not entirely sure why you're throwing an exception in Desmume, perhaps because the screens are not both initialised, but you have a good starting point now. I also suggest using NO$GBA for homebrew development - it's faster, more accurate and has better debug tools. Desmume is for commercial ROM's, it's not ideal for development.

Have a closer read of the Tic-tac-toe example, it will show you everything there is to know about loading sprites into memory, spawning and destroying them.

Hope this helps. :)
 
  • Like
Reactions: JuanR4140

JuanR4140

New Member
Newbie
Joined
Mar 29, 2021
Messages
3
Trophies
0
Age
16
XP
37
Country
United States
The reason why the screens have different colours for you is because you've only initialised one of them - it's NF_Set2D(Screen, Mode). You need to use this function twice, one for each screen, screen 0 and screen 1.

The reason why you can't see the sprite is because you haven't created it yet using
NF_CreateSprite(Screen, Sprite ID, Graphics slot in VRAM, Palette slot in VRAM, Position X, Position Y).

Keep in mind that you also have no background loaded on either screen, so depending on your sprite it may or may not be visible - I like to load a template background first so that I can see my loaded assets more clearly, you can replace it later.

Not entirely sure why you're throwing an exception in Desmume, perhaps because the screens are not both initialised, but you have a good starting point now. I also suggest using NO$GBA for homebrew development - it's faster, more accurate and has better debug tools. Desmume is for commercial ROM's, it's not ideal for development.

Have a closer read of the Tic-tac-toe example, it will show you everything there is to know about loading sprites into memory, spawning and destroying them.

Hope this helps. :)

Note to self, icon.bmp is mandatory. It gave me the error on ngba as well, turns out the icon.bmp had to be present. Now, it tells me "Sprite GFX 0 not found in VRAM Error code 111" What am I supposed to input for the graphics slot and palette slot in NF_CreateSprite()?
 

Foxi4

Endless Trash
OP
Global Moderator
Joined
Sep 13, 2009
Messages
30,825
Trophies
3
Location
Gaming Grotto
XP
29,840
Country
Poland
Note to self, icon.bmp is mandatory. It gave me the error on ngba as well, turns out the icon.bmp had to be present. Now, it tells me "Sprite GFX 0 not found in VRAM Error code 111" What am I supposed to input for the graphics slot and palette slot in NF_CreateSprite()?
You have to input whichever slot you loaded the sprite into. Make sure that you use the correct name for the sprite as well, otherwise it will fail to load.

EDIT: Example, assuming you've loaded everything correctly:

NF_CreateSprite(0, 0, 0, 0, 0);

Will create a Sprite on Screen 0 with the ID number 0 using assets from VRAM loaded into slot 0 and the palette number 0 at the position of 0x0px. All the arguments are numbers, you just have to remember which one refers to what. :P
 
Last edited by Foxi4,
  • Like
Reactions: JuanR4140

JuanR4140

New Member
Newbie
Joined
Mar 29, 2021
Messages
3
Trophies
0
Age
16
XP
37
Country
United States
You have to input whichever slot you loaded the sprite into. Make sure that you use the correct name for the sprite as well, otherwise it will fail to load.

IT WORKED! (I was doing NF_CreateSprite() before loadsprite and vramsprite)

Thank you so so much. Can't believe you'd still be online answering people's questions! Displaying a simple sprite is just the beginning for me though. Thank you so much again, really means a lot! Documentation is vague and the fact you helped was incredible. Thank you!
 
  • Like
Reactions: Foxi4

Foxi4

Endless Trash
OP
Global Moderator
Joined
Sep 13, 2009
Messages
30,825
Trophies
3
Location
Gaming Grotto
XP
29,840
Country
Poland
IT WORKED! (I was doing NF_CreateSprite() before loadsprite and vramsprite)

Thank you so so much. Can't believe you'd still be online answering people's questions! Displaying a simple sprite is just the beginning for me though. Thank you so much again, really means a lot! Documentation is vague and the fact you helped was incredible. Thank you!
Cool, congrats! Have fun messing around with the DS - it's been a decade, but I still have fond memories of coding for it. Simpler days. :P I hope you'll enjoy the rest of the tutorial, just read it with a critical eye - it's been a loooong time and several forum migrations, so some of the code boxes are a tad "wonky". Double-check that what you're coding "makes sense" and is consistent with the actual tutorial, some of the examples didn't import quite right.

In terms of execution order, things are simpler to debug if you think of the program as a long block diagram, each operation being one block. As you've discovered, you can only create things you have in memory already - it's easy if you're systematic, and you'll learn over time. :)
 
Last edited by Foxi4,
  • Like
Reactions: JuanR4140

Site & Scene News

Popular threads in this forum

General chit-chat
Help Users
  • No one is chatting at the moment.
    I @ idonthave: :)