Partie 1 : Installation

Développement amateur sur Nintendo DS Partie 1

Voici la traduction du tutorial de Chris Double, sur le développement sur Nintendo DS. Cette série d'article a pour intention de décrire ma compréhension du développement sur Nintendo DS et de l'utilisation des outils de développement associés.

Equipement et Logiciels

Pour exécuter votre code sur DS, vous avez besoin de transférer l'exécutable vers la console. Il y a plusieurs moyens de le faire et le mien passe par quelque chose appelé 'WifiMe'.

Cela suppose de faire fonctionne un programme sur PC avec une carte réseau compatible PCMCIA ou PCI 802.11g network qui apparaît sur la DS comme un serveur 'DS Download Play'.

Sur la DS, vous pouvez maintenant choisir l'option 'DS Download Play' et sélectionner le fichier rendu disponible par le serveur sur le PC. Ce programme va alors s'exécuter et permettre à l'exécutable contenu sur la cartouche GBA de la DS de s'exécuter lui aussi.

Donc, pour les besoins de ce tutorial, j'utiliserais :

Vous pouvez utiliser n'importe carte réseau sans fil compatible, cartouche flash et environnement de développement mais les exemples que je vais donner sont spécifiques à mon installation.

Installation

Les recommandations ci-dessous sont pour Windows uniquement. Cependant, DevKitPRO est multi plateforme et peut fonctionner sous Linux, MacOS X et Windows donc, l'installation sur un ordinateur utilisant un autre OS est possible.

  1. Installez la carte réseau PCI, le logiciel WifiMe et le driver RT2560 driver comme présenté dans le tutorial WifiMe. Soyez certain de ne pas installer le driver pour la carte réseau. Vous devez installer le driver RT2560 spécialement développé pour la DS.
  2. Copiez le programme 'wmb.exe' dans un répertoire. Il faut avoir un sous répertoire 'data' dans ce répertoire. Dans le sous répertoire 'data', il faut avoir un sous répertoire 'wifime' contenant tous les fichiers 'WifiMe'.
  3. Installez l'environnement de développement DevKitPRO en suivant la procédure décrite à cette page. Soyez certain d'avoir configuré votre PATH et variables d'environnement comme décrit dans cette procédure.

Les variables d'environnent et le PATH que j'ai changé sur mon PC sont :

DEVKITARM=/d/devkitPRO/devkitARM
DEVKITPRO=/d/devkitPRO
PATH=d:\\devkitpro\\msys\\bin;d:\\devkitpro\\devkitarm\\bin;

Soyez certain d'avoir téléchargé la librairie 'libnds' pour DevKitPRO. Vous pouvez la décompacter dans le bon répertoire en utilisant la commande 'tar':

d:
cd \\devkitpro
mkdir libnds
cd libnds
tar -jxvf libnds-20050521.tar.bz2
tar -jxvf libnds-src-20050521.tar.bz2

DevKitPRO utilise la librairie 'libnds' alors que d'autres environnement de développement se réfèrent à 'ndslib'. Ces librairies sont en fait la même librairie. 'ndslib' es la version originale et 'libnds' est une version un peu modifiée pour fonctionner sous l'environnement DevKitPRO. Elle est normalement toujours en phase avec 'ndslib'. Dans ces tutoriaux, je vais souvent utiliser les 2 noms pour désigner la même chose.

 

Test de l'installation

Téléchargez le fichier demo1.zip. il contient l'application exemple utilisée par ce tutorial. Décompactez ce fichier dans un répertoire de votre choix, allez dans le sous répertoire 'demo1' créé et exécutez la commande 'make'.

D:\\examples\\demo1>make
arm-elf-g++ -g -Wall -O2 -mcpu=arm7tdmi ... -oarm7_main.o
arm-elf-g++ -g -mthumb-interwork ... -oarm7.elf
arm-elf-objcopy -O binary arm7.elf arm7.bin
arm-elf-g++ -g -Wall -O2 -mcpu=arm9tdmi ... -oarm9_main.o
arm-elf-g++ -g -mthumb-interwork ... -o arm9.elf
arm-elf-objcopy -O binary arm9.elf arm9.bin
ndstool -c demo1.nds -9 arm9.bin -7 arm7.bin
Nintendo DS rom tool 1.21 - Jul 12 2005 by Rafael Vuijk (aka DarkFader)
dsbuild demo1.nds -o demo1.nds.gba
dsbuild 1.21 - Jul 12 2005
using default loader

Dans le répertoire 'demo1', 2 nouveaux fichiers ont été créés :

  • demo1.nds - Ce fichier peut être exécuté sous de nombreux émulateurs Nintendo DS. Il peut aussi fonctionner sur la console si vous pouvez le télécharger sur cette dernière (Sans utiliser l'approche cartouche flash décrite ci-dessous). Malheureusement, les émulateurs DS sont encore assez jeunes et donc ce fichier peut ne pas fonctionner sur certains émulateurs.
  • demo1.nds.gba - Ce fichier fonctionne sur la console si vous le copiez sur une cartouche flash et que vous l'exécutiez en utilisant 'wifime' comme décrit plus loin. C'est approximativement le même que demo1.nds' mais du code a été ajouté pour lui permettre à la console de passer en mode 'DS' plutôt que de l'exécuter en mode GBA, qui est le mode par défaut lors du chargement de code d'une cartouche flash.

Du répertoire où vous avez extrait le fichier 'wmb.exe', exécutez la commande suivante :

f:\\wmb>wmb -data wifime

Nintendo DS - Wireless Multiboot Application - Version 1.3 BETA 4

(c) 2005 Tim Schuerewegen

Device Description = Ralink RT2560 Device
Device Hardware ID = PCI\\VEN_1814&DEV_0201&SUBSYS_700A1799&REV_01
Device Location = PCI bus 1, device 9, function 0
Driver Version = 1.0.0.7

Loading "wifime"... ok

Press [x] to abort

Sending multiboot beacons & waiting for authentication

En utilisant le logiciel fourni avec votre cartouche flash GBA, copiez le fichier 'demo1.nds.gba' dessus. Il ne doit y avoir que ce fichier sur la cartouche.

Insérez la cartouche dans la console et allumez cette dernière. Choisissez 'DS Download Play'. La DS doit trouver le téléchargement 'WifiMe' . Sélectionnez le. Le logiciel va se télécharger et ensuite l'exécutable sur la carte flash va se lancer.

Introduction au développement

Les phrases qui suivent sont la compréhension de la façon de développer sur DS par Chris Double. Il n'a pas réalisé de développement sur GBA et DS avant, donc vos commentaires/corrections sont les bienvenues (voir le bas de la page pour son adresse EMAIL).

La Nintendo DS a deux CPU's., nommés ARM7 et ARM9. Les deux peuvent exécuter des instructions en même temps. Un programme DS est habituellement constitué d'un exécutable ARM7, ARM9 et d'un certain nombre d'icônes, bitmaps et ressources. Ils sont fusionnés en un seul 'exécutable' en utilisant l'outil appelé 'ndstool'. Le fichier résultant est donc comme un fichier 'auto gérant' son système de fichiers. EN finalité, on a donc un fichier 'someprog.nds' qui peut être exécuté sur la console portable.

Afin de pouvoir utiliser la technique de boot sur cartouche flash GBA, il faut un 'loader' additionnel qui doit commencer tout d'abord par un passage en mode DS. Ceci est ajouté au fichier .nds par un outil nommé 'dsbuild'. Le résultat sera un fichier de la forme 'someprog.nds.gba' (ou quelque fois un fichier 'someprog.ds.gba', suivant l'environnement de développement que vous utilisez).

DevKitPRO possède des templates et makefiles pour simplifier le développement . Cependant, dans cet exemple, je vais lancer toutes les commandes à la main plutôt que d'utiliser un makefile pour vous monter l'utilisation des différents outils. En fait, cela rendra le makefile plus 'magique'.

Programme de démo simple

Le code ARM7

La première chose à regarder est le code du CPU ARM7. Ce que j'en comprends est que l'ARM7 est le seul CPU qui peut être utilisé pour contrôler l'écran tactile. Les exemples de la librairie 'libnds' utilisent une 'bidouille' de codage pour cela qui met en place un gestionnaire d'interruption pour la 'Vertical Blank Interrupt'. C'est une interruption qui, de façon périodique, met à jour l'affichage.

Dans le gestionnaire d'interruption, la 'technique' consiste à récupérer les paramètres de l'écran tactile et à les stocker pour que l 'ARM9 les retrouvent. Souvenez vous que seul l'ARM7 peu accéder à l'écran tactile donc il a besoin de le lire. Mais, en le stockant dans une structure interne accessible à l'ARM9, il permet à ce CPU de l'utiliser.

Voici le code de l'interruption :

void InterruptHandler(void)
{
static int heartbeat = 0;

if (IF & IRQ_VBLANK)
{
uint16 but=0, x=0, y=0, xpx=0, ypx=0, z1=0, z2=0, batt=0, aux=0;
int t1=0, t2=0;
uint32 temp=0;
uint8 ct[sizeof(IPC->curtime)];


// Mise a jour du heartbeat
heartbeat++;

// Lecture des boutons X/Y buttons et de l'entree /PENIRQ
but = XKEYS;
if (!(but & 0x40))
{
// Lecture de l'ecran tactile
x = touchRead(TSC_MEASURE_X);
y = touchRead(TSC_MEASURE_Y);
xpx = ( ((SCREEN_WIDTH -60) * x) / TOUCH_WIDTH ) - TOUCH_OFFSET_X;
ypx = ( ((SCREEN_HEIGHT-60) * y) / TOUCH_HEIGHT ) - TOUCH_OFFSET_Y;
z1 = touchRead(TSC_MEASURE_Z1);
z2 = touchRead(TSC_MEASURE_Z2);
}

batt = touchRead(TSC_MEASURE_BATTERY);
aux = touchRead(TSC_MEASURE_AUX);

// Lecture de l'heure
rtcGetTime((uint8 *)ct);
BCDToInteger((uint8 *)&(ct[1]), 7);

// Lecture de la temperature
temp = touchReadTemperature(&t1, &t2);

// Mise a jour de la structure IPC
IPC->heartbeat = heartbeat;
IPC->buttons = but;
IPC->touchX = x;
IPC->touchY = y;
IPC->touchXpx = xpx;
IPC->touchYpx = ypx;
IPC->touchZ1 = z1;
IPC->touchZ2 = z2;
IPC->battery = batt;
IPC->aux = aux;

for(u32 i=0; i<sizeof(ct); i++)
{
IPC->curtime[i] = ct[i];
}

IPC->temperature = temp;
IPC->tdiode1 = t1;
IPC->tdiode2 = t2;


// code pour le sound :)
TransferSound *snd = IPC->soundData;
IPC->soundData = 0;
if (snd)
{
for (int i=0; i<snd->count; i++)
{
s8 chan = getFreeSoundChannel();
if (chan >= 0)
{
startSound(snd->data[i].rate,
snd->data[i].data,
snd->data[i].len, chan,
snd->data[i].vol,
snd->data[i].pan,
snd->data[i].format);
}
}
}
}

// Acquitte les interruptions
IF = IF;
}

Le code ARM7 possède une fonction 'main' comme tout programme C. Il est très simple, il initialise le gestionnaire d'interruption pour que la fonction décrite ci-dessus soit appelée puis il part dans une boucle infinie en attendant cette interruption.

int main(int argc, char ** argv)
{
// Reset l'horloge si besoin
rtcReset();

// autorise le son
SOUND_CR = SCHANNEL_ENABLE | SOUND_VOL(0x7F);
IPC->soundData = 0;

// Init du gestionnaire d'interruption
IME = 0;
IRQ_HANDLER = &InterruptHandler;
IE = IRQ_VBLANK;
IF = ~0;
DISP_SR = DISP_VBLANK_IRQ;
IME = 1;

// Laisse l'ARM7 en dehors de la RAM
while (1)
swiWaitForVBlank();
return 0;
}

J'ai mis ce code dans un fichier 'arm7_main.cpp'. C'est en fait une copie conforme du code des exemples de la 'libnds'. Le compilateur g++ de l'ARM7 est invoqué pour produire un fichier 'arm7.o', contenant le code compilé. Le grand nombre d'options du compilateur g++ sélectionne le type de code à générer pour l'ARM7, les répertoires des librairies à inclure et le fichier à produire en sortie.

arm-elf-g++ -g -Wall -O2 -mcpu=arm7tdmi -mtune=arm7tdmi \\
-fomit-frame-pointer -ffast-math -mthumb-interwork \\
-Id:\\devkitpro\\ndslib\\include -DARM7 -c arm7_main.cpp -oarm7_main.o

Maintenant que nous avons le fichier 'arm7_main.o', nous devons le linker avec la librairie standard ARM7 de la DS livrée avec 'libnds'. C'est la même chose que linker un programme C normal avec la librairie C standard. Cette étape produit un fichier au format 'elf' qui est convertit en binaire ARM7 pour la DS par la commande 'objcopy':

arm-elf-g++ -g -mthumb-interwork -mno-fpu  \\
-specs=ds_arm7.specs arm7_main.o -Ld:\\devkitpro\\ndslib\\lib -lnds7 -oarm7.elf

arm-elf-objcopy -O binary arm7.elf arm7.bin

Le code ARM9

Le code ARM9 va réaliser tout le travail dans cet exemple. Il va afficher un petit message ainsi que les coordonnées X et Y obtenues de l'écran tactile. Rapellez vous que c'est le code ARM7 qui obtient ces informations et les stocke dans une structure de données que nous pouvons accéder depuis l'ARM9.

Ce code est basé sur l'exemple hello_world de la librairie 'nsdlib' avec quelques changements mineurs. J'ai seulement documenté ce que j'ai compris, ce qui ne représente pas grand chose ! Un grand merci aux développeurs de libnds, ndsdev, wifime et autres utilitaires qui m'ont permit d'en arriver là.

Le code de l'ARM9 est une simple fonction 'main'. Tout d'abord, le mode vidéo est initialisé pour utilisé un des écran de la Nintendo DS. Dans notre cas, l'écran 'principal'. Nous initialisons aussi d'autres choses comme comment afficher le texte, la couleur de la font, etc :

powerON(POWER_ALL);
// Utilisation de l'ecran principal pour l'affichage
videoSetMode(MODE_0_2D | DISPLAY_BG0_ACTIVE);
vramSetBankA(VRAM_A_MAIN_BG);
BG0_CR = BG_MAP_BASE(31);

// Met la couleur dela font a blanc.
BG_PALETTE[255] = RGB15(31,31,31);

// Swap des ecrans pour que l'ecran principal soit l'ecran du bas.
lcdSwap();

A partir d'ici, le texte est affiché à l'écran et on entre dans une boucle pour afficher les données de l'écran tactile :

consoleInitDefault((u16*)SCREEN_BASE_BLOCK(31), (u16*)CHAR_BASE_BLOCK(0), 16);
consolePrintf("\\n\\n\\tHello World!\\n");
while(1)
{
consolePrintSet(0,10);
consolePrintf("Touch x = %d \\n", IPC->touchXpx);
consolePrintf("Touch y = %d \\n", IPC->touchYpx);
}

Vous devez porter attention aux IPC->touchX et IPC->touchY, membres de la structure initialisée dans l'ARM7 avec les valeurs de l'écran tactile. Ce code, dans 'arm9_main.cpp' est compilé de la même manière que le code ARM7 avec uniquement le changement d'options de compilation code pour la génération du code ARM9 :

arm-elf-g++ -g -Wall -O2 -mcpu=arm9tdmi -mtune=arm9tdmi \\
-fomit-frame-pointer -ffast-math -mthumb-interwork \\
-Id:\\devkitpro\\ndslib\\include -DARM9 -c arm9_main.cpp -oarm9_main.o

arm-elf-g++ -g -mthumb-interwork -mno-fpu \\
-specs=ds_arm9.specs arm9_main.o -Ld:\\devkitpro\\ndslib\\lib -lnds9 -o arm9.elf

arm-elf-objcopy -O binary arm9.elf arm9.bin

Création de l'exécutable pour la DS

Maintenant que nous avons les fichiers au format .bin pour l'ARM7 et l'ARM9, nous avons besoin de créer l'exécutable qui est le 'mini système de fichiers' contenant ces fichiers, les images, icônes, ressources, etc. Cet outil est 'ndstool':

ndstool -c demo1.nds -9 arm9.bin -7 arm7.bin

La ligne de commande 'ndstool' décrite ci-dessus va produire un fichier , 'demo1.nds', qui peut être exécuter sur hardware. Il va contenir le fichier 'arm9.bin' que nous avons compilé plus tôt pour le CPU ARM9 et le fichier 'arm7.bin' pour le fonctionnement du CPU ARM7.

La méthode que nous utilisons pour exécuter le code nous oblige à utiliser un 'loader' à ajouter au code qui s'exécute en mode GBA, puis permute la DS vers l'ARM9 pour lancer le code ARM9. L'outil utilisé pour réaliser cette fonction se nomme 'dsbuild':

dsbuild demo1.nds -o demo1.nds.gba

Cela va générer le fichier 'demo1.nds.gba'. Copiez ce fichier sur la cartouche flash GBA et utilisez WifiMe de la même manière que décrite dans la section Test de l'installation.

Vous pouvez aussi exécuter le fichier 'demo1.nds' avec n'importe quel émulateur DS. Une copie d'écran de l'application en cours d'exécution sur émulateur se trouve ici. Le code source complet est disponible dans demo1.zip et vous pouvez télécharger les version compilées demo1.nds et demo1.nds.gba pour les tester sur émulateur et console respectivement.

Plutôt que d'exécuter les commandes séparément pour produire le programme, il est plus facile d'utiliser un Makefile. Un Makefile liste les dépendances entre chaque fichier et les commands à exécuter pour produire le fichier exécutable voulu.

DevKitPRO est livré avec des templates de Makefile pour fabriquer des programmes moyennement complexes avec le minimum de travail. Je n'ai pas encore toutes les billes pour produire des makefiles fabriquant plusieurs cibles (binaires ARM7 et ARM9 personnalisés) donc j'ai écris un makefile très simple qui exécute les commandes citées et qui doit être à peu prés lisible et multi plateformes.. Le makefile est ici et exécuter la commande 'make' dans le sous répertoire 'demo1' doit fabriquer tous les fichiers voulus.

Conclusion

La Nintendo DS est une très bonne console pour le développement amateur. Elle possède 2 écrans, dont un est tactile, un micro, un hardware 3D, et le Wifi. Je vais certainement approfondir mes exemples dans les prochains articles. Tout commentaire ou suggestion sont les bienvenus. Voir mon adresse EMAIL ci-dessous.

Les mises à jour de ce tutorial peuvent être obtenues sur le weblog de l'auteur (Chris Double) : http://radio.weblogs.com/0102385. Il peut être contacté à l'adresse chris.double@double.co.nz

Copyright (c) 2005, Chris Double. Tous droits réservés. Traduction par AlekMaul (alekmaul@portabledev.com).