Afficher un sprite à l'écran ...
Par Bebert
Le 12 mai 2001

 

 



I à qui s'adresse cette doc ?

cette doc s'adresse à ceux qui ont des notions de bases (mais vraiment
la base de la base !) mais qui ne savent pas programmer en asm c'est
ultra détaillé , je pense que tut le monde peut comprendre (même moi ,
j'ai compris ce que j'ai écrit !)
Mais s'il y a un truc que vous ne comprenez pas , demandez à quelqu'un
qui s'y connaît , ou écrivez moi (superbebert2000@yahoo.fr)



II Qu'est ce qu'un sprite ?

Un sprite est un objet graphique de petite taille qu'on affiche à
l'écran , et qui est "fait" pour bouger.
Dans cette doc , on expliquera seulement comment afficher un sprite à
un endroit précis de l'écran.



III Ce qu'il faut absolument savoir

Je pars du principe que vous avez lu VOYAGE AU CENTRE DE LA HP48 G/GX
de la page 69 à la page 129 .
Si vous ne l'avez pas fait , faudrait le faire (vous pouvez télécharger
VCHP sur http://www.courbis.com/voyage48g.html
Si vous ne voulez pas le lire , bon , bah c pas grave , mais il faut
quand même que vous sachiez ce que c'est un registre , un champ , un
pointeur , une adresse , le champ P , le prologue d'un objet etc ...
Autre chose : l'écran de la calculatrice ne peut recevoir des données
que par "paquets" de 4 bits , la largeur de votre sprite doit donc être
un multiple de 4 , si ce n'est pas le cas, il faut ajouter des colonnes
de pixels blancs au début ou à la fin du grob.

Quelques notations que j'utiliserais :
x : abscisse
y : ordonnée
@ : adresse de l'écran de la pile
h : à la fin des nombres en base hexadécimal (ex : 34h )
d : à la fin des nombres en base décimal (ex : 131d)

Dans cette documentation , je prendrais comme exemple un grob de 8*16
pixels.



IV Le plat de résistance : Choucroute au plomb

Voici le source du programme commenté :

GOSBVL 0679B on sauve les registres
LA 00001
LC 00001
on stocke 00001 dans les registres A
et C ; A est l'abscisse x du sprite
( -1<x<131 ) , C est l'ordonnée y
( -1<y<64 ) , x et y sont des
naturels
R0=A A
R1=C A
On sauvegarde A et C dans R0 et R1
pour les avoir toujours sous la main
en cas de besoin
D0=8068D
C=DAT0 A
à l'adresse 8068D , y'a l'adresse du
grob qu'est affiché sur la pile
D0 pointe vers 8068D C=DAT0 va
chercher ce qu'il y a en 8068D
(l'adresse du grob de la pile )
RSTK=C RSTK , c une sorte de pile , pratique
pour sauver TEMPORAIREMENT des données
ici , on sauve l'adresse de l'écran...
C=R1 A
C=C+C A 
A=C A
CSL A 
A=A+C A
C=RSTK
C=C+A A
A=R0 A
B=A P
ASRB A
ASRB A
C=C+A A
D0=C
on met y dans C
On fait C*2 (décalage d'un bit) C=y*2
On copie y*2 dans A
On fait A*10h=A*16d donc A=y*20h=y*32h
On fait A=A+C (A=y*2+y*32=y*34 !!! )
On met dans C l'adresse @ de l'écran
note (ça enlève l'adresse de RSTK)
avec ça , C=@+y*34
A=x
B=A=x
A=A/2=x/2 (on décale d'un bit ...)
même chose : A=A/2=x/2/2=x/4
C=@+y*34 + x/4
maintenant , D0 pointe vers :
@ + y*34 + x/4


Bah voila !!!
Maintenant qu'on a fait tout ça , on peut quand même se poser une
question : Pourquoi faire pointer D0 (qui est un pointeur) vers
@ + y*34 + x/4 ???
Eh bien , c'est une bonne question !
Vous avez le choix entre quatre réponses :
-réponse A : la grande Muraille de Berlin
-réponse B : 3.14159...
-réponse C : hum humm
-réponse D : eh bien , c très compliqué , mais je vais quand même
expliquer : que veux t on faire ?
ON VEUT AFFICHER UN PETIT GROB A L'ECRAN CHEF ! l'écran de la pile,
c un grob normal (une suite de 0 et de 1 ; 0=blanc , 1=noir ) ...
Or , l'écran fait 131 * 64 pixels , même plus : 136 * 64 car il ne
fonctionne qu'avec des paquets de 8 bits (des octets) , donc , au bout
de chaque ligne , y'a 5 pixels qu'on voit jamais , mais ils sont bien
là . Donc , pour afficher un image à l'écran , il faut 136*64=8704 bits
(soit 2176 quartets ; 1 quartet = 4 bits )
c compliqué ! Mais comment fait la calculatrice pour affiche run grob à
l'écran ? eh bien , d'abord , elle va voir à l'adresse 8068D , à cette
adresse , y'a l'adresse du grob qu'on veut afficher : la caltos lit
l'adresse et va voir ce qu'il y a à cet endroit : ce sont les données
du grob à afficher , arrivée là , elle se prend pas la tête pendant 3
heures : elle lit les données si le 1er bit est 1 , elle allume le
premier pixel de l'écran , si c'est 2 (euh non ! , si c ZERO ) , elle
l'éteint et ainsi de suite pour chaque pixel ...
Bon , avec tout ça , comment va t on faire pour afficher un petit grob
sur la pile ? Bah , le grob de l'écran , il est quelque part dans la
mémoire , son adresse , on la récupère (voire plus haut ) : c'est @ ,
et on va remplacer les bits du grob de l'écran par les bits de notre
grob qu'on veut afficher ... , mais comme notre grob est supposé plus
petit que l'écran , faut pas fouttre le bordel et remplacer tous les
bits, il faut remplacer les BONS bits (ceux qu'on été bénis par Dieu !)
pour trouver les bons bits , c simple ! Dans un grob , les bits de
chaque ligne sont mis les uns au bout des autres (ex: pour avoir les
bits de la ligne 1 , on utilise un nombre entre 0 et 136 , pour la
ligne 2 , entre 137 et 2*136=272 ...) Nous , on veut la ligne y , alors
on fait 34*y (34=136*4 , car tout se fait avec des quartets : un
pointeur pointe sur un quartet précis , pas sur un bit ) , avec ça, on
la ligne qu'il faut (l'ordonnée) , pour avoir le bon quartet sur la
ligne , on ajoute x/4 , voila , on l'adresse du quartet à partir duquel
on doit remplacer des bits (i.e. allumer les pixels correctement pour
que ça fasse le dessin qu'on veut !)
Ouf ! 34 lignes !!! Je crois que je me suis enflammé ...
Bon , la réponse D est un peu plus longue que les autres , j'espère que
cela n'influencera pas vostre choix :)

Maintenant , y'a encore un autre problème (pfffffff :( )
On connaît le quartet à partir duquel il faut remplacer des bits , mais
peut être qu'on veut remplacer des bits à partir du 3ème bit du quartet,
et le pointeur pointe sur le début du quartet (le 1er bit) , comment
faire pour pouvoir recopier à partir du 3ème , ou du 2ème ... bit ???
eh bien , c'est simple : si on veut recopier à partir de 3ème bit , par
exemple , on va quand même recopier à partir du premier bit , mais on
va décaler les quartets qu'on veut recopier de 2 bits , ainsi , les 2
premiers bits seront des 0 , et à partir du 3ème , y'aura les bons bits
de notre grob !!!!
Bon , maintenant , on va essayer de savoir de combien de bits il faut
décaler : =>

*DECALAGE:
LC 3
C=C&B P
Un nom de label qui sert à rien
On met 3 dans C , 3 = 0011 en binaire
P=0, par défaut
puis on fait le et logique :
1&1=& 1&0=0 0&1=0 0&0=0
dans B , on avait x on fait x&3
si le dernier quartet de x est :
0000 alors x&3=0000 =>pas de décalage
0001 alors x&3=0001 => on décale de 1
0010 alors x&3=0010 => on décale de 2
0011 alors x&3=0011 => on décale de 3
logique ! le & , ici , nous permet de
récupérer les 2 bits de poids faible
de B , qui correspondent à
l' "abscisse" dans le quartet , donc
au décalage en bit
P=C 0
C=P 15
% on stocke le décalage dans P
C=P 15 % puis on le stocke dans le dernier
% quartet de C

Désormais , on connait l'adresse du quartet à partir duquel il faut
modifier le grob , et le nombre de décalages d'un bit qu'il faut faire
pour afficher l'image au pixel près . On a tout ce qu'il nous faut pour
afficher notre sprite ... Tout ? NOOOOOOOON , bien sur , il nous faut
aussi notre sprite ...
Bon , on va considérer que vous avez déjà un grob de petite taille
stockée dans une variable globale . Supposons que le nom de cette
variable est : DESSINDUSPRITE :)
Bon , maintenant , fa ut qu'on intègre les données du sprite dans le
prog en asm ... c facile

*ADRESSE.DONNEES.SPRITE
GOSUB DATAS.SPRITE
un label qui sert à rien
on demande au prog daller au
label qui s'appelle DATAS.SPRITE
note:ça met l'adresse du truc qu'y'a
après le gosub dans RSTK
$4040E0A0E111F111F191F11
1FF1FE322E323E764E7E7202
0202020202020E1E1
*DATAS.SPRITE
C=RSTK
ici , ce sont les données du sprite
en quartets (sans prologue)

le label DATAS.SPRITE
on rappelle l'adresse des données du
sprite
D1=C et D1 pointe vers cette adresse

 

Ce coup là , on a tout ce qu'il nous faut ; On va pouvoir lancer la
grosse machine de Guerre : l'algorithme de copiage des données !

Bon , jusqu'ici , c'était facile , maintenant , on va passer aux choses
sérieuses ! :)
Bon , je fais quoi , je parle tout de suite des pixels transparents ,
ou je fais un truc simple avec des pixels blancs et des pixels noirs ?
Bon ,on va commencer par un truc simple

Tout d'abord , on va copier le grob ligne par ligne (car , comme notre
sprite est plus petit que l'écran , entre deux lignes , il faut faire
un saut de 34h ( la "longueur" d'une ligne )

P=0 P va servir de compteur de ligne
inconvénient : on ne peux pas
afficher de sprite de plus de 16
lignes , si on veut afficher un grob
de plus de 16 lignes , il faut
utiliser un champ d'un registre
comme compteur ...
*COPY.LINE

A=0 A
un nom de label qui sert à qqch (le
début d'une boucle ... )
pour virer tout les déchets de A
A=DAT1 B dans A champ , on on copie les
données du sprite

Bon, là , il risque d'y avoir quelques problèmes !
le champ X fait 3 quartets (=12 bits=12 pixels), et dans cet exemple
précis, j'ai décidé d'afficher un sprite de 8*16 , pourquoi ai-je
décidé d'utiliser un champ plus grand ???
Et bien , c simple : une adresse sert à désigner un quartet bien précis
, donc quand on écrit à un adresse , on écrit au début d'un quartet ,
de plus , un quartet ne code pas un pixel , mais 4
(logique 1 quartet = 4 bit ...)
Donc , si on écrit à l'adresse de l'écran , ça écrira au début d'un
"groupe" de quatre pixels ... bon , tout ça je l'ai déjà expliqué plus
haut , donc on va décaler tous les bits dans le champ considéré (ici:X)
Car si on décale le champ B (2 quartets) vers la gauche , comme notre
image fait 2 quartets de large, les bits de poids faible seront décalés,
et les bits de poids fort seront irrémédiablement perdus
Alors que si on décale les bits de notre grob dans le champ X (=12 bits)
notre ligne de grob (8 bits) ne sera pas amputée , (car le décalage
maximum est de 3 bits et 8+3 < 12 ...)
Bon , vous avez compris , je pense

A retenir : toujours utiliser un champ plus grand pour faire les
décalages.

Rem: si vous ne souhaitez pas afficher au pixel près , et si vous
souhaitez écrire "tous les 4 pixels" , bien sur , vous n'estes pas
obligé de faire de décalage, et le programme est beaucoup plus
simple !!!
:)

D1=D1+2


A=C S
On copie ligne par ligne ,ici , D1
pointe sur la 1ère ligne de 8 pixels
On le fait pointer sur la ligne suiv.
On met la valeur du décalage dans C.S
A=A-1 S
GOC DECALAGE.OK




A=A+A X
On décrément A.S
si A.S=0 => pas de décalage => la
carry passe à 1 avec la
décrémentation et on va à DECALAGE.OK
sinon , As est décrémenté , et on
exécute c'qu'y après l'GOC DECALA...
On décale de 1 bit vers la gauche
A=A-1 S
GOC DECALAGE.OK
A=A+A X
A=A-1 S
GOC DECALAGE.OK
A=A+A X
On refait ceci encore 2 fois
Au cas où il faudrait faire 3
A=A+A X % décalages ...

ça y est ! la ligne de pixels a été décalée le nombre de fois qu'il
faut, maintenant , on peut commencer le recopiage sur l'écran de la
pile

Rem : les décalages ont un inconvénient : cela créeras une ou plusieurs
colonne blanche à gauche du sprite , mais on verras plus tard comment
empêcher cela !!!

*DECALAGE.OK
DAT0=A X
Bon , voila , on peut recopier
D0 pointe vers l'adresse où il faut
recopier , et on recopie (enfin)
D0=D0+34 On passe à la ligne suivante
dans le grob de la pile :
une ligne =136 pixels = 34 quartets
P=P+1 On incrémente P , si P passe à 0, (
on vient alors de recopier la
denière ligne ) alors la carry passe à 1
GONC COPY.LINE Si la carry n'est pas à 1 (i.e il
reste des lignes à copier , alors on
retourne à COPY.LINE pour copier
la ligne suivante

 

Maintenant , on peut conclure , mais si on fait comme cela , le grob
qu'on va afficher va recouvrir le grob qui est affiché sur la pile
(d'autant plus qu'il y a les décalages) et peut être qu'on ne veut pas
que certains pixels soient recouverts ...
Illustration:

l'écran , avec un grob de décor déjà affiché :
************************
*                                            *
*                                            *
*                                            *
*                                            *
************************
*                                            *
*                                            *
************************

Le grob qu'on veut afficher ; un bonhomme :
***
*  *
***
  *
  *
  **

On affiche le bonhomme (sans décalage)
************************
*                                            *
*                ***                      *
*                *  *                      *
*                ***                      *
*********  *  ************
*                  *                        *
*                  ***                    *
************************
Deux pixels du décor ont été effacé !!!

Pire : avec un décalage de 3 :
************************
*                                            *
*                ***                      *
*                *  *                      *
*                ***                      *
******        *  ************
*                  *                        *
*                  ***                    *
************************
!!!!


Bon , heureusement , quelqu'un a trouvé une solution : les pixels
transparents !!!!!
C'est un technique très simple :
D'abord , il faut faire un "double" grob du bonhomme avec d'abord
l'ombre du bonhomme , et ensuite le dessin du bonhomme ; c'est un grob
en double largeur
exemple:
******
****  *
******
*      *
*      *
**    **

Attention: le dessin du bonhomme doit avoir une largeur multiple de 8 ,
donc , le grob avec l'ombre sera un multiple de 16 , si votre dessin
n'a pas une largeur multiple de 4 , complétez avec des pixels blancs
dans le dessin, et AUSSI dans l'ombre
exemple :
***       ***     |
***       *  *     |
***       ***     |
  *           *       | 
  *           *       |
  **         **     |
voila , je vous ai mis une petite limite ( | ) à droite


Bon ,en suite , on copie d'abord l'ombre dans B , puis le dessin
dans A , on fait le décalage pour les deux champs et au label
DECALAGE.OK , on ne se contente pas de copier de données.
On copie dans C les données du décor (qui est affiché sur l'écran de la
pile ) , on inverse B (l'ombre) ; on fait C=C&B , donc les pixels noirs
dans l'ombre et noirs sur le décor seront notés 0 , les pixels blancs
dans le décor seront notés 0 , les pixels blancs dans l'ombre
(le "tour" du bot) et noirs dans le décor seront notés 1
Ensuite : C=C!A (C=C ou A)
-Les pixels blancs dans l'ombre , noirs dans le décor et blancs sur
le dessin du bot seront notés 1 donc noirs ,c ce qu'on voulais, si si !
-Les pixels noirs sur le dessin du bot seront noirs de toute façon
-Les pixels noirs dans l'ombre , noirs dans le décor et blancs sur le
dessin du bot seront notés 0 donc blancs , c ce qu'on voulais aussi,
croyez moi !

Bref , ça marche !!!
Bon , c un peu difficile à voir comme ça en le lisant , mais vous pouvez
faire des dessin sur du papier (oui , ça existe encore !) pour voir un
peu ce que ça donne.
Ensuite on copie C sur l'écran de la pile , et là , ça marche !!!

*COPY.LINE
A=0 A
C=0 A
C=DAT1 B
B=C A
D1=D1+2

On fait le ménage ...


On copie l'ombre
On la met dans B
On passe aux données du dessin du bot
qui sont juste "à côté" de celles
de l'ombre
A=DAT1 B
D1=D1+2
A=C S
A=A-1 S
GOC DECALAGE.OK
A=A+A X
B=B+B X
A=A-1 S
GOC DECALAGE.OK
A=A+A X
B=B+B X
A=A-1 S
GOC DECALAGE.OK
A=A+A X
B=B+B X
on les met dans A
on passe à la ligne suivante





On fait les décalages
A=A+A X % Pour A et B




...
*DECALAGE.OK
C=DAT0 X
B=-B-1 X
C=C&B X
C=C!A X
DAT0=C X

On fait les opérations logiques
sus-décrites


D0=D0+34

P=P+1
GONC COPY.LINE
on passe à la ligne suivante (dans
le grob de la pile)
On teste P
On boucle ...

 

Maintenant que le grob est copié (ce qui se fait très rapidement) , on
peut mettre une boucle pour qu'on puisse le voir pendant quelques
secondes , ici , j'ai fait un truc simple , mais on peut aussi faire
une boucle infinie , avec un test de touche pour quitter le programme

LA FFFFF
{A=A-1 A

UPNC}
on met FFFFFh=1048575d dans A champ A
on décrémente A (si A=0 , la carry
est armée )
on retourne au début de l'accollade
(UP) si la carry n'est pas armée (NC)
GOVLNG 05143 enfin ,on restaure les registres
comme ils étaient avant que le prog
soit exécuté , si on le fait pas , la
calculatrice est "déboussolée" et
ça plante :(

 

V Conclusion

Les explications sont super détaillées
Mais s'il y a un truc que vous ne comprenez pas , vous pouvez m'écrire
et je répondrais à vos question en 2 semaines au plus
mon email : superbebert2000@yahoo.fr
La semaine , je n'ai pas accès à Internet , mais vous pouvez aller sur
un forum ou sur un chat
Je vous conseille un site : www.hp-sources.com


Voila , c'était ma première doc , écrivez moi pour me dire ce que vous
en pensez !

@+