|
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 !
@+
|