ASMtrad CPC

Apprenez l'assembleur Z80

Affichage de sprite

Ca y est nous y sommes, à la fin de ce cours nous pourrons afficher un sprite sans soucis.

Jusque la nous avions vu comment envoyer des données que l'on connait déjà.
Nous avions défini le contenu des octets et on les envoyait vers l'écran.

Ce type de sprite est appelé: autogénéré.
C'est assez rapide et pour certains cas vous verrez que d'avoir appris à le faire vous sera utile un jour.

C'est bien, mais ce n'est pas suffisant.

Imaginons que nous voulions avoir une seule routine de sprite capable d'afficher différents gfx.
On pourrait même juste parler d'animer un sprite, ce qui reviendrait au même puisque nous aurions alors plusieurs gfx à envoyer les uns à la suite des autres...

Si nous utilisons la méthode des sprites autogénéré, il va nous falloir autant de routine que de gfx...
Autant dire que cela va devenir long et pénible. En plus ca prend de la place en RAM...

Notre but deviens donc d'afficher à l'écran un gfx, par exemple conçu avec le célèbre OCP art studio.
Ne vous enflammez pas pour autant nous avons encore quelques petits trucs à voir !!!

LDI / LDIR: l'instruction de transfert d'octets.
Voici une instruction qui va vous être très utile !!!

Que font ces instructions ?

Commençons par LDI:

LDI prend l'octet contenu à l'adr HL et l'envois à l'adresse DE.
Puis DE=DE+1 et HL=HL+1. On appelle cela incrémenter un registre (lui faire +1).

C'est donc parfait pour recopier 1 seul octet quelque part (y compris juste à coté... Lisez bien vous venez d'apprendre à faire un scrolling soft ^^).

LDIR:

C'est la même chose mais pour une chaine d'octet.
LDIR prend l'octet contenu en HL et le recopie en DE sur une longueur BC.
HL et DE sont incrémenté BC fois.
BC est décrémenté jusque 0 (donc après un LDIR BC=0 !!!)

Concrètement et pour que cela soit bien clair:

HL contient l'adresse de vos données
DE contient la destination (facile à retenir: DEstination)
BC contient le nombre d'octets à copier à partir de l'adresse HL. C'est donc la longueur.

Exemple:

                                LD         HL,#4000               ;HL contient l'adresse d'origine
                                LD         DE,#C000               ;DE contient la destination
                                LD         BC,#10                 ;BC contient la longueur
                                LDIR

Après le LDIR:

HL=#4010 ;c'est bien HL+BC
DE=#C010 ;c'est bien DE+BC
BC=0 ;qu'est ce que je vous avais dit...

En fait si vous réfléchissez bien (ne forcez pas trop quand même), vous comprendrez que BC sert de compteur au LDIR.
Ainsi, chaque octet est envoyé 1 par 1 et BC est décrémenté à chaque fois.
Quand BC=0 c'est que c'est fini.

Faites donc bien attention à ce que vous récupérez après un LDIR !!!

Si vous utilisez BC par exemple (exemple bien choisi...) comme compteur pour votre propre routine, alors il faudra le sauvegarder avant de faire un LDIR...

"Sauvegarder... C'est bien gentil il ne nous a jamais expliqué comment on sauvegarde quelque chose..."

Bien sur que si !!! Et le LD (adr),reg8bit ??? C'est pas une sauvegarde ça ???
Oui bon d'accord c'est une sauvegarde mais il y a quand même beaucoup mieux !!!

LA PILE !!! Ou: Comment ranger la vaisselle vous apprend à coder...

Qui n'a jamais pesté quand madame votre mère vous demandait de participer aux taches ménagères, en rangeant la vaisselle ou en mettant la table...

Ou peut être un autre specimen du genre, partageant votre couche aujourd'hui en font peut être de même...

Et bien allez les remercier !!! Ce faisant elles vous ont appris le fonctionnement d'une chose très utile: la Pile !!!

La pile c'est un moyen de sauvegarde. Tellement pratique que le système (votre BASIC adoré entre autre) lui même s'en sert, c'est peu dire !!!

Imaginez une pile d'assiettes.
Prenez la première assiette. Cette assiette est la dernière a avoir été posée !!!
Si vous prenez la suivante, c'est donc l'avant dernière à avoir été posée...

La pile fonctionne de cette façon: La dernière valeur sauvegardée est la première que vous récupèrerez !!!

Pour poser une assiette, il y a l'instruction PUSH.
Pour la reprendre il y a l'instruction POP.

La pile fonctionne avec des registres 16 bits UNIQUEMENT (soulignez moi ca au marqueur fluo !!!)
Nous pouvons donc faire des PUSH HL / PUSH DE / PUSH BC et autres (mais comme je ne vous ai pas parlé des autres registres...).

Plus un autre: AF.
AF ????
"A", vous le connaissez c'est l'accu (accumulateur).
Mais F c'est quoi ???

F c'est le registre des flags !!! Vous vous souvenez les petits drapeaux !!! Débordement; Zéro... C'est dans celui la que ca se passe.

Sauf que F, vous ne pouvez pas écrire dedans et vous ne pouvez pas le lire directement !!!
Seules les instructions qui testent les flags peuvent s'en servir !!!
Enfin presque... puisque si vous le sauvegardez avec un PUSH AF, vous pourrez donc voir le contenu de F même si cela n'a aucun interet/

Vous pouvez sauvegarder A avec la pile en faisant un PUSH AF, c'est ce qu'il faut retenir...

Voyons notre pile:
Chargeons nos registre et sauvegardons avec la pile chaqu'un d'entre eux, en pensant à une pile d'assiette.

                                LD         HL,#4591
                                LD         DE,#5032
                                LD         BC,#2154
                                PUSH       HL
                                PUSH       DE
                                PUSH       BC

Comme je vous l'ai dit, c'est comme une pile d'assiettes et on pose donc les registres les uns au dessus des autres.

Nous avons donc:

pile

Elle est pas jolie ma pile ???

Bref, récupérons une valeur:
POP HL

Et il y a quoi dans HL ??? #2154 !!! Eh oui, car c'est comme une pile d'assiette: on récupère en premier la dernière valeur posée...

Si on voulait récuperer nos valeurs dans les bons registres, au lieu du POP HL il fallait donc faire:

                                POP        BC
                                POP        DE
                                POP        HL

Dans cet ordre là, donc dans le sens inverse qu'ils ont été sauvegardés !!!

Pour le fonctionnement normalement vous devriez avoir compris. Mais vous vous demandez peut être ou est cette fameuse pile ?

Il est évident que c'est quelque part en RAM vu que de toute façon on ne peut mettre quelque chose qu'en RAM...

En fait il existe un registre 16 bits dont je ne vous ai pas parlé: SP
SP c'est le pointeur de pile. C'est lui qui dit à quelle adresse elle se trouve.
Pour le moment contentez vous de savoir ça et ne touchez pas à ce registre !!!
Nous verrons un de ces jours comment fonctionne la pile et ce qu'on peut faire avec. Mais aujourd'hui ce n'est pas le sujet. Donc laissez SP là ou il est ;)

Pourquoi je vous dit ça ? Parce que le système (encore lui) s'en sert. Et donc si vous-y touchez, ça va tout simplement planter.

Nous avons donc vu: le LDIR; la pile...

Vous savez déjà presque tout pour faire votre routine de sprite (vous n'avez tout de même pas cru que j'allais vous donner le truc tout fait ?)

Reste juste une instruction qui vous servira:

DJNZ adr

DJNZ adr c'est une instruction de saut. C'est comme un JP mais un peut spécial.

DJNZ est un saut relatif, c'est à dire qu'il saute de l'adresse ou est l'instruction.
Soit en arrière, soit en avant, et, d'un certain nombre d'octets maximum (-128; +127).
Avec ce type d'instruction pas question donc de sauter à l'autre bout de la RAM.

Mais DJNZ adr a un avantage non négligeable: il utilise B comme compteur !!!

Imaginons:

                                LD         B,10
                LABEL           NOP                               ;un NOP ca fait rien pendant 1 microseconde
                                DJNZ       LABEL

La première fois que le Z80 va rencontrer votre DJNZ, B sera égal à 10.
Si ce n'est pas égal à 0 il va décrémenter B (-1) et sauter à LABEL.
La boucle suivante, il fera la même chose (sauf que B=9) et ainsi de suite jusqu'à ce que B=0.
Quand B sera égal à 0, il ira voir ce qu'il y a après le DJNZ adr (et B=0 pour le coup)

C'est donc très pratique pour faire répéter une séquence un certain nombre de fois, puisqu'il suffira de mettre dans B le nombre de répétitions.

Votre routine d'affichage de sprite

Voila ca y est, on a tout vu. Vous être donc capable d'afficher un sprite et notre chapitre est terminé.
Reste à préciser une chose, puis à vous donner un plan à suivre pour réussir cet exercice, de loin le plus difficile que vous aurez à faire dans votre carrière de codeur !!!

Tout le monde est passé par là et c'est tordu la tête un moment avant d'y arriver.
Mais c'est grâce à cette réfléxion que vous allez comprendre la logique de la programmation en assembleur !!!

Aussi et j'insiste sur ce point: NE TRICHEZ PAS !!!

Si vous trichez; demandez de l'aide etc etc, vous n'irez pas plus loin car vous n'aurez rien compris.

En cas de soucis, vous pourrez m'envoyer vos sources sur mon mail: BDCIRON(at)GMAIL.COM

Pour vos sprites: dessinez sous OCP un truc pas trop grand (pas la peine de faire un machin qui prend la moitié de l'écran).
Allez ensuite dans "window"; puis "define window".
Sélectionnez au mieux votre dessin afin de ne garder que le nécéssaire.
Puis retournez dans "window"; puis "File" et sauvegardez votre gfx.

Vous aurez alors sur votre D7 un fichier .WIN
Chargez alors celui-ci en RAM par exemple en #4000
Sous BASIC:
10 MEMORY &4000-1:load"sprite.win",&4000

Notez que les données sont linéaires !!!
Même si votre gfx est un carré, celui-ci est stocké de façon à ce que tous les octets se suivent.
Ainsi la ligne 2 en RAM est après la ligne 1... C'est linéaire !!!

La marche à suivre pour réaliser votre routine:

  1. Commencez par afficher 1 ligne avec LDIR (ou LDI si vous voulez, peu importe.).
  2. Ajoutez ensuite le calcul de la ligne inférieure.
  3. Faites une boucle pour que tout ceci se répète le nombre de ligne qu'il faut !!!

N'oubliez pas le RET en fin de routine.

Faites attentions à vos registres. DJNZ utilise B mais LDIR le modifie...
LDIR incrémente certains registres, faites y attention, vous avez besoin d'un en particulier à sauvegarder avant le LDIR.

Bon courage à tous, c'est un dur moment à passer.

Dernière précision: En mode 0, 1 octet=2 pixels. En mode 1, 1 Octet=4 pixel. En mode 2, 1 octet=8 pixel...