Revenir au plan du site

Animer un sprite soft


Une petite modification de la routine de sprite qui prend en entrée l'adresse du sprite dans DE. En permutant à la fin HL et DE, on remet dans DE l'adresse du sprite suivant. On peut ainsi réaliser plusieurs appels successifs à la routine, sans devoir mettre de côté le pointeur de données ou calculer l'adresse de l'étape suivante.

Téléchargez [cette planche de sprites], puis assemblez ce code source (vous avez le droit de le lire, il est commenté de partout).
BUILDSNA : BANKSET 0
ORG #100
RUN #100

ld bc,#7F00+%10001100+%00 : out (c),c ; MODE 0
; on règle quelques couleurs pour notre sprite
ld bc,#7F00 : out (c),c : ld a,#54 : out (c),a
ld bc,#7F01 : out (c),c : ld a,#44 : out (c),a
ld bc,#7F02 : out (c),c : ld a,#5C : out (c),a
ld bc,#7F03 : out (c),c : ld a,#5E : out (c),a
ld bc,#7F04 : out (c),c : ld a,#4E : out (c),a
ld bc,#7F05 : out (c),c : ld a,#43 : out (c),a
ld bc,#7F06 : out (c),c : ld a,#4B : out (c),a

ld iy,listePositions
debutSequence
ld de,donnees_sprite
ld a,20 ; notre compteur de sprites
deplacerSprite
exa ; on met le compteur dans A'
call waitVBL
ld l,(iy+0) : inc yl ; en n'incrémentant que le poids faible, on revient tout seul
; au départ au bout de 256 valeurs
ld bc,60 ; X=60
ld h,0 ; Y=(iy+0)
ld xl,21
ld xh,72
call AfficheSprite
exa ; on récupère notre compteur dans A
dec a
jr nz,deplacerSprite
jr debutSequence ; on reprend la séquence de zéro au bout de 20 étapes

align 256
listePositions
angle=0
repeat 256
defb sin(angle)*20+20+70
angle=angle+360/128 ; 2 périodes histoires d'aller plus vite
rend

waitVBL
ld b,#F5
.loop in a,(c) : rra : jr nc,.loop
ret


AfficheSprite
; BC=coordonnée X (0-319)
; HL=coordonnée Y (0-199)
; DE=adresse des données du sprite
; XL=largeur du sprite en octets
; XH=hauteur du sprite en nombre de lignes
push de ; on met DE dans la pile car le calcul d'adresse s'en sert
call CalculeAdressePixel
pop de ; et on récupère DE
; HL=destination écran
; DE=toujours l'adresse source des données
ex hl,de ; on permute source et destination pour être utilisées
; avec l'instruction LDIR

.afficheLignes
ld b,0
ld c,xl ; chargeur la largeur d'une ligne
push de ; on met l'adresse de début de la ligne de côté
ldir ; copier la ligne de HL vers DE sur BC octets
pop de ; on récupère l'adresse du début de ligne
; et on calcule le passage à la ligne suivante
ex hl,de ; on permute HL et DE pour pouvoir faire des additions
ld bc,#800 : add hl,bc : jr nc,.dansLeBloc
ld bc,80-#4000 : add hl,bc ; changement de bloc!
.dansLeBloc
ex hl,de ; on permute à nouveau pour retrouver notre DE une ligne plus bas
dec xh ; notre compteur de lignes
jr nz,.afficheLignes
ex hl,de ; on renvoie dans DE l'adresse du sprite suivant
ret
;-------------------
CalculeAdressePixel
; BC=coordonnée X (0-159)
; HL=coordonnée Y (0-199)
; adresse de la ligne dans HL en résultat
ld de,tableau
add hl,hl ; adresses 16 bits, il faut indexer de 2 en 2
add hl,de
ld a,(hl) : inc hl
ld h,(hl) : ld l,a
srl bc : add hl,bc
ret
;-------------------
adresse_ecran=#C000
largeur_ecran=80
tableau
repeat 25
repeat 8
defw adresse_ecran
adresse_ecran+=#800
rend
adresse_ecran+=largeur_ecran
adresse_ecran-=#4000
rend

donnees_sprite incbin 'quiPiqueSequence.bin'

Ça va trop vite!

Bienvenue dans le monde de la programmation pure hard où les routines non optimisées doivent être ralenties ^_^

Mais pourtant, nous attendons bien la VBL? Oui, c'est le cas, mais la planche de sprites n'en contient qu'une vingtaine, ce qui fait qu'à 50Hz, autrement dit 50 balayages par seconde, c'est trop rapide.

L'idée première serait d'appeler deux ou trois fois la routine d'attente VBL c'est ça? Mais il y a un hic! Une VBL dure quelques milliers de NOP, si on redemande à attendre la VBL juste après la première, on sera toujours dans la même VBL. C'est assez facile à vérifier en triplant la ligne, il ne se passe rien c'est toujours aussi rapide.

La solution est d'abord de vérifier que nous ne sommes pas dans une VBL avant d'attendre la VBL. Nous devons attendre le début de la VBL, pas autre chose.
BUILDSNA : BANKSET 0
ORG #100
RUN #100

ld bc,#7F00+%10001100+%00 : out (c),c ; MODE 0
; on règle quelques couleurs pour notre sprite
ld bc,#7F00 : out (c),c : ld a,#54 : out (c),a
ld bc,#7F01 : out (c),c : ld a,#44 : out (c),a
ld bc,#7F02 : out (c),c : ld a,#5C : out (c),a
ld bc,#7F03 : out (c),c : ld a,#5E : out (c),a
ld bc,#7F04 : out (c),c : ld a,#4E : out (c),a
ld bc,#7F05 : out (c),c : ld a,#43 : out (c),a
ld bc,#7F06 : out (c),c : ld a,#4B : out (c),a

ld iy,listePositions
debutSequence
ld de,donnees_sprite
ld a,20 ; notre compteur de sprites
deplacerSprite
exa ; on met le compteur dans A'
call waitVBLstart ; 50Hz
call waitVBLstart ; 25Hz
call waitVBLstart ; 17Hz
call waitVBLstart ; 12Hz nous afficherons 12 images par seconde
ld l,(iy+0) : inc yl ; en n'incrémentant que le poids faible, on revient tout seul
; au départ au bout de 256 valeurs
ld bc,60 ; X=60
ld h,0 ; Y=(iy+0)
ld xl,21
ld xh,72
call AfficheSprite
exa ; on récupère notre compteur dans A
dec a
jr nz,deplacerSprite
jr debutSequence ; on reprend la séquence de zéro au bout de 20 étapes

align 256
listePositions
angle=0
repeat 256
defb sin(angle)*20+20+70
angle=angle+360/128 ; 2 périodes histoires d'aller plus vite
rend

waitVBLstart
ld b,#F5
.loop in a,(c) : rra : jr c,.loop ; attendre l'absence de VBL
waitVBL
ld b,#F5
.loop in a,(c) : rra : jr nc,.loop ; attendre la VBL
ret


AfficheSprite
; BC=coordonnée X (0-319)
; HL=coordonnée Y (0-199)
; DE=adresse des données du sprite
; XL=largeur du sprite en octets
; XH=hauteur du sprite en nombre de lignes
push de ; on met DE dans la pile car le calcul d'adresse s'en sert
call CalculeAdressePixel
pop de ; et on récupère DE
; HL=destination écran
; DE=toujours l'adresse source des données
ex hl,de ; on permute source et destination pour être utilisées
; avec l'instruction LDIR

.afficheLignes
ld b,0
ld c,xl ; chargeur la largeur d'une ligne
push de ; on met l'adresse de début de la ligne de côté
ldir ; copier la ligne de HL vers DE sur BC octets
pop de ; on récupère l'adresse du début de ligne
; et on calcule le passage à la ligne suivante
ex hl,de ; on permute HL et DE pour pouvoir faire des additions
ld bc,#800 : add hl,bc : jr nc,.dansLeBloc
ld bc,80-#4000 : add hl,bc ; changement de bloc!
.dansLeBloc
ex hl,de ; on permute à nouveau pour retrouver notre DE une ligne plus bas
dec xh ; notre compteur de lignes
jr nz,.afficheLignes
ex hl,de ; on renvoie dans DE l'adresse du sprite suivant
ret
;-------------------
CalculeAdressePixel
; BC=coordonnée X (0-159)
; HL=coordonnée Y (0-199)
; adresse de la ligne dans HL en résultat
ld de,tableau
add hl,hl ; adresses 16 bits, il faut indexer de 2 en 2
add hl,de
ld a,(hl) : inc hl
ld h,(hl) : ld l,a
srl bc : add hl,bc
ret
;-------------------
adresse_ecran=#C000
largeur_ecran=80
tableau
repeat 25
repeat 8
defw adresse_ecran
adresse_ecran+=#800
rend
adresse_ecran+=largeur_ecran
adresse_ecran-=#4000
rend

donnees_sprite incbin 'quiPiqueSequence.bin'