Revenir au plan du site

Sauvegarder l'arrière plan pour le restituer plus tard


La méthode du XOR est pratique et plutôt rapide mais c'est moche dès lors qu'il y a un arrière plan. Il faut donc choisir une méthode pour restituer le fond après le passage d'un sprite. Une des techniques est de sauvegarder dans un tampon (de la taille du sprite), le fond de l'écran à l'endroit même où l'on va afficher le sprite.

Pour sauvegarder le fond, nous calculerons l'adresse écran et au lieu de lire les données puis écrire dans l'écran, nous ferons l'inverse. Lire l'écran et écrire dans le tampon.

La restitution du fond pourra se faire avec la routine de sprite classique, en lui donnant l'adresse du tampon de sauvegarde plutôt que celle du sprite. Quand on veut afficher le hibou, c'est toujours la même valeur qui est envoyée, sinon on met dans DE le tampon et on appelle RestituerTampon. C'est vraiment histoire de raccourcir un peu le source.
AfficherHibou
ld de,donnees_sprite
RestituerTampon ; nécessite DE pré-positionné
ld hl,80 ; Y=80 pour être à peu prêt centré
ld xl,9 : ld xh,36 ; dimensions du sprite largeur/lignes
call AfficheSprite
ret


Au final, nous voyons que chaque amélioration se paie (ici au prix fort) car si avec la méthode XOR, on ne passait que deux fois au même endroit. Ici, il faut restituer puis sauvegarder et enfin afficher, nous faisons maintenant 3 passages au même endroit.
; restitution, sauvegarde, affichage
ld b,#80 : ld a,(restorex8) : cp 255 : jr z,.first8 : ld c,a : ld de,backup8 : call RestituerTampon : .first8
ld b,#80 : ld a,(positionx) : ld de,backup8 : call SauvegarderPortionEcran
ld b,#80 : ld a,(positionx) : ld (restorex8),a : ld c,a : call AfficherHibou

Toujours pour raccourcir un peu notre source, au lieu de sauvegarder/restituer DE avec PUSH DE et POP DE avant le calcul de l'adresse écran, on va changer l'addition dans la routine
CalculeAdressePixel
ld de,tableau
add hl,hl ; adresses 16 bits, il faut indexer de 2 en 2
add hl,de

Le code d'addition est plus lent mais sans avoir à faire PUSH/POP, on gagne du temps machine au global ;)
CalculeAdressePixel
add hl,hl ; adresses 16 bits, il faut indexer de 2 en 2
ld a,lo(tableau) : add l : ld l,a
ld a,h : adc hi(tableau): ld h,a


Source d'exemple, nous utilisons [hibou] et [forêt]
BUILDSNA : BANKSET 0
ORG #38 : EI : RET
ORG #100 : RUN #100

ld sp,#100 : ei
ld bc,#7F00+%10001100+%00 : out (c),c ; MODE 0
ld hl,palette : ld bc,#7F00
setPalette out (c),c : inc c : inc b : outi : ld a,(hl) : or a : jr nz,setPalette

; définir notre coordonnée X et son incrément pour faire bouger le sprite tout seul
ld hl,40 ; milieu écran en nombre d'octets
ld (positionx),hl
ld hl,1
ld (incrementx),hl

;****************
BouclePrincipale
;****************
call waitVBL ; on attend d'être sûr que l'écran précédent soit complètement affiché
ld bc,#BC00+12 : out (c),c : ld a,#30 : inc b : out (c),a ; écran visible en #C000
; si restorex8 vaut 255 c'est le premier affichage, il ne faut pas 'restituer'
ld b,#80 : ld a,(restorex8) : cp 255 : jr z,.first8 : ld c,a : ld de,backup8 : call RestituerTampon : .first8
ld b,#80 : ld a,(positionx) : ld c,a : ld de,backup8 : call SauvegarderPortionEcran
ld b,#80 : ld a,(positionx) : ld (restorex8),a : ld c,a : call AfficherHibou
call DeplacerHibou
;
call waitVBL ; on attend d'être sûr que l'écran précédent soit complètement affiché
ld bc,#BC00+12 : out (c),c : ld a,#20 : inc b : out (c),a ; écran visible en #8000
; si restorexC vaut 255 c'est le premier affichage, il ne faut pas 'restituer'
ld b,#C0 : ld a,(restorexC) : cp 255 : jr z,.firstC : ld c,a : ld de,backupC : call RestituerTampon : .firstC
ld b,#C0 : ld a,(positionx) : ld c,a : ld de,backupC : call SauvegarderPortionEcran
ld b,#C0 : ld a,(positionx) : ld (restorexC),a : ld c,a : call AfficherHibou
call DeplacerHibou

jr BouclePrincipale

DeplacerHibou
; déplacer notre sprite
ld hl,(positionx) : ld bc,(incrementx) : add hl,bc : ld (positionx),hl
; gérer le "rebond" aux bords
ld a,h : or l     : jr nz,.pasGauche : ld hl,1  : ld (incrementx),hl : .pasGauche
ld a,l : cp 79-9 : jr nz,.pasDroite : ld hl,-1 : ld (incrementx),hl : .pasDroite
ret

AfficherHibou
ld de,donnees_sprite
RestituerTampon ; nécessite DE pré-positionné
ld hl,80 ; Y=80 pour être à peu prêt centré
ld xl,9 : ld xh,36 ; dimensions du sprite largeur/lignes
call AfficheSprite
ret

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

SauvegarderPortionEcran
ld hl,80 ; Y=80 pour être à peu prêt centré
ld xl,9 : ld xh,36 ; dimensions du sprite largeur/lignes
; BC=page + coordonnée X (0-79) / HL=coordonnée Y (0-199) / DE=adresse du tampon de sauvegarde
; XL=largeur du sprite en octets / XH=hauteur du sprite en nombre de lignes
call CalculeAdressePixel
; HL=source écran
; DE=tampon de sauvegarde
ld b,0
.sauvegardeLignes
ld c,xl ; chargeur la largeur d'une ligne
push hl ; on met l'adresse de début de la ligne de côté
ldir
pop hl ; on récupère l'adresse du début de ligne
; et on calcule le passage à la ligne suivante
ld a,h : add 8 : ld h,a ; ajouter #800 à HL
and #38 ; on teste si on déborde de la bank (passage de page+#3800 à page+#0000)
jr nz,.nextLine ; pas zéro, on est toujours dans le même bloc de lignes
ld a,80  : add l : ld l,a ; on ajoute 80 (largeur d'une ligne en octets) pour passer au bloc suivant
ld a,#C0 : adc h : ld h,a ; et on enlève #4000 (additionner #C000 c'est comme enlever #4000)
.nextLine
dec xh ; notre compteur de lignes
jr nz,.sauvegardeLignes
ret
; -------------------------------
AfficheSprite
; BC=page + coordonnée X (0-79) / 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
call CalculeAdressePixel
; 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

ld b,0
.afficheLignes
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
pop de ; on récupère l'adresse du début de ligne
; et on calcule le passage à la ligne suivante
; notre routine de passage à la ligne suivante, adaptée pour DE
ld a,d : add 8 : ld d,a ; ajouter #800 à DE
and #38 ; on teste si on déborde de la bank (passage de page+#3800 à page+#0000)
jr nz,.nextLine ; pas zéro, on est toujours dans le même bloc de lignes
ld a,80  : add e : ld e,a ; on ajoute 80 (largeur d'une ligne en octets) pour passer au bloc suivant
ld a,#C0 : adc d : ld d,a ; et on enlève #4000 (additionner #C000 c'est comme enlever #4000)
.nextLine
dec xh ; notre compteur de lignes
jr nz,.afficheLignes
ret
;-------------------
CalculeAdressePixel
; B=page vidéo #00, #40, #80 ou #C0
; C=coordonnée X (0-79)
; HL=coordonnée Y (0-199)
; adresse de la ligne dans HL en résultat
add hl,hl ; adresses 16 bits, il faut indexer de 2 en 2
ld a,lo(tableau) : add l : ld l,a : ld a,h : adc hi(tableau): ld h,a
ld a,(hl) : inc hl
ld h,(hl) : ld l,a
add hl,bc ; ajouter la position X en octets et la page!
ret
;-------------------
adresse_ecran=#0000
largeur_ecran=80
tableau
repeat 25
repeat 8
defw adresse_ecran
adresse_ecran+=#800
rend
adresse_ecran+=largeur_ecran
adresse_ecran-=#4000
rend

palette defb #54,#56,#5C,#46,#5E,#40,#47,#43,#4D,#4E,#4B,#4C,0 ; Rose
donnees_sprite incbin 'hibouRose.bin'
restorex8 defb 255 ; il ne faut pas restituer le fond la première fois
restorexC defb 255 ; il ne faut pas restituer le fond la première fois
positionx defw 0
incrementx defw 0
backup8 defs 9*36 ; on réserve la place de sauvegarde pour notre sprite
backupC defs 9*36 ; on réserve la place de sauvegarde pour notre sprite
; initialiser notre écran sur les deux pages!
org #8000 : incbin 'foret8couleurs.bin'
org #C000 : incbin 'foret8couleurs.bin'


Notre hibou se déplace sur toutes les portions de l'écran, par contre, il n'est pas détouré. Rendez-vous dans [ l'article suivant ] pour traiter la transparence des sprites purement logiciels