et maintenant, la vue et le toucher !

Maintenant que le coeur bat, tel Frankeinstein dans son antre, nous allons rajouter la vue et le toucher à notre émulateur, il vivra bientôt, je vous le dis ^^.

Et donc, comment dessine-t-il les Mickeys, notre Chip 8 ?

La résolution graphique de notre Chip8 est de 64×32 pixels, et il possède 1 couleur :).
La particularité de ce dernier est de ne tracer les graphiques que par sprite. C'est à dire par objet animé de 8 pixels de long et de 1 à 15 pixels de haut.
Si un pixel d'un sprite est en contact à un pixel de l'écran, il le mange et annule ce dernier ... Le registre VF est positionné à ce moment là pour indiquer qu'il y a eu collision entre notre sprite et l'écran.
L'origine de l'écran se trouve en haut à gauche. Toutes les coordonnées sont positives, commencent à 0 et sont calculées modulo 64 pour l'axe des X et modulo 32 pour l'axe des Y lors de l'affichage des sprites.
Aussi, le Chip 8 possède une police de caractère héxadécimale de 4 x 5 pixels. Les caractères sont donc (si vous avez bien suivi le cours précédents) 0-9 et A-F.
Cette police de caractère est stockée à l'adresse 0000h et les points sont, ce que l'on appelle, 'bit coded'.
Qui dit 'bit', dit représentation binaire, un point allumé vaut 1 et un point éteint vaut 0. Comme chaque caractère fait 4 points de long, on stockera juste les 4 bits dans chaque octet.
Ainsi, notre police de 16 caractères x 5 lignes de 1 octet de long (4 bits mais étendu à 8 bits, vous suivez ;)) prendra 16*5 octets !

Ok pour le blabla, mais concrétement pour Hal8000 ?

Vous vous souvenez que nous avons déclaré un tableau pour gérer les pixels de notre écran, c'est donc lui qui va être mis à jour lors de l'exécution de l'émulateur.
Ensuite, on a parlé de la police de caractère stockée sur 8*5 octets, nous la mettrons directement à l'adresse mémoire 0000h, sans la stocker dans des variables.
Voici donc le début de la déclaration de cette police, tout le reste est du même acabit, vous pouvez même changer la forme des lettres si vous le souhaitez ^^.

 
Ensuite, reprenons les instructions laissées en plan la dernière fois pour les compléter.
La première, "Fr29 Font vr" Pointe I vers le sprite de caractère hexadécimal contenu dans vr devient donc :
 
Car nous devont avoir le caractère contenu dans opcode2 et comme chaque caractère faire 5 lignes de haut et commence à l'adresse 0000h, le premier sera donc à 0000h+0*5, le second à 0000h+1*5, etc jusqu'au 16ème en 0000h+15*5.

La seconde, "Drys Sprite vr,vy,s" Affiche le sprite en position vr,vy , de hauteur s devient alors :
 
 
Cette fonction, plus compliquée demande quelques explications ... Tout d'abord, quelques opérateurs C bien utiles à connaitre :
>> pour le décalage logique à droite et << pour la même chose mais sur la gauche.
Ces 2 opérateurs permettent de faire ce que l'on appelle une rotation de bits, ainsi var >> 3 va décaler 3 fois les bits contenus dans var vers la droite.
Posons l'exemple : si var = 43, cela fait donc 00101011 en binaire (sur 8 bits) et donc, on décale 3 fois les bits vers la droite.
00101011 -> 00010101 1er décalage ...
00010101 -> 00001010 2nd décalage ...
00001010 -> 00000101 3me décalage ...
:)
On passe donc de 43 à 5 via cette méthode. Pourquoi toutes ces explications me direz vous :D ?
Et bien pour l'instruction (0x80 >> uCol) présentée dans notre fonction !!! Qui permet de donc de faire basculer le nombre 10000000 vers 00000001 et de tester toutes les combinaisons pour nos 8 pixels :)
Ingénieux non ? Attention à ne pas oublier les parenthèses, cet opérateur est assez allergique s'il n'en possède pas autours de l'opération à décaler.
L'opérateur % est le modulo en C , utilisé pour notre balayage de la mémoire graphique (on ne dépasse ainsi pas les 64 pixels x 32 pixels).
Enfin l'opérateur ^ est l'opérateur OU Exclusif qui met le bit à 1 si il est à 0 et le met à 0 s'il est à 1 en prenant comme paramètre 1 bien entendu. C'est bien ce que nous voulons faire pour nos pixels non, d'où le ^=1 :) !
Ainsi, notre fonction se résume donc à balayer toutes les lignes et pour chaque ligne, tous les pixels de la ligne. Elle sera donc assez lente mais le but de notre cours est de comprendre et non pas d'optimiser tout de suite ^^.

C'est bien, mais j'affiche comment sur mon écran le résultat ?

Oui, continuons ^^
Maintenant, nous savons que notre tableau Chip8_Display contient des valeurs 0 ou 1 pour afficher ou pas un pixel.
Comme l'écran de la DS, en mode graphique est de 256 x 192 pixels et que celui du Chip 8 de 64 x 32 pixels, nous allons faire en sorte qu'un pixel du chip 8 devient 4x4 pixels sur la DS.
Ainsi nous aurons (64*4) x (32*4) soit 256 * 128 pixels utilisés. Pour la largeur, c'est donc ok, puisque l'on atteint 256 pixels, pour la hauteur, on va commencer à (192-128)/2 soit à la 32 ème ligne pour être bien centré sur l'écran.
Pour ne pas s'ennuyer avec les pbs graphiques de la DS, nous allons utilisé le mode graphique 16 bits, permettant d'afficher un pixel à chaque fois via la macro RGB15(r,g,b) suivant la couleur de ce pixel.
Aussi, RGB15(31,31,31) correspond à un pixel tout blanc et RGB15(0,0,0) à un pixel tout noir :). Attention, dans ce mode, il faut aussi positionner le dernier bit de la couleur pour la rendre visible, d'où le | 0x8000 dans les déclarations de cette dernière.
Commençons donc par l'initialisation du mode graphique, que nous mettrons dans un fichier nommé hal8000generic.c, car ces fonctions seront génériques et ne doivent pas polluer les autres fichiers, plus spécifique au cpu ou au programme principal.


Ensuite, nous déclarons une variable qui va être utilisé pour "pointer" vers la mémoire vidéo et ainsi dessiner sur cette dernière.
 
 
Et enfin, notre routine de mise à jour de l'écran, qui balaye donc les 32 lignes et 64 colonnes et affiche les pixels si besoin est (par gros patés de 4x4 sur la DS :D !).


C'est bien sympa tout cela, je peux toucher :P ?

Oui, on va donc continuer par expliquer l'agencement des touches sur notre Chip8.
Ce dernier possède donc un clavier un peu particulier car il est ... hexadécimal ^^. Il possède donc 16 touches de 0 à 9 puis A à F (cf notre leçon précédente pour la représentation hexadécimale).
Il ressemble à celui présenté ci dessous, on remarquera que les touches 4,6, 2 et 8 sont souvent utilisées pour les déplacements dans toutes les directions.

+---+---+---+---+
| 1 | 2 | 3 | C |
+---+---+---+---+
| 4 | 5 | 6 | D |
+---+---+---+---+
| 7 | 8 | 9 | E |
+---+---+---+---+
| A | 0 | B | F |
+---+---+---+---+

Nous utiliserons donc un tableau, nommé Chip8_Tches, qui va contenir 16 emplacements suivant ces 16 touches. 1 voudra dire que la touche est appuyée et 0 le contraire.
 

3 opcodes sont utilisées pour la gestion des touches, une pour savoir si une touche est enfoncée, une autre pour savoir si elle est relachée et la dernière qui attend qu'une touche soit utilisée.
Voici le code de ces 3 opcodes, laissé vide dans notre dernier cours :

Le premier teste la touche enfoncée
 
 
Le second la touche relachée
 

Et le troisième attend qu'une touche soit enfoncée. Cette dernière est un peu particulière car elle revient sur cette instruction (via le Chip8CPU.reg_PC -= 2) tant qu'aucune touche n'a été appuyée (puisqu'elle attend une touche appuyée on vous dit ^^ !).
 
 
Enfin, voici donc la gestion de nos touches. Elle est décomposée en 2 versions, au début, on regarde via les touches et ensuite via le stylet et notre beau clavier dessiné sur l'écran du bas.
Cette première partie met d'abord toutes les touches à "non appuyée" et ensuite teste les touches de la DS et fait correspondre une touche du Chip8 (on a essayé de faire intelligent avec les Haut, Bas, Gauche, Droite comme sur le clavier présenté ci-dessus).
 
 
Cette seconde partie regarde si le stylet est utilisé et met la touche correspondante à 1.
 

Il va de soit qu'il est impossible d'avoir plusieures touches appuyées via le stylet mais cela n'est pas grave pour cet émulateur.

Un peu de cosmétique aussi avec l'affichage de notre superbe clavier sur l'écran du bas :)
 
 
et bien entendu l'appel à la gestion des touches et de l'affichage dans la boucle d'exécution de notre programme
 

Bien ! on peut tester avec un programme ?

Oui, on commence par contre par gérer les timers de son et de délai dans la boucle de notre chip8, sinon, ils ne seront jamais changé, ce qui n'est pas sympa pour un timer :) ...
 
 
Enfin, nous allons donc maintenant ajouter la lecture d'un fichier. Ce fichier est chargé à l'adresse 200h et fait au maximum la capacite de la mémoire (soit 1000h-200h).
Nous allons donc créer cette fonction de lecture, qui est ma foi toute simple et ne demande pas plus d'explication je pense ...


Et nous l'appelons donc après l'initialisation de notre chip8 :)
 
 
Et voici le travail ...
 

Voilà, c'est tout pour cette fois ci, mais je pense qu'il y a beaucoup à regarder maintenant que l'émulateur produit quelque chose de "palpable" en sortie.
La prochaine fois, on ajoutera un chargeur de fichiers, plus propre que de recompiler tout à chaque changement de fichier !
Aussi, nous passerons au devkit r20 et à la dernière libnds, pour éviter les problèmes de compatibilité.
Il nous restera, si je peux, le son et les instructions SuperChip pour terminer cette mini formation sur l'émulation d'un programme.
Hal8000 Jour3  (176 fois)02 Mar 2007 | size: 318 kB
Version qui lance un jeu :D !