MIKEGBA
10/10/2006, 01h00
Initiation Assembleur - Partie 2 - Mode 4 GBA
Auteur : MIKEGBA
TELECHARGER LA ROM ICI (http://perso.orange.fr/michel.vachta/mapmode4.bin)
Acte 1
....bon alors la suite de ce tuto d'initiation à l'assembleur !!
Alors, ce soir, on va juste voir le "sommaire" de cette petite initiation avec juste une petite rom à télécharger, sans le source pour l'instant :p , histoire de vous mettre un peu l'eau à la bouche ...
J'ai choisi d'orienter ce tuto sur l'utilisation du mode 4 qui semble intéresser ici de plus en plus de monde.
Le petit programme que vous pouvez télécharger ici vous permet de vous déplacer dans une map de 2048*2048 pixels. Utiliser les touches de direction pour vous déplacer ..... en 60 fps bien sur !:)
On est donc en mode 4, mais ce que vous voyez, ce n'est pas un bitmap géant de 2048*2048 pixels, mais un ensemble de tile de 32*32 pixels.
Le format de fichier utilisé ici est le même que pour une map de background en mode 0 de la gba.
On a donc 3 fichiers source:
-1 fichier palette 256 couleurs
-1 fichier de tile regroupé dans un bitmap de 256*512 pixels
-1 fichier "map" de 8 ko .
Pour coder 1 tile, on a 16 bits, comme pour le mode 0 de la gba:
-le bit 0 à 9 nous indiquera le numéro du tile.
-le bit 10 nous indiquera si le tile est flippé horizontalement.
-le bit 11 nous indiquera si le tile est flippé verticalement.
-le bit 12 à 15 ne sont pas utilisé, alors que dans le mode textbackground, cela nous indiquait la palette, inutile ici puisque l'on est en mode 4 .
L'intéret de ce petit programme est qu'il permet de survoler vraiment beaucoup de chose, à la fois en assembleur , mais aussi dans l'utilisation du mode 4
On decortiquera donc dans les jours qui suivent l'ensemble des fonctions qui le compose, et on verra donc comment en assembleur, faire les choses suivantes:
-effacer un buffer, flipper les buffers, plotter 32 pixels d'une traite:w00t: , flipper des textures, savoir faire des sauts rapides en asm, décaler des textures dans les registres, flipper le contenu d'un registre,lire une structure,utiliser l'iwram, etc, etc...
....voilà voilà, la suite bientot, en esperant que ça pourra vous interresser et que ça vous donne un petit coup de main si vous vous lancer dans le mode 4 et l'asm.
Acte 2
Bon, pour ceux qui ont télécharger le programme dont on découvriar peu à peu le source, voilà donc la suite !
Mais commençons par le commencement !
En fait, notre programme, qu'est ce qu'il fait ?
Il affiche un background tilé en mode 4, ce bacground est donc composé de tile de 32*32 pixels. Ok, on en déduit donc que ce petit programme est composé de 2 fonctions principales:
-1 fonction d'affichage de map, en fonction de notre coordonée X et Y de notre caméra.
-1 fonction d'affichage de tile "software" 32*32 qui va etre appelée par notre fonction d'affichage de map.
Donc, avant de savoir afficher une map, et bien il faut déja savoir afficher un tile.
On va donc ce soir s'attarder ce soir sur l'étude de la problématique de base de l'affichage d'un tile de 32*32 pixels en mode 4.
Les problémes à résoudre pour l'affichage d'un malheureux bout d'image de 32*32 pixels en mode 4 sont toutefois relativement nombreux:
1) en mode 4, on ne peut pas plotter 1 seul pixel à la fois, car le bus vidéo ne supporte q'un adressage en écriture de 16 ou 32 bits: On ne peut donc stoquer que 2 ou 4 pixels à la fois, et comme vu dans un précédent post, on a vu qu'il fallait etre "aligné en mémoire pour faire ce stoquage. Pas question donc de stoquer 2 pixels de suite à une adresse impaire.
2) Il va falloir que l'on soit rapide, car on va quand meme devoir stoquer au total 240*160 pixels, soit un total de 38400 pixels !! alors finalement, le probleme numéro 1 n'est plus un obstacle , car de toute façon en plottant pixel par pixel on serait alors très loin des 60 fps....:D ..... et on verra par la suite qu'on aura une solution pour les stoquer par paquet de 32 pixels, quels que soit notre adresse de stoquage.
3) malheureusement pour nous, nos tiles seront parfois affichés que partiellement, car à cheval sur les bords de l'écran, il va falloir gerer ça, et le faire en amont , afin de ne pas se taper les tests pour chaque pixels....
4) On a besoin aussi de gerer les tiles flippés horizontalement et verticalement, afin d'économiser en stoquage.
Finalement, ça fait pas mal de choses à penser avant de taper notre première ligne de code !!
Alors, allons y , c'est parti !!
Pour notre routine d'affichage de tile, on a déja besoin de connaitre nos parametres d'entrée, et ce sont les suivants:
On va donc considérer, que notre routine d'affichage de map, lorsqu'elle apellera la routine d'affichage de tile, fournira en entrée les variables suivantes:
-registre r9 contiendra l'adresse en mémoire de notre tile
-registre r5 contiendra l'info de fliping horizontal du tile ( 1=oui / 0 =non )
-registre r10 contiendra la coordonée X d'affichage du tile ( de -31 à 239 )
-registre r7 contiendra la coordonée Y d'affichage du tile ( de -31 à 159 )
-registre r0 contient pour l'instant la valeur 256, qui est la largeur de la page de texture ou se trouve nos tile ( je rapelle qu'on travaille avec une source de tile regroupé dans une page de texture de 256*512 pixels )
en 1er lieu, on sauvegarde dans la pile, notre registre de retour, et on initialise dans r1 la hauteur de notre tile à 32 pixels.
AFFICHE_TILE: stmfd sp!,{r14}
mov r1,#32 /* initialisation de la hauteur du tile */
On va ensuite commencer par résoudre notre problematique de taille de tile, c'est à dire savoir si on est à chaval ou pas sur un bord de l'écran. On commence par le plus facile, c'est à dire les tests en Y. :) .On va donc corriger notre hauteur de tile et aussi sa coordonée Y d'affichage. Par exemple, si notre coordonée d'affichage en entrée Y était de -5, alors la coordonée d'affichage Y devient 0 et la hauteur diminue à 32-5 soit 27 pixels de haut.
Il faut aussi dans ce cas dés à présent modifier en conséquence l'adresse de la source de notre texture de tile, puisqu'on ne va pas afficher ses 5 premieres lignes....
Egalement, si notre coordonée d'affichage Y=155, alors la hauteur du tile n'est plus que de 4 pixels ( puisque il y'a 160 lignes sur l'écran lcd )
le code suivant de 9 lignes d'asm fait tout ça pour nous.
DETERMINATION_TILE_Y: cmp r7,#0 /* test si coordonée négative */
addmi r1,r7,#32 /* si négatif on calcule notre nouvelle hauteur de tile */
rsbmi r7,r7,#0 /* si négatif on calcule notre nouvelle adresse source de tile */
mulmi r6,r7,r0 /* avec la formule source=source+( -Y*256 ) on avait en entrée r0=256 */
addmi r9,r9,r6
movmi r7,#0 /* on initialise à 0 notre Y d'affichage */
bmi DETERMINATION_TILE_X /* et on va s'occuper maintenant de la correction des x.... */
cmp r7,#128 /* sinon, si Y>128, on ne change que la hauteur d'affichage du tile */
rsbgt r1,r7,#160
DETERMINATION_TILE_X: la suite demain !:p
Juste un petit commentaire au passage, pour signaler qu'en arm, on peut subordonnée l'éxéxcution d'une instruction à une condition.
exemple: addmi r9,r9,r6 ne s'éxécutera que SI le résultat du test précédent vérifiait la condition "plus petit que" ( suffixe mi ).
Dans le code, le test était cmp r7,#0, c'est à dire compare r7 à la valeur 0. Le résultat du test est conservé jusqu'au prochain test, on peut donc éxécuter des instructions conditionnées des dizaines de lignes plus loin , tant que l'on a pas lancé de nouveaux tests.:)
Voilà, c'est tout pour ce soir, c'est déja pas mal.... ça va un poil se compliquer avec les corrections des X ! :ph34r:
...bon, je conseille vivement aussi de télécharger le pdf de la doc sur l'arm7 pour la bonne compréhension de ce petit tuto, vous y trouverez notemment tous les suffixe pour les conditions par exemple....:rolleyes:
Acte 3
Ok, on poursuit !
On passe maintenant à la correction de la coordonée X d'affichage du tile. Cette fois ci c'est légerement plus compliqué, car on a vu que si on veut stoquer par paquet de 4 pixels, et on le fera meme directement par paquet de 32 , on ne peut stoquer que sur des adresses multiples de 4 !
Donc, il nous faut trouver la coordonée X multiple de 4 qui précède notre X réel.
par exemple, si notre coordonée X d'affichage est 7, alors on doit transformer ce 7en 4 .SI c'est 33, alors le X devient 32, etc...
Pour trouver le multiple de 4 , l'instruction BIC est notre ami. BIC met à 0 les bits que l'on veut.
si je fais bic r1,r2,#3, alors dans r1 on aura le contenu de r2 masqué avec la valeur #3 ( #3 en binaire= 00000011 ), les 2 derniers bits de r2 sont mis à 0, ce qui revient à avoir un multiple de 4 .... :) )
le contenu de r2 n'est pas changé, donc si je fais ensuite sub r3,r2,r1, j'aurais donc dans r3 la différence entre mon X d'origine et mon X multiple de 4.
Bon, alors voilà, vous vous dites, pourquoi afficher à une coordonée x=4 alors qu'on veut afficher notre tile à x=7 ?? il est devenu fou ?:hp:
Alors voilà venu donc le moment de dévoiler la clef de l'affichage:
-> Vu que l'on décale l'adresse d'écriture de 3 pixels vers la gauche ( on passe de x=7 à x=4 ), et bien on va dans ce cas décaler notre texture SOURCE de 3 pixels vers la droite et ainsi tout le monde sera aligné comme il faut !
On verra par la suite comment on va décaler la texture source grace à nos index de décalage que l'on va commencer par calculer.
On calcule l'index de décalage grace à la différence entre notre X d'origine et notre X multiple de 4. Si on a x=11, alors le x multiple de 4=8, donc notre différence est donc de 11-8 = 3. On sait que notre décalage est de 3 pixels, mais ce que l'on veut c'est le nombre de bits à décaler. Comme on est en mode 4, 1 pixel=8 bits, donc on multipliera par 8 pour avoir l'index de décalage final soit ici 3x8= 24 !
Pour multiplier par 8, on fera bien sur un décalage de 3 bits vers la gauche ( lsl #3)
Place au code maintenant !!
DETERMINATION_TILE_X: bic r3,r10,#3 /* coordonée x alignée (multiple de 4 précédent ) */
sub r11,r10,r3 /* ici on va calculer dans r11 et r12 des index de décalages de texture */
/* on en verra l'utilité dans la suite du code */
mov r11,r11, lsl #3 /* r11=décalage de bit à faire (0,8,16 ou 24 ) */
rsb r12,r11,#32 /* r12=décalage de bit complément à 32 */
ldr r10,Adr_MEMOIRE_VIDEO /* Calcul de l'adresse vidéo de destination */
ldr r10,[r10] /* ici on récupère déja dans r10 l'adresse du buffer qui sera 0x06000000 ou 0x0600a000 */
mov r2,r7, lsl #8 /* ici on multiplie notre coordonée Y corrigée par 240 */
sub r2,r2,r7, lsl #4 /* et on l'ajoute à l'adresse de notre buffer vidéo */
add r10,r10,r2
cmp r3,#0 /*enfin, si notre coordonée X corrigée est supérieure à 0 */
addge r10,r10,r3 /* alors, on l'ajoute, sinon on laisse à 0 ( on ne va tout de meme pas plotter devant l'écran !!!!!) */
Acte 4
Bon, récapitulons:
Après ces quelques lignes de code, on a déja calculé pas mal de paramètres pour notre plotage de tiles, et on a donc maintenant:
r10: adresse vidéo d'écriture de notre tile
r3: coordonée X multiple de 4 de notre tile
r11 et r12 : nombre de décalage de bits à droite et à gauche à faire dans notre texture source pour aligner correctement la texture avec l'écran.
r1: hauteur du tile ( nombre de ligne à tracer, en principe 32 sauf si notre tile est à cheval sur le haut et le bas de l'écran )
Il reste encore un paramètre important à calculer, avant que l'on ne rentre véritablement dans la boucle d'affichage. On a vu que l'on voulait ploter les tile directement par paquet de 32 pixels.
On pourrait donc maintenant le faire, cependant, on aurait de joli bugs d'affichage pour les cas ou le tile est à cheval sur le coté droit ou gauche de l'écran.
Imaginons que l'on doivent plotter le tile à X= 232, il ne faut pas que l'on plotte 32 pixels, mais seulement 8 !! Idem, si la coordonée est = -8, on en ploteras que 24.
On est donc devant un beau casse tete, d'autant, qu'il existe si on fait bien les compte 17 cas de figure différents:
-1er cas: le tile est completemnt dans l'écran.
-8 cas ou le tile est à cheval sur le bord gauche de l'écran ( 4,8,12,16,20,24,28 ou 32 pixels ) à plotter.
-8 cas ou le tile est à cheval sur le bord droit de l'écran....
Il est évident, vu que l'on veut une routine d'affichage rapide..., que l'on ne vas pas y'aller en bourrin à faire 17 tests....
On va donc rester serein, et commencer par calculer dans quel cas on se trouve, et ça on peut le faire en quelques lignes d'assembleur
allons y !!:)
SUITE_TILE: mov r4,#0 /* r4 contiendra l'info sur le cas dans lequel on se trouve*/
bge CAS_POSITIF /* on se réfere ici à notre test précédent ou on testait le signe de r3, notre coordonée d'affichage en x. */
/* si r3 est négatif, dans ce cas, on initialise r4 ( qui contiendra notre cas ) à une
valeur comprise entre 1 et 8. Pour ce faire, on transfere dans r4 la valeur absolue de r3
( qui est négatif ), grâce à l'instruction rsb.
rsb permet de soustraire d'une constante un registre.
ici on soustrait r3 de 0 et on met le résultat dans r4 ( r4=0-r3)
on divise ensuite r4 par 4, avec un décalage binaire de 2 bits vers la droite pour
avoir une valeur entre 1 et 8 ( puisque r3 était un multiple de 4 de 1 à 32...:rolleyes: ) */
CAS_NEGATIF: rsb r4,r3,#0
mov r4,r4, lsr #2
b STORE_TILE
/* si r3 est positif, dans ce cas, on initialise r4 ( qui contiendra notre cas ) à une
valeur comprise entre 9 et 16 ou 0 !!
En effet, si r3<208, alors on laisse notre cas à 0, cela signifie que notre tile sera
affiché normalement, car il n'est pas à chavel sur un bord d'écran.
Sinon, on regarde combien on a de pixels à plotter ( en regardant de combien on dépasse
x=208, et comme tout à l'heure on divise par 4 avace décalage de 2 bits vers la droite, ce qui donne une avleur entre 0 et 7.
On ajoute ensuite 9, car on veut une valeur entre 9 et 16
CAS_POSITIF: cmp r3,#208
subge r4,r3,#208
movge r4,r4, lsr #2
addge r4,r4,#9
A ce stade , le registre r4 contient maintenant une valeur comprise entre 0 et 16 qui correspond à toutes les possibilités d'affichage ! Si on regarde tous les cheminements possibles du code, on met pas plus de 5 ou 6 instructions asm pour faire ça.... et ce calcul restera acquis pour les 32 lignes d'affichage du tile:)
Pour la fin de l'acte 4, juste une dernière ligne de code.
La suite du code va nécéssiter l'utilisation de nombreux registres, et on en a malheureusement pas un nombre infini à notre disposition.
On va donc"compresser" dans un meme registre le contenu de 2 autres, que l'on extraiera par la suite selon nos besoin ( on verra plus tard )
STORE_TILE: orr r14,r5,r4, lsl #16 /* mix dans r14 info fliping et case */
le registre r14 contient maintenant dans la partie droite la valeur du registre r5 ( qui valait 1 ou 0 , c'est notre info sur le fliping horizontal du tile ), et dans sa partie gauche la valeur du registre r4 ( qui a une valeur entre 0 et 16 )
Cela n'est bien sur possible car l'on sait que chaque registre n'utilisait en réalité qu'une valeur utile de 16 bits maximum chacun...:whst:
Voilà, c'est tout pour cette fois, mais reposez vous les neurones, car ça va secouer un peu plus dans la prochaine partie ou l'on rentre enfin dans la boucle d'affichage et on commencera par le fliping horizontal des tiles !!:D
Acte 5
Bien, bien, après cette partie d'initialisation, on passe vraiment à la boucle d'affichage proprement dite. ( la partie qui intéresse le plus Pitt !! )
Notre boucle va se répetter autant de fois qu'on a de ligne a tracer, donc pour un tile au milieu de l'écran 32 ligne, et moins si notre tile est à cheval sur un bord de l'écran en haut ou en bas, mais ça on l'a déja calculé precedemment.
-Dans cette boucle on va:
- charger nos 32 pixels à plotter, si besoin les flipper si l'on a à faire à un tile qui est flippé horizontalement ( et pour cela on teste notre info de flipping )
- faire scroller notre texture à l'intérieur des registre d'autant de pixel qu'il le faut pour etre aligné avec une adresse multiple de 4, grâce aux index de décalage qu'on a calculé precedemment.
- brancher notre routine sur l'un des 15 cas possible d'affichage
- et enfin afficher nos 32 pixels.
Tout dabords, le flipping:
si l'on considere nos 32 pixels numérotés de 1 à 32, voilà comment ils vont etre répartis dans les registres r0 à r7:
r0 : p1,p2,p3,p4
r1 : p5,p6,p7,p8
r2 : p9,p10,p11,p12
r3 : p13,p14,p15,p16
r4 : p17,p18,p19,p20
r5 : p21,p22,p23,p24
r6 : p25,p26,p27,p28
r7 : p29,p30,p31,p32
En cas de flip horizontal, on veut donc inverser l'ordre d'affichage, et afficher de gauche à droite les pixels 32 à 1 et non plus 1 à 32
On s'appercoit donc qu'on va devoir swapper le registre r0 avec r7, r1 avec r6, r2 avec r5, r3 avec r4
....et à l'intérieur de chaque registre il faudra également swapper les 4 pixels, pour arriver finalement au résultat suivant :
r0 : p32,p31,p30,p29
r1 : p28,p27,p26,p25
r2 : p24,p23,p22,p21
r3 : p20,p19,p18,p17
r4 : p16,p15,p14,p13
r5 : p12,p11,p10,p9
r6 : p8,p7,p6,p5
r7 : p4,p3,p2,p1
la permutation des registres ne pose pas de soucis, là ou ça se corse, c'est la permutation des 4 pixels à l'intérieur de chaque registre. La solution dans le code plus bas, ou l'on voit que la permutaion des valeurs 8 bits dans 1 registre peut se faire en 3 instructions...
La phase suivante de scrolling de la texture dans les 8 registres vers la gauche, va donc nécéssiter l'utilisation d'un 9em registre. Et oui, si l'on décale 1 registre vers la gauche, il faut bien que l'on récupere les pixels "perdus" dans un 2em registre.
l'exemple suivant permettra de mieux comprendre.
si on considere le registre r1 avec les pixels p0,p1,p2,p3 et le registre r2 avec les pixels p4,p5,p6,p7
On veut par exemple faire scroller les pixels de 2 unités vers la gauche, on aura à la fin :
r0: ?,?,p0,p1
r1: p2,p3,p4,p5
r2: p6,p7,?,?
On aura eu donc recour au registre r0 pour récupérer les pixels p0 et p1 !
Dans le code ci-dessous, on passe en fait de 8 registres r0 à r7 à 9 registres r0 à r8.
Ce qu'il faut noter dans cette phase , c'est que les instruction de décalage ne sont pas faite avec des constante, comme par exemple un mov r7,r7, lsl #16, mais le nombre de décalage est fait par un registre: mov r7,r7, lsl r11, ce qui veut dire si r11 contient par exemple la valeur #16, fait un décalage vers la gauche de 16 bits.
Voilà, rendez vous demain pour le dernier acte, qui va concerner le branchement en fonction de nos différents cas d'affichage. Le code source complet du programme sera aussi en téléchargement à partir de demain !
BOUCLE_AFFICHAGE:stmfd sp!,{r0,r1,r9} /* Point de départ de la boucle, on empile 3 registres
ldmia r9,{r0-r7} /* on charge nos 32 pixels dans les registres r0 à r7 à partie de l'adresse source de la texture pointée par r9
tst r14,#1 /* on teste notre info de flipping */
beq NO_TILE_FLIPING /* si pas de flip alors on passe */
TILE_FLIPING: mov r8,r7 /* 1) phase de permutaion des registres
mov r7,r0
mov r0,r8
mov r8,r6
mov r6,r1
mov r1,r8
mov r8,r5
mov r5,r2
mov r2,r8
mov r8,r4
mov r4,r3
mov r3,r8
mov r8,#0xff00 /* 2) phase de permutations des pixels à l'intérieur de chaque registre */
orr r8,r8,r8, lsl #16 /* ou l'on se sert d'un masque inittialisé dans r8 à 0xff00ff00
and r9,r8,r0, ror #16 /* permutation des pixels dans les 8 registre grace à une combinaison and,and,orr mixée avec des rotations...
and r0,r8,r0, ror #8
orr r0,r0,r9, lsr #8
and r9,r8,r1, ror #16
and r1,r8,r1, ror #8
orr r1,r1,r9, lsr #8
and r9,r8,r2, ror #16
and r2,r8,r2, ror #8
orr r2,r2,r9, lsr #8
and r9,r8,r3, ror #16
and r3,r8,r3, ror #8
orr r3,r3,r9, lsr #8
and r9,r8,r4, ror #16
and r4,r8,r4, ror #8
orr r4,r4,r9, lsr #8
and r9,r8,r5, ror #16
and r5,r8,r5, ror #8
orr r5,r5,r9, lsr #8
and r9,r8,r6, ror #16
and r6,r8,r6, ror #8
orr r6,r6,r9, lsr #8
and r9,r8,r7, ror #16
and r7,r8,r7, ror #8
orr r7,r7,r9, lsr #8
NO_TILE_FLIPING: mov r8,r7, lsr r12 /* scrolling de la texture dans les registres */
mov r7,r7, lsl r11
orr r7,r7,r6, lsr r12
mov r6,r6, lsl r11
orr r6,r6,r5, lsr r12
mov r5,r5, lsl r11
orr r5,r5,r4, lsr r12
mov r4,r4, lsl r11
orr r4,r4,r3, lsr r12
mov r3,r3, lsl r11
orr r3,r3,r2, lsr r12
mov r2,r2, lsl r11
orr r2,r2,r1, lsr r12
mov r1,r1, lsl r11
orr r1,r1,r0, lsr r12
mov r0,r0, lsl r11
Acte 6
:D Bon, aprés une courte interruption ( joke inside ), suite et fin de cette routine d'affichage de tiles software en mode 4 !!
On a donc à cet stade dans nos registre r0 à r8 notre ligne de texture correctement flippée si nécéssaire et décalée comme il faut pour corrspondre à une adresse de storage multiple de 4.
On sait également que dans la partie gauche du registre r14, on a l'info de notre cas d'affichage , et en fonction de cette info, on va brancher le programme sur une des 15 routines d'affichage qui correspond au cas qui nous intéresse.
On va donc faire une sorte d'équivalent en C à un switch/case, à la différence énorme que l'on ne vas faire AUCUN TEST pour brancher sur la routine voulue, ce qui nous permet d'économiser un temps cpu considérable.
Pour ce faire , on va "jouer" avec le registre r15, ( pc ) qui est notre programme counter. Ce registre contient en permanence l'adresse de la prochaine instruction qui doit etre éxécutée par le cpu. L'astuce consiste donc à modifier ce registre r15 pour lui indiquer "ou aller" à la prochaine instruction. Si vous jetez un coup d'oeil au bout de code ci dessus, on s'apercoit que chacune des 15 routines fait 4 instructions. On sait que chaque instruction arm est codée sur 32 bits, soit 4 octets, donc chaque routine de 4 instruction occupe donc 8*4= 32 octets.
On commence donc à isoler notre info sur la routine à éxécutée contenue dans la partie gauche de r14, et on met dans r9
mov r9,r14, lsr #16
on ajoute ensuite à notre r15, cette valeur multipliée par 16 ( x4 pour le nombre de ligne ASM et x4 pour le fait q'une instruction=4 octets ), r15 contient maintenant l'adresse ou il doit sauter à la prochaine instruction:
add pc,pc,r9, lsl #4
....et voilà le tour est joué, en 2 lignes d'asm, on a fait un branchement conditionné à l'état d'une valeur.... pas besoin de faire une suite de
cmp r14,#0
beq .......
cmp r14,#1
beq......
etc....
...et c'est encore plus rapide que la lecture d'une table de saut , puisqu'il n'ya pas de valeur à lire dans une table.:p
Bien sur cette technique ne s'applique que dans le cas ou chaque routine fait strictement le meme nombre d'instruction. M'enfin meme si c'est pas le cas , y'a toujours moyen de contourner en branchant de cette facon sur une table d'instruction qui renverra elle meme ensuite à la bonne adresse......B)
Voilà, vous pouvez voire enfin que chaque routine individuelle, n'est que le storage de nos 32 pixels, en tenant compte des pixels présents à l'écran pour ne pas écraser les pixels devant le tile.
A la fin de chaque routine de plotage on branche ensuite vers le label END_CASE: ou l'on va mettre à jour toutes nos variable pour afficher la ligne suivante du tile.
On met à jour l'adresse d'écriture dans notre buffer vidéo ( registre r10), on décrémente le nombre de ligne à afficher ( registre r1 ).... et enfin on met à jour également notre adresse de lecture dans notre texture source ( registre r9 )
C'est donc là, qu'on s'occupe du flipping vertical du tile !! en effet, sans flipping, on aurait mis à jour simplement en ajoutant la valeur #256 , car la page de texture fait 256 pixels de large.
Donc, pour descendre d'une ligne dans la texture, on ajoute #256, et si on veut remonter ( flip vertical ), alors on ajoute #-256 .
Et comme on veut pas faire de tests, on a initialisé avant dans le registre r0 , avant d'entrer dans la routine, la valeur adéquate ( #256 ou # -256 )
et on a donc la ligne add r9,r9,r0 ( tu vois Pitt, je te l'avais dit qu'on faisait le flip vertical en une seule ligne :D )
Ci dessous la suite et donc la fin de cette routine d'affichage de tile software .
Voilà donc la fin de ce petit tuto d'initiation à l'assembleur et au mode 4, j'espere qu'il aura pu vous être unpetit peu utile, ou que au moins cela vous aura intéréssé.
a bientot pour de prochaines aventures !!:)
CALCUL_BRANCHEMENT_CASE: mov r9,r14, lsr #16
add pc,pc,r9, lsl #4
nop /* instruction dummy pour occuper 32 bits */
CASE_0: ldr r9,[r10]
orr r0,r0,r9 stmia r10,{r0-r8}
b END_CASE
CASE_1: ldr r9,[r10]
orr r1,r1,r9
stmia r10,{r1-r8}
b END_CASE
CASE_2: ldr r9,[r10]
orr r2,r2,r9
stmia r10,{r2-r8}
b END_CASE
CASE_3: ldr r9,[r10]
orr r3,r3,r9
stmia r10,{r3-r8}
b END_CASE
CASE_4: ldr r9,[r10]
orr r4,r4,r9
stmia r10,{r4-r8}
b END_CASE
CASE_5: ldr r9,[r10]
orr r5,r5,r9
stmia r10,{r5-r8}
b END_CASE
CASE_6: ldr r9,[r10]
orr r6,r6,r9
stmia r10,{r6-r8}
b END_CASE
CASE_7: ldr r9,[r10]
orr r7,r7,r9
stmia r10,{r7-r8}
b END_CASE
CASE_8: ldr r9,[r10]
orr r8,r8,r9
str r8,[r10]
b END_CASE
CASE_9: ldr r9,[r10]
orr r0,r0,r9
stmia r10,{r0-r7}
b END_CASE
CASE_9A: ldr r9,[r10]
orr r0,r0,r9
stmia r10,{r0-r6}
b END_CASE
CASE_10: ldr r9,[r10]
orr r0,r0,r9
stmia r10,{r0-r5}
b END_CASE
CASE_11: ldr r9,[r10]
orr r0,r0,r9
stmia r10,{r0-r4}
b END_CASE
CASE_12: ldr r9,[r10]
orr r0,r0,r9
stmia r10,{r0-r3}
b END_CASE
CASE_13: ldr r9,[r10]
orr r0,r0,r9
stmia r10,{r0-r2}
b END_CASE
CASE_14: ldr r9,[r10]
orr r0,r0,r9
stmia r10,{r0-R1}
b END_CASE
CASE_15: ldr r9,[r10]
orr r0,r0,r9
str r0,[r10]
NOP
END_CASE: ldmfd sp!,{r0,r1,r9}
add r9,r9,r0
add r10,r10,#240 subs r1,r1,#1
bne BOUCLE_TILE
ldmfd sp!,{r15}
Auteur : MIKEGBA
TELECHARGER LA ROM ICI (http://perso.orange.fr/michel.vachta/mapmode4.bin)
Acte 1
....bon alors la suite de ce tuto d'initiation à l'assembleur !!
Alors, ce soir, on va juste voir le "sommaire" de cette petite initiation avec juste une petite rom à télécharger, sans le source pour l'instant :p , histoire de vous mettre un peu l'eau à la bouche ...
J'ai choisi d'orienter ce tuto sur l'utilisation du mode 4 qui semble intéresser ici de plus en plus de monde.
Le petit programme que vous pouvez télécharger ici vous permet de vous déplacer dans une map de 2048*2048 pixels. Utiliser les touches de direction pour vous déplacer ..... en 60 fps bien sur !:)
On est donc en mode 4, mais ce que vous voyez, ce n'est pas un bitmap géant de 2048*2048 pixels, mais un ensemble de tile de 32*32 pixels.
Le format de fichier utilisé ici est le même que pour une map de background en mode 0 de la gba.
On a donc 3 fichiers source:
-1 fichier palette 256 couleurs
-1 fichier de tile regroupé dans un bitmap de 256*512 pixels
-1 fichier "map" de 8 ko .
Pour coder 1 tile, on a 16 bits, comme pour le mode 0 de la gba:
-le bit 0 à 9 nous indiquera le numéro du tile.
-le bit 10 nous indiquera si le tile est flippé horizontalement.
-le bit 11 nous indiquera si le tile est flippé verticalement.
-le bit 12 à 15 ne sont pas utilisé, alors que dans le mode textbackground, cela nous indiquait la palette, inutile ici puisque l'on est en mode 4 .
L'intéret de ce petit programme est qu'il permet de survoler vraiment beaucoup de chose, à la fois en assembleur , mais aussi dans l'utilisation du mode 4
On decortiquera donc dans les jours qui suivent l'ensemble des fonctions qui le compose, et on verra donc comment en assembleur, faire les choses suivantes:
-effacer un buffer, flipper les buffers, plotter 32 pixels d'une traite:w00t: , flipper des textures, savoir faire des sauts rapides en asm, décaler des textures dans les registres, flipper le contenu d'un registre,lire une structure,utiliser l'iwram, etc, etc...
....voilà voilà, la suite bientot, en esperant que ça pourra vous interresser et que ça vous donne un petit coup de main si vous vous lancer dans le mode 4 et l'asm.
Acte 2
Bon, pour ceux qui ont télécharger le programme dont on découvriar peu à peu le source, voilà donc la suite !
Mais commençons par le commencement !
En fait, notre programme, qu'est ce qu'il fait ?
Il affiche un background tilé en mode 4, ce bacground est donc composé de tile de 32*32 pixels. Ok, on en déduit donc que ce petit programme est composé de 2 fonctions principales:
-1 fonction d'affichage de map, en fonction de notre coordonée X et Y de notre caméra.
-1 fonction d'affichage de tile "software" 32*32 qui va etre appelée par notre fonction d'affichage de map.
Donc, avant de savoir afficher une map, et bien il faut déja savoir afficher un tile.
On va donc ce soir s'attarder ce soir sur l'étude de la problématique de base de l'affichage d'un tile de 32*32 pixels en mode 4.
Les problémes à résoudre pour l'affichage d'un malheureux bout d'image de 32*32 pixels en mode 4 sont toutefois relativement nombreux:
1) en mode 4, on ne peut pas plotter 1 seul pixel à la fois, car le bus vidéo ne supporte q'un adressage en écriture de 16 ou 32 bits: On ne peut donc stoquer que 2 ou 4 pixels à la fois, et comme vu dans un précédent post, on a vu qu'il fallait etre "aligné en mémoire pour faire ce stoquage. Pas question donc de stoquer 2 pixels de suite à une adresse impaire.
2) Il va falloir que l'on soit rapide, car on va quand meme devoir stoquer au total 240*160 pixels, soit un total de 38400 pixels !! alors finalement, le probleme numéro 1 n'est plus un obstacle , car de toute façon en plottant pixel par pixel on serait alors très loin des 60 fps....:D ..... et on verra par la suite qu'on aura une solution pour les stoquer par paquet de 32 pixels, quels que soit notre adresse de stoquage.
3) malheureusement pour nous, nos tiles seront parfois affichés que partiellement, car à cheval sur les bords de l'écran, il va falloir gerer ça, et le faire en amont , afin de ne pas se taper les tests pour chaque pixels....
4) On a besoin aussi de gerer les tiles flippés horizontalement et verticalement, afin d'économiser en stoquage.
Finalement, ça fait pas mal de choses à penser avant de taper notre première ligne de code !!
Alors, allons y , c'est parti !!
Pour notre routine d'affichage de tile, on a déja besoin de connaitre nos parametres d'entrée, et ce sont les suivants:
On va donc considérer, que notre routine d'affichage de map, lorsqu'elle apellera la routine d'affichage de tile, fournira en entrée les variables suivantes:
-registre r9 contiendra l'adresse en mémoire de notre tile
-registre r5 contiendra l'info de fliping horizontal du tile ( 1=oui / 0 =non )
-registre r10 contiendra la coordonée X d'affichage du tile ( de -31 à 239 )
-registre r7 contiendra la coordonée Y d'affichage du tile ( de -31 à 159 )
-registre r0 contient pour l'instant la valeur 256, qui est la largeur de la page de texture ou se trouve nos tile ( je rapelle qu'on travaille avec une source de tile regroupé dans une page de texture de 256*512 pixels )
en 1er lieu, on sauvegarde dans la pile, notre registre de retour, et on initialise dans r1 la hauteur de notre tile à 32 pixels.
AFFICHE_TILE: stmfd sp!,{r14}
mov r1,#32 /* initialisation de la hauteur du tile */
On va ensuite commencer par résoudre notre problematique de taille de tile, c'est à dire savoir si on est à chaval ou pas sur un bord de l'écran. On commence par le plus facile, c'est à dire les tests en Y. :) .On va donc corriger notre hauteur de tile et aussi sa coordonée Y d'affichage. Par exemple, si notre coordonée d'affichage en entrée Y était de -5, alors la coordonée d'affichage Y devient 0 et la hauteur diminue à 32-5 soit 27 pixels de haut.
Il faut aussi dans ce cas dés à présent modifier en conséquence l'adresse de la source de notre texture de tile, puisqu'on ne va pas afficher ses 5 premieres lignes....
Egalement, si notre coordonée d'affichage Y=155, alors la hauteur du tile n'est plus que de 4 pixels ( puisque il y'a 160 lignes sur l'écran lcd )
le code suivant de 9 lignes d'asm fait tout ça pour nous.
DETERMINATION_TILE_Y: cmp r7,#0 /* test si coordonée négative */
addmi r1,r7,#32 /* si négatif on calcule notre nouvelle hauteur de tile */
rsbmi r7,r7,#0 /* si négatif on calcule notre nouvelle adresse source de tile */
mulmi r6,r7,r0 /* avec la formule source=source+( -Y*256 ) on avait en entrée r0=256 */
addmi r9,r9,r6
movmi r7,#0 /* on initialise à 0 notre Y d'affichage */
bmi DETERMINATION_TILE_X /* et on va s'occuper maintenant de la correction des x.... */
cmp r7,#128 /* sinon, si Y>128, on ne change que la hauteur d'affichage du tile */
rsbgt r1,r7,#160
DETERMINATION_TILE_X: la suite demain !:p
Juste un petit commentaire au passage, pour signaler qu'en arm, on peut subordonnée l'éxéxcution d'une instruction à une condition.
exemple: addmi r9,r9,r6 ne s'éxécutera que SI le résultat du test précédent vérifiait la condition "plus petit que" ( suffixe mi ).
Dans le code, le test était cmp r7,#0, c'est à dire compare r7 à la valeur 0. Le résultat du test est conservé jusqu'au prochain test, on peut donc éxécuter des instructions conditionnées des dizaines de lignes plus loin , tant que l'on a pas lancé de nouveaux tests.:)
Voilà, c'est tout pour ce soir, c'est déja pas mal.... ça va un poil se compliquer avec les corrections des X ! :ph34r:
...bon, je conseille vivement aussi de télécharger le pdf de la doc sur l'arm7 pour la bonne compréhension de ce petit tuto, vous y trouverez notemment tous les suffixe pour les conditions par exemple....:rolleyes:
Acte 3
Ok, on poursuit !
On passe maintenant à la correction de la coordonée X d'affichage du tile. Cette fois ci c'est légerement plus compliqué, car on a vu que si on veut stoquer par paquet de 4 pixels, et on le fera meme directement par paquet de 32 , on ne peut stoquer que sur des adresses multiples de 4 !
Donc, il nous faut trouver la coordonée X multiple de 4 qui précède notre X réel.
par exemple, si notre coordonée X d'affichage est 7, alors on doit transformer ce 7en 4 .SI c'est 33, alors le X devient 32, etc...
Pour trouver le multiple de 4 , l'instruction BIC est notre ami. BIC met à 0 les bits que l'on veut.
si je fais bic r1,r2,#3, alors dans r1 on aura le contenu de r2 masqué avec la valeur #3 ( #3 en binaire= 00000011 ), les 2 derniers bits de r2 sont mis à 0, ce qui revient à avoir un multiple de 4 .... :) )
le contenu de r2 n'est pas changé, donc si je fais ensuite sub r3,r2,r1, j'aurais donc dans r3 la différence entre mon X d'origine et mon X multiple de 4.
Bon, alors voilà, vous vous dites, pourquoi afficher à une coordonée x=4 alors qu'on veut afficher notre tile à x=7 ?? il est devenu fou ?:hp:
Alors voilà venu donc le moment de dévoiler la clef de l'affichage:
-> Vu que l'on décale l'adresse d'écriture de 3 pixels vers la gauche ( on passe de x=7 à x=4 ), et bien on va dans ce cas décaler notre texture SOURCE de 3 pixels vers la droite et ainsi tout le monde sera aligné comme il faut !
On verra par la suite comment on va décaler la texture source grace à nos index de décalage que l'on va commencer par calculer.
On calcule l'index de décalage grace à la différence entre notre X d'origine et notre X multiple de 4. Si on a x=11, alors le x multiple de 4=8, donc notre différence est donc de 11-8 = 3. On sait que notre décalage est de 3 pixels, mais ce que l'on veut c'est le nombre de bits à décaler. Comme on est en mode 4, 1 pixel=8 bits, donc on multipliera par 8 pour avoir l'index de décalage final soit ici 3x8= 24 !
Pour multiplier par 8, on fera bien sur un décalage de 3 bits vers la gauche ( lsl #3)
Place au code maintenant !!
DETERMINATION_TILE_X: bic r3,r10,#3 /* coordonée x alignée (multiple de 4 précédent ) */
sub r11,r10,r3 /* ici on va calculer dans r11 et r12 des index de décalages de texture */
/* on en verra l'utilité dans la suite du code */
mov r11,r11, lsl #3 /* r11=décalage de bit à faire (0,8,16 ou 24 ) */
rsb r12,r11,#32 /* r12=décalage de bit complément à 32 */
ldr r10,Adr_MEMOIRE_VIDEO /* Calcul de l'adresse vidéo de destination */
ldr r10,[r10] /* ici on récupère déja dans r10 l'adresse du buffer qui sera 0x06000000 ou 0x0600a000 */
mov r2,r7, lsl #8 /* ici on multiplie notre coordonée Y corrigée par 240 */
sub r2,r2,r7, lsl #4 /* et on l'ajoute à l'adresse de notre buffer vidéo */
add r10,r10,r2
cmp r3,#0 /*enfin, si notre coordonée X corrigée est supérieure à 0 */
addge r10,r10,r3 /* alors, on l'ajoute, sinon on laisse à 0 ( on ne va tout de meme pas plotter devant l'écran !!!!!) */
Acte 4
Bon, récapitulons:
Après ces quelques lignes de code, on a déja calculé pas mal de paramètres pour notre plotage de tiles, et on a donc maintenant:
r10: adresse vidéo d'écriture de notre tile
r3: coordonée X multiple de 4 de notre tile
r11 et r12 : nombre de décalage de bits à droite et à gauche à faire dans notre texture source pour aligner correctement la texture avec l'écran.
r1: hauteur du tile ( nombre de ligne à tracer, en principe 32 sauf si notre tile est à cheval sur le haut et le bas de l'écran )
Il reste encore un paramètre important à calculer, avant que l'on ne rentre véritablement dans la boucle d'affichage. On a vu que l'on voulait ploter les tile directement par paquet de 32 pixels.
On pourrait donc maintenant le faire, cependant, on aurait de joli bugs d'affichage pour les cas ou le tile est à cheval sur le coté droit ou gauche de l'écran.
Imaginons que l'on doivent plotter le tile à X= 232, il ne faut pas que l'on plotte 32 pixels, mais seulement 8 !! Idem, si la coordonée est = -8, on en ploteras que 24.
On est donc devant un beau casse tete, d'autant, qu'il existe si on fait bien les compte 17 cas de figure différents:
-1er cas: le tile est completemnt dans l'écran.
-8 cas ou le tile est à cheval sur le bord gauche de l'écran ( 4,8,12,16,20,24,28 ou 32 pixels ) à plotter.
-8 cas ou le tile est à cheval sur le bord droit de l'écran....
Il est évident, vu que l'on veut une routine d'affichage rapide..., que l'on ne vas pas y'aller en bourrin à faire 17 tests....
On va donc rester serein, et commencer par calculer dans quel cas on se trouve, et ça on peut le faire en quelques lignes d'assembleur
allons y !!:)
SUITE_TILE: mov r4,#0 /* r4 contiendra l'info sur le cas dans lequel on se trouve*/
bge CAS_POSITIF /* on se réfere ici à notre test précédent ou on testait le signe de r3, notre coordonée d'affichage en x. */
/* si r3 est négatif, dans ce cas, on initialise r4 ( qui contiendra notre cas ) à une
valeur comprise entre 1 et 8. Pour ce faire, on transfere dans r4 la valeur absolue de r3
( qui est négatif ), grâce à l'instruction rsb.
rsb permet de soustraire d'une constante un registre.
ici on soustrait r3 de 0 et on met le résultat dans r4 ( r4=0-r3)
on divise ensuite r4 par 4, avec un décalage binaire de 2 bits vers la droite pour
avoir une valeur entre 1 et 8 ( puisque r3 était un multiple de 4 de 1 à 32...:rolleyes: ) */
CAS_NEGATIF: rsb r4,r3,#0
mov r4,r4, lsr #2
b STORE_TILE
/* si r3 est positif, dans ce cas, on initialise r4 ( qui contiendra notre cas ) à une
valeur comprise entre 9 et 16 ou 0 !!
En effet, si r3<208, alors on laisse notre cas à 0, cela signifie que notre tile sera
affiché normalement, car il n'est pas à chavel sur un bord d'écran.
Sinon, on regarde combien on a de pixels à plotter ( en regardant de combien on dépasse
x=208, et comme tout à l'heure on divise par 4 avace décalage de 2 bits vers la droite, ce qui donne une avleur entre 0 et 7.
On ajoute ensuite 9, car on veut une valeur entre 9 et 16
CAS_POSITIF: cmp r3,#208
subge r4,r3,#208
movge r4,r4, lsr #2
addge r4,r4,#9
A ce stade , le registre r4 contient maintenant une valeur comprise entre 0 et 16 qui correspond à toutes les possibilités d'affichage ! Si on regarde tous les cheminements possibles du code, on met pas plus de 5 ou 6 instructions asm pour faire ça.... et ce calcul restera acquis pour les 32 lignes d'affichage du tile:)
Pour la fin de l'acte 4, juste une dernière ligne de code.
La suite du code va nécéssiter l'utilisation de nombreux registres, et on en a malheureusement pas un nombre infini à notre disposition.
On va donc"compresser" dans un meme registre le contenu de 2 autres, que l'on extraiera par la suite selon nos besoin ( on verra plus tard )
STORE_TILE: orr r14,r5,r4, lsl #16 /* mix dans r14 info fliping et case */
le registre r14 contient maintenant dans la partie droite la valeur du registre r5 ( qui valait 1 ou 0 , c'est notre info sur le fliping horizontal du tile ), et dans sa partie gauche la valeur du registre r4 ( qui a une valeur entre 0 et 16 )
Cela n'est bien sur possible car l'on sait que chaque registre n'utilisait en réalité qu'une valeur utile de 16 bits maximum chacun...:whst:
Voilà, c'est tout pour cette fois, mais reposez vous les neurones, car ça va secouer un peu plus dans la prochaine partie ou l'on rentre enfin dans la boucle d'affichage et on commencera par le fliping horizontal des tiles !!:D
Acte 5
Bien, bien, après cette partie d'initialisation, on passe vraiment à la boucle d'affichage proprement dite. ( la partie qui intéresse le plus Pitt !! )
Notre boucle va se répetter autant de fois qu'on a de ligne a tracer, donc pour un tile au milieu de l'écran 32 ligne, et moins si notre tile est à cheval sur un bord de l'écran en haut ou en bas, mais ça on l'a déja calculé precedemment.
-Dans cette boucle on va:
- charger nos 32 pixels à plotter, si besoin les flipper si l'on a à faire à un tile qui est flippé horizontalement ( et pour cela on teste notre info de flipping )
- faire scroller notre texture à l'intérieur des registre d'autant de pixel qu'il le faut pour etre aligné avec une adresse multiple de 4, grâce aux index de décalage qu'on a calculé precedemment.
- brancher notre routine sur l'un des 15 cas possible d'affichage
- et enfin afficher nos 32 pixels.
Tout dabords, le flipping:
si l'on considere nos 32 pixels numérotés de 1 à 32, voilà comment ils vont etre répartis dans les registres r0 à r7:
r0 : p1,p2,p3,p4
r1 : p5,p6,p7,p8
r2 : p9,p10,p11,p12
r3 : p13,p14,p15,p16
r4 : p17,p18,p19,p20
r5 : p21,p22,p23,p24
r6 : p25,p26,p27,p28
r7 : p29,p30,p31,p32
En cas de flip horizontal, on veut donc inverser l'ordre d'affichage, et afficher de gauche à droite les pixels 32 à 1 et non plus 1 à 32
On s'appercoit donc qu'on va devoir swapper le registre r0 avec r7, r1 avec r6, r2 avec r5, r3 avec r4
....et à l'intérieur de chaque registre il faudra également swapper les 4 pixels, pour arriver finalement au résultat suivant :
r0 : p32,p31,p30,p29
r1 : p28,p27,p26,p25
r2 : p24,p23,p22,p21
r3 : p20,p19,p18,p17
r4 : p16,p15,p14,p13
r5 : p12,p11,p10,p9
r6 : p8,p7,p6,p5
r7 : p4,p3,p2,p1
la permutation des registres ne pose pas de soucis, là ou ça se corse, c'est la permutation des 4 pixels à l'intérieur de chaque registre. La solution dans le code plus bas, ou l'on voit que la permutaion des valeurs 8 bits dans 1 registre peut se faire en 3 instructions...
La phase suivante de scrolling de la texture dans les 8 registres vers la gauche, va donc nécéssiter l'utilisation d'un 9em registre. Et oui, si l'on décale 1 registre vers la gauche, il faut bien que l'on récupere les pixels "perdus" dans un 2em registre.
l'exemple suivant permettra de mieux comprendre.
si on considere le registre r1 avec les pixels p0,p1,p2,p3 et le registre r2 avec les pixels p4,p5,p6,p7
On veut par exemple faire scroller les pixels de 2 unités vers la gauche, on aura à la fin :
r0: ?,?,p0,p1
r1: p2,p3,p4,p5
r2: p6,p7,?,?
On aura eu donc recour au registre r0 pour récupérer les pixels p0 et p1 !
Dans le code ci-dessous, on passe en fait de 8 registres r0 à r7 à 9 registres r0 à r8.
Ce qu'il faut noter dans cette phase , c'est que les instruction de décalage ne sont pas faite avec des constante, comme par exemple un mov r7,r7, lsl #16, mais le nombre de décalage est fait par un registre: mov r7,r7, lsl r11, ce qui veut dire si r11 contient par exemple la valeur #16, fait un décalage vers la gauche de 16 bits.
Voilà, rendez vous demain pour le dernier acte, qui va concerner le branchement en fonction de nos différents cas d'affichage. Le code source complet du programme sera aussi en téléchargement à partir de demain !
BOUCLE_AFFICHAGE:stmfd sp!,{r0,r1,r9} /* Point de départ de la boucle, on empile 3 registres
ldmia r9,{r0-r7} /* on charge nos 32 pixels dans les registres r0 à r7 à partie de l'adresse source de la texture pointée par r9
tst r14,#1 /* on teste notre info de flipping */
beq NO_TILE_FLIPING /* si pas de flip alors on passe */
TILE_FLIPING: mov r8,r7 /* 1) phase de permutaion des registres
mov r7,r0
mov r0,r8
mov r8,r6
mov r6,r1
mov r1,r8
mov r8,r5
mov r5,r2
mov r2,r8
mov r8,r4
mov r4,r3
mov r3,r8
mov r8,#0xff00 /* 2) phase de permutations des pixels à l'intérieur de chaque registre */
orr r8,r8,r8, lsl #16 /* ou l'on se sert d'un masque inittialisé dans r8 à 0xff00ff00
and r9,r8,r0, ror #16 /* permutation des pixels dans les 8 registre grace à une combinaison and,and,orr mixée avec des rotations...
and r0,r8,r0, ror #8
orr r0,r0,r9, lsr #8
and r9,r8,r1, ror #16
and r1,r8,r1, ror #8
orr r1,r1,r9, lsr #8
and r9,r8,r2, ror #16
and r2,r8,r2, ror #8
orr r2,r2,r9, lsr #8
and r9,r8,r3, ror #16
and r3,r8,r3, ror #8
orr r3,r3,r9, lsr #8
and r9,r8,r4, ror #16
and r4,r8,r4, ror #8
orr r4,r4,r9, lsr #8
and r9,r8,r5, ror #16
and r5,r8,r5, ror #8
orr r5,r5,r9, lsr #8
and r9,r8,r6, ror #16
and r6,r8,r6, ror #8
orr r6,r6,r9, lsr #8
and r9,r8,r7, ror #16
and r7,r8,r7, ror #8
orr r7,r7,r9, lsr #8
NO_TILE_FLIPING: mov r8,r7, lsr r12 /* scrolling de la texture dans les registres */
mov r7,r7, lsl r11
orr r7,r7,r6, lsr r12
mov r6,r6, lsl r11
orr r6,r6,r5, lsr r12
mov r5,r5, lsl r11
orr r5,r5,r4, lsr r12
mov r4,r4, lsl r11
orr r4,r4,r3, lsr r12
mov r3,r3, lsl r11
orr r3,r3,r2, lsr r12
mov r2,r2, lsl r11
orr r2,r2,r1, lsr r12
mov r1,r1, lsl r11
orr r1,r1,r0, lsr r12
mov r0,r0, lsl r11
Acte 6
:D Bon, aprés une courte interruption ( joke inside ), suite et fin de cette routine d'affichage de tiles software en mode 4 !!
On a donc à cet stade dans nos registre r0 à r8 notre ligne de texture correctement flippée si nécéssaire et décalée comme il faut pour corrspondre à une adresse de storage multiple de 4.
On sait également que dans la partie gauche du registre r14, on a l'info de notre cas d'affichage , et en fonction de cette info, on va brancher le programme sur une des 15 routines d'affichage qui correspond au cas qui nous intéresse.
On va donc faire une sorte d'équivalent en C à un switch/case, à la différence énorme que l'on ne vas faire AUCUN TEST pour brancher sur la routine voulue, ce qui nous permet d'économiser un temps cpu considérable.
Pour ce faire , on va "jouer" avec le registre r15, ( pc ) qui est notre programme counter. Ce registre contient en permanence l'adresse de la prochaine instruction qui doit etre éxécutée par le cpu. L'astuce consiste donc à modifier ce registre r15 pour lui indiquer "ou aller" à la prochaine instruction. Si vous jetez un coup d'oeil au bout de code ci dessus, on s'apercoit que chacune des 15 routines fait 4 instructions. On sait que chaque instruction arm est codée sur 32 bits, soit 4 octets, donc chaque routine de 4 instruction occupe donc 8*4= 32 octets.
On commence donc à isoler notre info sur la routine à éxécutée contenue dans la partie gauche de r14, et on met dans r9
mov r9,r14, lsr #16
on ajoute ensuite à notre r15, cette valeur multipliée par 16 ( x4 pour le nombre de ligne ASM et x4 pour le fait q'une instruction=4 octets ), r15 contient maintenant l'adresse ou il doit sauter à la prochaine instruction:
add pc,pc,r9, lsl #4
....et voilà le tour est joué, en 2 lignes d'asm, on a fait un branchement conditionné à l'état d'une valeur.... pas besoin de faire une suite de
cmp r14,#0
beq .......
cmp r14,#1
beq......
etc....
...et c'est encore plus rapide que la lecture d'une table de saut , puisqu'il n'ya pas de valeur à lire dans une table.:p
Bien sur cette technique ne s'applique que dans le cas ou chaque routine fait strictement le meme nombre d'instruction. M'enfin meme si c'est pas le cas , y'a toujours moyen de contourner en branchant de cette facon sur une table d'instruction qui renverra elle meme ensuite à la bonne adresse......B)
Voilà, vous pouvez voire enfin que chaque routine individuelle, n'est que le storage de nos 32 pixels, en tenant compte des pixels présents à l'écran pour ne pas écraser les pixels devant le tile.
A la fin de chaque routine de plotage on branche ensuite vers le label END_CASE: ou l'on va mettre à jour toutes nos variable pour afficher la ligne suivante du tile.
On met à jour l'adresse d'écriture dans notre buffer vidéo ( registre r10), on décrémente le nombre de ligne à afficher ( registre r1 ).... et enfin on met à jour également notre adresse de lecture dans notre texture source ( registre r9 )
C'est donc là, qu'on s'occupe du flipping vertical du tile !! en effet, sans flipping, on aurait mis à jour simplement en ajoutant la valeur #256 , car la page de texture fait 256 pixels de large.
Donc, pour descendre d'une ligne dans la texture, on ajoute #256, et si on veut remonter ( flip vertical ), alors on ajoute #-256 .
Et comme on veut pas faire de tests, on a initialisé avant dans le registre r0 , avant d'entrer dans la routine, la valeur adéquate ( #256 ou # -256 )
et on a donc la ligne add r9,r9,r0 ( tu vois Pitt, je te l'avais dit qu'on faisait le flip vertical en une seule ligne :D )
Ci dessous la suite et donc la fin de cette routine d'affichage de tile software .
Voilà donc la fin de ce petit tuto d'initiation à l'assembleur et au mode 4, j'espere qu'il aura pu vous être unpetit peu utile, ou que au moins cela vous aura intéréssé.
a bientot pour de prochaines aventures !!:)
CALCUL_BRANCHEMENT_CASE: mov r9,r14, lsr #16
add pc,pc,r9, lsl #4
nop /* instruction dummy pour occuper 32 bits */
CASE_0: ldr r9,[r10]
orr r0,r0,r9 stmia r10,{r0-r8}
b END_CASE
CASE_1: ldr r9,[r10]
orr r1,r1,r9
stmia r10,{r1-r8}
b END_CASE
CASE_2: ldr r9,[r10]
orr r2,r2,r9
stmia r10,{r2-r8}
b END_CASE
CASE_3: ldr r9,[r10]
orr r3,r3,r9
stmia r10,{r3-r8}
b END_CASE
CASE_4: ldr r9,[r10]
orr r4,r4,r9
stmia r10,{r4-r8}
b END_CASE
CASE_5: ldr r9,[r10]
orr r5,r5,r9
stmia r10,{r5-r8}
b END_CASE
CASE_6: ldr r9,[r10]
orr r6,r6,r9
stmia r10,{r6-r8}
b END_CASE
CASE_7: ldr r9,[r10]
orr r7,r7,r9
stmia r10,{r7-r8}
b END_CASE
CASE_8: ldr r9,[r10]
orr r8,r8,r9
str r8,[r10]
b END_CASE
CASE_9: ldr r9,[r10]
orr r0,r0,r9
stmia r10,{r0-r7}
b END_CASE
CASE_9A: ldr r9,[r10]
orr r0,r0,r9
stmia r10,{r0-r6}
b END_CASE
CASE_10: ldr r9,[r10]
orr r0,r0,r9
stmia r10,{r0-r5}
b END_CASE
CASE_11: ldr r9,[r10]
orr r0,r0,r9
stmia r10,{r0-r4}
b END_CASE
CASE_12: ldr r9,[r10]
orr r0,r0,r9
stmia r10,{r0-r3}
b END_CASE
CASE_13: ldr r9,[r10]
orr r0,r0,r9
stmia r10,{r0-r2}
b END_CASE
CASE_14: ldr r9,[r10]
orr r0,r0,r9
stmia r10,{r0-R1}
b END_CASE
CASE_15: ldr r9,[r10]
orr r0,r0,r9
str r0,[r10]
NOP
END_CASE: ldmfd sp!,{r0,r1,r9}
add r9,r9,r0
add r10,r10,#240 subs r1,r1,#1
bne BOUCLE_TILE
ldmfd sp!,{r15}