Tuesday, September 29, 2009

IRQ_VCOUNT

Voilà exactement le genre de technique de programmation que j'ai toujours admiré sur les consoles (et les ordinateurs Commodore, qui d'une certaine façon, sont des consoles avec un clavier et un lecteur de disquette). Mettons que je veuille modifier au milieu d'une image les paramètres de l'affichage, comme la palette de couleur, la priorité et la position de tel ou tel plan du jeu, etc.

Pour un jeu vidéo, ça me permettrait par exemple de garder une zone de score fixe dans un jeu à scrolling, ou (*ze* cas d'école) de virer la palette dans les bleus à la hauteur du plan d'eau. Dans mon éditeur de niveau, ça permettrait aux boutons de rester affichés correctement malgré les choix d'afficher ou de masquer les différents "calques".

Bien sûr, ça peut être approché aussi sur les cartes VGA etc. à condition de bien synchroniser le timer avec les débuts et fin de ligne à l'écran. Et c'est là toute l'élégance de la "solution console": il y a une ligne d'interruption dédiée directement à cette synchronisation (donc plus besoin de timers), et dans le cas de la DS, on a carrément le luxe d'un registre supplémentaire qui indique si la ligne courante correspond ou non à une ligne indiquée.

void vcountirq() {

if (REG_VCOUNT<192) {
BG_PALETTE[0]=RGB15(0,0,31);
REG_DISPSTAT = (REG_DISPSTAT&0xff)|(192<<8);
} else {
BG_PALETTE[0]=RGB15(0,0,0);
REG_DISPSTAT = (REG_DISPSTAT&0xff)|(160<<8);
}
The 'vertical count matched' interrupt is a perfect explanation of why I prefer underground console programming against overworld PC games. It is a very simple mechanism that lets code be run when the graphic controller is about to process line Y, and yet it enables a wide panel of special effects in games. The most common "showcase" is for sure implementing water: at a given horizontal position, you swap the palette and provide a "bluer" one so that bottom of the screen looks sunken.
The above code snippet does just that in a "raw" fashion where only backdrop color is changed. Don't let yourself scared by those bit masking, combining and shifting. It is just another facet of computing that is *very* handy when dealing with hardware registers. If you think of your process as an "electronic process" taking place on an array of bits, it just becomes natural.

Une petite fonction toute simple donc, qui sera appelée "sur la bonne ligne" et qui change les couleurs (ici, une seule, et entre deux valeurs pré-calculées, mais c'est juste pour la démo). Astuce, pour pouvoir être invoquée sur plusieurs lignes (plutôt que toujours sur la même) et donc remettre la couleur à sa valeur précédente, je change le numéro de la ligne où l'interruption doit se produire pour lui donner la valeur suivante dans l'interruption elle même (c'est le code avec REG_DISPSTAT).

Bon, okay, le code avec les & et les << fait un peu peur: c'est des manipulations de portions d'entiers pour modifier un byte qui se trouve en réalité dans la moitié d'un registre de 16 bits. La programmation DS est remplie de bidouille de ce genre. C'est de l'informatique d'avant XML, qui exploite pleinement la structure binaire des nombres.

Bien sûr, pour que ça fonctionne, je dois enregistrer la fonction vcountirq via la libnds (il ne suffit pas de lui donner le bon nom pour que ça se fasse tout seul. Vous rêvez, les gars!)
irqSet(IRQ_VCOUNT, vcountirq);

REG_DISPSTAT = REG_DISPSTAT|(160<<8);
irqEnable(IRQ_VCOUNT);
Vous remarquerez qu'on retrouve ici aussi les "manipulations bizarres" sur DISPSTAT, pour l'initialisation. Heureusement, le web est plein de gens qui passent leur temps à expliquer "X&FF = garder les 8 bits de poids faibles de X", etc.

Et pour désactiver l'effet,
irqDisable(IRQ_VCOUNT);
tout simplement.
J'aurais pu vous bricoler tout ça juste pour le fun ou parce que je prépare en secret un nouveau niveau de Bilou avec de l'eau. Malheureusement, je n'en suis pas encore là. Je suis dans une impasse avec mon éditeur de niveau, tout simplement: les boutons qui me permettent de décider si je veux voir ou non le fond, le plan principal, les sprites, etc. disparaissent ou deviennent illisibles par suite des manipulations pour changer la visibilité des plans. Résultat, rien de tel qu'un bon "virq" pour forcer l'affichage à "revenir" dans le bon état le temps de m'afficher la barre d'outils qui doit se trouver en bas de l'écran tactile...

Well, i wish i was posting this message because i'm working on an impressive water level. Not yet. I'm simply stuck with the UI of my level editor, where i need more layers than the console actually provide, so I'm doing a dynamic "horizontal split" when the user press "down" to access layer visibility setup. The hardest part is to integrate this properly in the GuiEngine (as usual with OOP >_<)

... Reste à intégrer ça dans le GuiEngine ...

edit: i managed to integrate and test on real hardware. It works, despite the amount of time taken by the operations confirms just "dma'ing" a new palette wouldn't work. The actual code for my 'vcount' handler is only 8 lines long (50 instructions, involving 8 stores to video registers), but it takes two scanlines to be fully processed. Plus, the first scanline shows a "flickering" area of some 24 pixels wide, suggesting that even the "companion code" that brings us from the interrupt to the "virtual void online" snippet is also too long. Maybe pushing it to ITCM would help ... and having the interrupt taking place one line in advance and forcing a busy loop until we encounter a HBLANK.

update: Pour la petite info, c'est maintenant intégré et testé sur la DS elle-même. Et ça marche, même si je suis surpris de voir 8 modifications de registres (les données sont constantes: ça se voit dans l'assembleur généré par le compilo) prendre quand même deux lignes d'affichage pour s'exécuter ! Je devrais peut-être essayer de mettre les routines concernées en ITCM, histoire de m'assurer qu'elles soient toujours en cache, ou qqch comme ça (sur du C++, ça va être coton, tiens!).

Thursday, September 24, 2009

Sur GBA

On est en janvier 2001. Le GBA commence tout doucement à prendre de l'ampleur et je rencontre dans le bus un gars qui s'est acheté une "cartouche programmable" pour y mettre plus de jeu. Je me renseigne un peu, entre 2 release de Clicker, et je trouve pas mal de documentation technique confuse, mais alléchantes, sur les capacités de la bête.

Ma petite BD de Bilou a le vent en poupe. Je fais 2 à 3 "mini-strips" par semaine, et je commence à envisager la possibilité de réimporter tous ces petits personnages dans un jeu ... sur GBA, justement. L'occasion de donner un coup de jeune aux mouvements possibles. La version BASIC était assez simpliste de ce point de vue-là : un bouton (ENTER) pour passer au pouvoir magique suivant, et un autre (SPACE) pour utiliser le pouvoir sélectionné. [L] et [R] pourrait bien jouer ce rôle ici, mais après quelques heures passées sur Megaman X, je me mets à chercher quelque-chose qui n'interromperait pas l'action à tout bout de champ.

A.D. 2001, I discover the Game Boy Advance and meet in a bus a guy who bought a strange cartridge with some 256MB of flash memory that can host games you upload from a PC. I dig the hardware specs that seems very interesting though still incompletely understood. By the same time, my little Bilou comic project is gaining speed, with 2 to 3 strips a week, and i'm starting to dream of re-importing those characters into a newer version of the video game ... on the GBA.

Finalement, la réponse de Nintendo concernant ma demande d'informations supplémentaire sur le hardware son de la GBA et la perspective de devoir gérer le mode thumb et les copies entre ROM et RAM de quelques Ko auront raison de l'aventure avant même que je n'aie installé une suite GCC pour ARM (que mon frère s'était quand-même empressé de télécharger chez Wan, avec tous les tutos disponibles à l'époque! Il y croyait dur comme fer!)

These drawings are a testimony of an attempt to re-think the controls of the game. After each level, Bilou recovers one of the "magic stones" that increases its abilities. Here, i tried to stick to two buttons and avoid interupting the action, while the BASIC version had a "select power" button (ENTER) and a "use power" button.

Despite my brother's efforts to make me start working on it, Nintendo's reply (we were asking for detailed information about how the sound worked on the device) and some awkward aspects of GBA programming (co-existence of thumb and normal instruction set and the need to manually move data between ROM and a fairly small SRAM area) burried down the project. Only the Nintendo DS that can potentially serve both as the game platform and the development platform revived the dream of a Bilou game on a true gaming console.


Tout ça parce que dans son commentaire sur "Summer Flight", mon frère mentionnait que le mod avait été remixé plus tard pour la version GBA de Bilou (qui, je le rappelle, ... ok, je sors)

Monday, September 21, 2009

curseur de copie pour LEDS


There isn't much to show (yet) about the new modifications of LEDS and its (disappeared) user interface. I'm still under the process of revising the GuiEngine's internals, adding a "transient window" flag here, a GUI_ANYKEY event there, analysing the codeflow so that i can intercept keystroke in the proper widget, etc.
But altogether, it slowly brings a revised level editor with "improved" features. The most interesting one being the (so) long awaited "block copy" mode. Note that it isn't quite the kind of "block copy" you might expect, but rather an adaptation of the "copy cursor" from CPC basic that Pierrick loved so much: you L-click on a tile from an object you want to copy, and then you click and draw from another location to reproduce the object at the source. Pretty useful.


Je progresse sur l'éditeur de niveau, même s'il n'y a pas encore grand-chose à montrer. C'est l'inconvénient de l'approche "interface utilisateur absente" où j'utilise au maximum les boutons de la DS comme contrôles. Pourtant, en interne, ça se transforme pas mal dans le code du GuiEngine, notamment pour introduire les fenêtres "temporaires" capables de disparaître dès qu'on relâche la touche qui les a invoquées, etc.
Mais la nouveauté de ce soir, c'est la copie de blocs sur la map, inspirée largement du "curseur de copie" du CPC qui plaisait tant à Pierrick.

vous voulez essayer ? téléchargez le petit 'pack' combinant la version actuelle de l'éditeur, le niveau 1 de Bilou et ses tiles tous prêts au travail...

  • L+dpad = move around the map
  • "draw mode" : L+click = grab tile, click = paint tile, dpad = select another block from the tileset
  • "copy mode" : L+click = define the source, click & move = copy from the initial click position the source image
  • select = toggle between bg and fg edition
  • B = toggle blocksize (8x8 or 16x16)
  • L = toggle between "draw mode" and "copy mode"
This is a debugging demo, so (unfortunately), you can't save your edits right now and you cannot edit anything else but the embedded map with the embedded tiles. A fully-featured application with external files selection from the map is of course planned as soon as those widgets update are done.

todo: allow tiletable as source for copies.

Tuesday, September 15, 2009

Révision du LevelEditor

C'est reparti, donc, pour un tour sur l'éditeur de niveaux. Et ça risque de prendre un moment, compte tenu du fait que je ne me souvenais même plus de l'architecture que j'avais choisie pour les classes "*Window". Avant de m'attaquer aux gros morceaux comme "copier-coller de blocs" ou "rogner la carte", je vais déjà un peu rationaliser l'espace de travail, histoire d'éviter qu'il soit bêtement saturé.

Pour ça, je compte sur le bouton "R" pour passer du mode "édition précise" au mode "brouillon" et sur un nouveau système de "pull down window", c.a.d. des fenêtres qui deviennent actives (et visible sur l'écran tactile) tant que le Dpad est maintenu dans une direction donnée. Ceux qui ont joué à Zelda: Phantom Hourglass voient de quoi je parle.

I'm resuming work on the level editor. It's been so long that i need to draw UML diagrams again to figure out the relationship between the classes, and so on. As you can see on the figure, there are quite a lot of things i want to alter on the UI, most of them relying on a newly introduced trick to have "pull" windows (that is, windows that only remain active on the touch screen as long as you hold the DPAD in the direction that invoked them.

  • [done] clean-up user-interaction
  • [done] layer visibility, "grid magnetism", etc.
  • [done] block-copy mode
  • [wish] pattern-fill mode
  • [todo] map cropping
  • [todo] tile-swapping tool.
  • [need] fix that dprint() macro !
  • [done] .cmd parsing and monsters edition
  • [wish] radar for moving around
  • [done] status report out of the playground.

Debugging sur DS

En règle générale, j'aime encore bien le débugging "à la dure" à grand coup de désassembleur et autres guru meditation, mais de temps en temps, ça ne suffit plus. Je ne suis pas contre le débugging "tea-time" avec une bonne tisane, un fluo et quelques listings, ceci dit ...

I usually enjoy the "guru meditation" kind of debugging, analyzing machine code and registers dump, with a good cup of tea, listings and a "swing" fluo pen. Yet, this time it wasn't enough, so i reused the kind of techniques i've mastered toying with my network processor, a few years ago. Since there are tons of tutos on using GDB in English, I'll focus on trying to give you the feeling of what's going on.
Mais cette fois-ci, c'est le module player NTXM qui tourne sur l'ARM7 qui posait problème, et sur l'ARM7, pas de moyen facile de sortir des messages d'erreur. J'avais bien bricolé un petit mécanisme de "rapport d'erreur" pour libntxm, qui restait désespérément muet. Bref, c'est un peu par chance que j'ai découvert que la version 0.7.3 de desmume (sous Linux, en tout cas) intègre un double "stub GDB". Du coup, moi:

desmume-cli gedsdemo.nds --arm7gdb=6667 --arm9gdb=6669
ln -s `which arm-eabi-gdb` gdb
ddd runme/arm7/runme.arm7.elf &
ddd gedsdemo.elf &
Ca mérite un mot d'explication. GDB est le débugguer intégré au compilateur gcc et g++ dans les systèmes Linux, les distributions cygwin, et le devkitpro. Dans la plupart des cas, on appellera directement le débuggueur avec le programme à corriger sur le système-cible, mais pour le développement embarqué, on sait rarement faire tourner le débuggueur complet sur le système-cible. Dans ce cas, on fait tourner une version minimale du debuggueur (le stub) avec le programme, sur le système-cible (ici, l'émulateur DS) et le front-end peut tourner sur un autre système (le PC linux, en l'occurence). Desmume intègre deux 'stubs', un pour chaque processeur de la DS, et on donne au démarrage le numéro de port à utiliser pour chacun d'eux.

desmume-cli gedsdemo.nds --arm7gdb=6667 --arm9gdb=6669
Gdb is the natural companion debugger in the GNU gcc/g++ compiler suite. It is a command-line debugger with a fairly crude (compared e.g. to Borland's debugger), but very complete and powerful interface. It is naturally available in the devkitpro toolchain as arm-eabi-gdb binary where it can disassemble and understand ARM7 and ARM9 machine code as long as they are in .ELF binaries. To get a fancy environment that lets me browse source and data structure, i use DDD (data display debugger) that acts as a front-end for a gdb session that it will launch and completely manage for you. And as crème de la crème, DDD also provides you a direct interface through the GDB session by means of a console at the bottom of its window, so if some magic button is missing for you to use the gdb tricks you know, just type it and you'll get it.
Ensuite, on démarre le débuggueur en lui donnant le fichier ELF à manipuler. Personnellement, j'utilise ddd (Data Display Debugger), qui permet l'affichage des données en graphe et le suivi de l'exécution dans les sources, en plus de toutes les fonctionnalités de gdb (breakpoints, inspection de la pile et des registres, etc.)
L'astuce est d'expliquer à ddd d'utiliser la version de gdb pour processeur ARM du devkitpro plutôt que la version pour intel propre à ma machine. Il suffit pour ça de créer un lien symbolique du nom de "gdb" pointant vers arm-eabi-gdb dans un des répertoires couverts par la variable $PATH. Dans mon cas, le répertoire courant fait l'affaire.

Reste à expliquer à chacun des gdb qu'il doit s'attacher à un stub existant plutôt que d'essayer de faire tourner lui-même le programme (par la commande run, qu'on aurait utilisé dans une session de débugging classique). Pour ça, j'utilise

target remote localhost:6667
dans la fenêtre qui fait tourner le code ARM7 et

target remote localhost:6669
dans la fenêtre qui fait tourner le code ARM9. Histoire de pouvoir suivre l'exécution des programmes, j'ai donné un "break main" dans chaque fenêtre, histoire d'interrompre le programme lors du début de son exécution en C.

Now, the system you want to debug is different from the system that runs the debugger. You may not be used to it, but remember that it is an über-common situation for people doing cross-compiling, kernel development and embedded systems. Debugging your NDS program with DDD and Desmume emulator just happens to be a specific instance of this problem. The debugged system will have to provide a (lightweight) stub for gdb, that is, a piece of code that blindly grab memory content, dumps registers, enforce breakpoints and stuff. On your debugging system, gdb has the guts to attach to this stub through the "target remote " command. Once attached, it all happens as if your debugged system had an extra process running gdb through a window of your desktop. Enjoy conditional breakpoints, data structure browsing and all ^_^.

Le reste dépend évidemment du problème qu'on cherche à résoudre. Ici, c'était une histoire de communication entre ARM7 et ARM9 (du moins, c'est ce que je croyais). Un breakpoint côté ARM9 sur l'envoi de commandes m'apprend que cette partie-là s'exécute bien sans problème, par contre mon breakpoint sur "calcNextPos" dans le module player n'est jamais appelé. Je passe en revue l'initialisation de l'ARM7 pas à pas et je me rends compte qu'il bloque au niveau de l'initialisation du Wifi ... logique, le code ARM9 pour la démo n'utilisait pas le Wifi. Faute d'un "ppp-nowifi.arm7", j'ajoute un "new Wifi(...); Wifi_Disable()" dans le code de ma démo pour pouvoir faire ma release "dans les temps" (avant le 9/9/2009 :)

PS: pour ceux que ça intéresse, je précise que QEMU (émulateur PC) intègre également un stub gdb, et que pour les autres programmes (p.ex. mes outils sur network processor), il suffit d'utiliser gdbserver qui supporte aussi bien les connexions TCP que le debugging sur cable null-modem.

Ca marche tellement bien que pour les tests que je veux faire sur mon LevelEditor, je vais carrément construire une "version light" (ledsdemo) qui tourne sous desmume et développer les nouveaux widgets, etc. dans cet environnement-là. Je le sens bien.

To conclude, 0xtob's library wasn't to blame if i got no sound: i made a slight mistake in my ARM7 initialisation code that prevented the module player from ever executing. Oh, and btw, yes, I am aware that a GDB stub running on bare DS hardware and accepting connections through WiFi is available. It just haven't been needed so far.

Just one last trick that is handy: ddd comes with tons of options to select the "inferior debugger", but i just prefer symlinking arm-eabi-gdb so that it is found under the name "gdb" when the $PATH is scanned, looking for the command to be run. Aliases won't work here.

Wednesday, September 09, 2009

Un Bilou tout neuf.

Wait a minute. Look at the date: the guy who posted a news on your forum hasn't dug this blog properly. You might be looking for a more recent demo

Level 2 is merely there to demonstrate we can now have multiple levels.
Voilà. Je vous livre mon travail de vacances: la forêt mystérieuse de Bilou, revue et corrigée. C'est une démo jouable de mon moteur de plate-forme toujours en cours de développement, où vous incarnez Bilou (la balle bleue) et récoltez les pommes pour progresser dans les niveaux. Nouveautés dans la release 0.999:

En cours de jeu, sautez avec X ou Y et dirrigez le personnage (la boule bleue, faut-il le répéter) avec le Dpad. Start passe le jeu en mode pause debugging, L+START revient au mode normal.

The sign points to the left: you haven't collected enough apples to access the second half of the level.
Here comes at last the new playable demonstration of my game engine on the Nintendo DS and a two-level game featuring Bilou, my blue and round mascot. Dodge angry applemans, avoid getting stomped by the funky funghis and collect enough apples to pass through the tree-with-a-sign (see picture on the left). You move Bilou (the blue hero) with the Dpad and jump with X or Y. Only yellow Woodworms can be stomped for now. In this new release:
  • sound & music by Pierrick H thanks to 0xtob's libntxm,
  • ease-of-use thanks to Noda's EFS
  • new monsters
  • improved controls
  • one extra (draft) level
  • 5 hitpoints per lives and unlimited lives
  • a few hidden places you needn't to find
  • some more gfx
  • parallax scrolling
yet, this is still work in progress. your comments are welcome on any aspects.

3, 2, 1 ... Release !


  • corriger la map du niveau 1
  • corriger la map du niveau 2
  • faire la vaisselle
  • dessiner une sortie pour le niveau 1
  • ajouter le haut des arbres
  • arranger le scrolling
  • bercer *deline
  • faire tourner la musique
  • empaqueter le tout.
  • uploader sur SourceForge
  • annoncer sur dev-fr