Revenir au plan du site

Animation avec les sprites hard

Comme évoqué dans le [ premier article sur les sprites hard ], les sprites hards possèdent leur propre mémoire qu'il faut écrire à chaque fois qu'on veut en changer le contenu. Ce ne sont pas des sprites hard "traditionnels" avec lesquels il n'y aurait que l'adresse des données à changer.

En conséquence de quoi, voyons un peu comment nous pouvons en changer le contenu :

Copie au LDIR

La copie au LDIR est la plus simple. Relativement rapide, cela prendra 1544 nops pour la copie d'un seul sprite. Avec cette méthode, il n'est pas possible de changer tous les sprites hard à l'écran dans la frame (50Hz).

Est-ce grave d'être lent? Pas obligatoirement, cette méthode peut être utilisée pour une première initialisation.
ld hl,source_de_donnees ; 3 nops
ld de,#4000 ; 3 nops
ld bc,256 ; 3 nops
ldir ; 255 x 6 + 5 nops

Copie au LDI

Même technique mais en déroulant le LDIR, on gagne quelques nops, c'est pas foufou
ld hl,source_de_donnees ; 3 nops
ld de,#4000 ; 3 nops
ld bc,256 ; 3 nops
copieSprite
ldi 32
jp p,copieSprite

Bien entendu, on ne va pas dérouler pour chaque copie, on se fera une petite fonction de copie dédiée à copier les 256 octets d'un sprite

Copie avec décompression à la volée

Dans les techniques lentes, il est intéressant de grouper les pixels par deux. En effet, les données d'un sprite n'ont que 16 valeurs possibles (transparence + 15 couleurs). Ces valeurs n'occupent que 4 bits, on peut donc avoir les informations de deux pixels dans un octet. L'outil convgeneric va générer des groupes de pixels en mettant le premier pixel (celui de gauche) dans les 4 bits du bas et le deuxième pixel (celui de droite) dans les 4 bits du haut.

Il faut savoir que l'ASIC ne décode pas toujours tous les bits. Dans le cas des sprites hard, il va IGNORER les 4 bits du haut de l'octet. On peut écrire ce qu'on veut.
ld hl,#4000 ; adresse du premier pixel du premier sprite hard
ld (hl),#55 ; on envoie #55 mais l'ASIC ne reçoit que #05
ld (hl),#F5 ; tout pareil, il faut considérer que la valeur passe par un filtre de type AND #F

Comme l'écriture dans l'ASIC va ignorer les bits superflux, la première écriture se fait normalement, on envoit l'octet directement sans utiliser de masque.

Pour envoyer le deuxième octet, il faut décaler les bits, afin que les 4 bits du haut se retrouvent... ...en bas

Vous pouvez jeter un oeil au [ schéma ] sur les décalages de la rubrique sur les instructions de rotations et décalages.

Nous avons plusieurs candidats pour obtenir le résultat que l'on veut : SRL, SRA, RRCA, RLCA.

Le SRL est le plus intuitif car il va vider la partie haute tout en décalant vers le bas. Par contre, l'instruction est lente, le coût total est de 8 nops.

Le RRCA et le RLCA vont faire la ronde des bits. Comme ces instructions ne font que 1 NOP, l'opération est effectuée en 4 NOP seulement.
01101111 ; valeur de départ
11011110 ; après 1x RLCA
10111101 ; après 2x RLCA
01111011 ; après 3x RLCA
11110110 ; après 4x RLCA

Et dans l'autre sens c'est pareil, mais différemment.
01101111 ; valeur de départ
10110111 ; après 1x RRCA
11011011 ; après 2x RRCA
11101101 ; après 3x RRCA
11110110 ; après 4x RRCA

Voici le détail de la routine de copie à partir de pixels groupés.
ld hl,source_de_donnees ; 3 nops
ld de,#4000 ; 3 nops
decrunchSprite
ld a,(hl) : inc hl ; on lit deux pixels groupés qu'on met dans A
ld (de),a : inc e ; copier le premier pixel, les 4 bits du haut sont ignorés
rrca : rrca : rrca : rrca ; faire tourner l'octet source de 4 bits sur lui même
ld (de),a : inc e ; écrire le deuxième pixel
jr nz,decrunchSprite ;

On descend à 2181 nops mais les données de nos sprites sont deux fois plus petites! À retenir ;)

Avec une petite table de conversion, contenant l'octet pré-shifté de 4, et en alignant les données sources sur 128, on peut gratter un peu (256 nops). Et si on déroule un peu, on gagne encore un peu plus de 300 nops.

La routine reste lente comme une copie au LDIR mais nos données sont deux fois plus petites, c'est excellent pour un usage peu exigeant.
startingindex 0
align 256
tableDecale4
repeat 256,x
defb x>>4
rend

decrunchSprite
; HL = source alignée sur 128 octets
; DE = destination dans l'ASIC
ld b,hi(tableDecale4) ; poids fort de la table de conversion dans B
.loop
repeat 8
ld a,(hl) : inc l : ld (de),a : inc e
ld c,a ; on copie la valeur du groupe de pixel dans C pour indexer dans la table
ld a,(bc) ; on va lire la valeur de notre table, 4 shifts d'un coup pré-calculés
ld (de),a : inc e
rend
jr nz,.loop
ret

Rendez-vous dans l'[ article suivant ] pour voir une méthode performante et souvent adaptée au jeu.