Sauvegarde et restitution du fond d'un sprite clippéOK, alors on sait sauvegarder le fond avant affichage et aussi restituer le fond avant d'entamer un nouvel affichage. Qu'est-ce qui change avec les sprites clippés? En fait peu de choses, on a vu que notre routine clippée affichait uniquement dans l'écran, et à y bien regarder, pour chaque sprite clippé, il existe un rectangle uniquequi correspond à la version clippée de notre sprite. Il est donc possible d'utiliser les mêmes routines de sauvegarde et restitution que pour les sprites non clippés. Par contre, comme nous ne voulons pas refaire les calculs de clipping, on va stocker plus d'informations dans notre tableau de sprites. Ce tableau de sprites à afficher va aussi contenir des informations indépendantes pour la restitution : - À commencer par la largeur et la hauteur qui peuvent changer. - On va aussi mettre l'adresse du tampon écran - L'adresse de départ à l'écran sera la même, on ne va pas la recalculer, on la stocke aussi! struct sprite positionx defw positiony defw donnees defw sprLargeur defb ; si zéro, liste terminée sprHauteur defb adresseEcran defw ; pour la restitution resLargeur defb ; pour la restitution ; si zéro, ne pas restituer resHauteur defb ; pour la restitution adresseSauvegarde defw ; pour la restitution endstructOn reprend la routine de sauvegarde du cours [Restituer le fond après le déplacement d'un sprite] On va tailler dedans pour enlever le calcul d'adresse redondant et aussi préserver quelques informations : - la hauteur (car on boucle avec un DEC dessus, en fin de routine elle serait perdue). - HL parce qu'on va afficher le sprite au même endroit qu'on sauvegarde... - DE parce qu'on enregistre ailleurs que la zone qu'on va lire pour afficher SauvegarderPortionEcran ; HL=source écran ; DE=tampon de sauvegarde push de,hl ld de,(iy+sprite.adresseSauvegarde) ld a,xh : ld yh,a ; sauvegarde de XH ld de,(iy+sprite.adresseEcran) ld hl,(iy+sprite.adresseSauvegarde) 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 ld a,yh : ld xh,a ; restitution de XH pop hl,de retConcernant les restitutions, elles peuvent se faire toutes en même temps, avant de commencer à afficher les nouveaux sprites, on va faire une boucle simplesur notre tableau et voir si il faut restituer ou pas. Et surtout, aucun test de clipping, on sait qu'on est toujours dedans! RestituerPortionEcranReboucle ld bc,{sizeof}sprite : add iy,bc ; sprite suivant! RestituerPortionEcran ld a,(iy+sprite.sprLargeur) : or a : ret z ; terminé ld a,(iy+sprite.resLargeur) : or a : ret z ; terminé aussi ^_^ ld xl,a : ld a,(iy+sprite.resHauteur) : ld xh,a ld hl,(iy+sprite.adresseSauvegarde) ld de,(iy+sprite.adresseEcran) ld b,0 .restitueLignes 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 ld a,d : add 8 : ld d,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 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,.restitueLignes jp RestituerPortionEcranReboucleMaintenant il faut rassembler tout ce petit monde dans un seul source et faire un test :)Pour ce test, nous aurons deux tableaux de sprite (avec 1 sprite) car il faut un tableau par buffer. On commence par restituer le fond si il y a lieu d'être, ensuite on demande l'affichage et une sauvegardese fait au préalable. C'est vraiment un gros morceau, prenez le temps de le digérer, avec ce source vous avez un moteur d'affichage complet (hors scrollings ;) ) Téléchargez les fichiers [hibou] et [foret] si ce n'est déjà fait BUILDSNA : BANKSET 0 ORG #38 : EI : RET ORG #100 : RUN #100struct sprite positionx defw positiony defw donnees defw sprLargeur defb ; si zéro, liste terminée sprHauteur defb adresseEcran defw ; pour la restitution resLargeur defb ; pour la restitution (si 0, ne rien faire) resHauteur defb ; pour la restitution adresseSauvegarde defw ; pour la restitution endstruct 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 reboucle ld iy,tableauTic : call RestituerPortionEcran ; ne fera rien à la première exécution ld iy,tableauTic : call BougeSprite ld iy,tableauTic : ld a,#C0 : ld (videoBank),a call AfficherTableauSprite : call WaitVBL : ld bc,#BC00+12 : out (c),c : ld bc,#BD30 : out (c),cld iy,tableauToc : call RestituerPortionEcran ; ne fera rien à la première exécution ld iy,tableauToc : call BougeSprite ld iy,tableauToc : ld a,#80 : ld (videoBank),a call AfficherTableauSprite : call WaitVBL : ld bc,#BC00+12 : out (c),c : ld bc,#BD20 : out (c),c jr reboucle ;--------------------------------------------------------------------- BougeSprite ;--------------------------------------------------------------------- ld hl,(.positionx) : ld de,(.bougex) : add hl,de : ld (.positionx),hl : ld (iy+sprite.positionx),hl ; bouger et mettre à jour la structure courante ld hl,(.positiony) : ld de,(.bougey) : add hl,de : ld (.positiony),hl : ld (iy+sprite.positiony),hl ; idem pour le Y ; gérer les changements de direction ld a,(.bougey) : or a : jr z,.testx ; on bouge en Y, tester le min et le max ld a,(.positiony) : cp -18 : jp z,.aDroite cp 184 : jp z,.aGauche ret .testx ld a,(.positionx) : cp #FC : jr z,.enHaut cp 76 : jr z,.enBas ret .aDroite ld hl,0 : ld (.bougey),hl : inc l : ld (.bougex),hl : ret .aGauche ld hl,0 : ld (.bougey),hl : dec hl : ld (.bougex),hl : ret .enBas ld hl,0 : ld (.bougex),hl : inc l : inc l : ld (.bougey),hl : ret .enHaut ld hl,0 : ld (.bougex),hl : dec hl : dec hl : ld (.bougey),hl : ret .positionx defw 76 .positiony defw 164 .bougex defw 0 .bougey defw 2 tableauTic defw 0 ; posx defw 0 ; posy defw donnees_sprite defb 9,36 ; dimensions defw 0 defb 0,0 defw tamponTic defs {sizeof}sprite ; une structure vide pour arrêter le tableau tableauToc defw 0 ; posx defw 0 ; posy defw donnees_sprite defb 9,36 ; dimensions defw 0 defb 0,0 defw tamponToc defs {sizeof}sprite ; une structure vide pour arrêter le tableau ;---------------------------------------------------- WaitVBL ld b,#F5 .loop in a,(c) : rra : jr nc,.loop : ret ;---------------------------------------------------- AfficherTableauSprite ;---------------------------------------------------- .loop ld (iy+sprite.resLargeur),0 ; la largeur de restitution sera notre TAG qu'il faut restituer ld hl,(iy+sprite.positionx) bit 7,h : jr nz,.testGauche ; si c'est négatif on fait le test de gauche ; on teste le débordement à droite (80 octets en configuration standard) ld de,80 : or a : sbc hl,de jp m,.hautBas jr .horsEcran .testGauche ld a,(iy+sprite.sprlargeur) : add l : ld l,a : ld a,h : adc 0 jp m,.horsEcran ; négatif hors écran, reste à tester le zéro ld a,l : or a : jr z,.horsEcran ; on fait un raccourci de calcul car on partait d'une position négative ; quasiment le même code de tester le débordement en vertical .hautBas ld hl,(iy+sprite.positiony) bit 7,h : jr nz,.testHaut ; si c'est négatif on fait le test du haut ; on teste le débordement en bas de l'écran de 200 lignes ld de,200 : or a : sbc hl,de jp m,.affiche jr .horsEcran .testHaut ld a,(iy+sprite.sprhauteur) : add l : ld l,a : ld a,h : adc 0 jp m,.horsEcran ; négatif hors écran, reste à tester le zéro ld a,l : or a : jr z,.horsEcran ; on fait un raccourci de calcul car on partait d'une position négative .affiche ld b,#C0 : videoBank=$-1 ; bank vidéo par défaut ld c,(iy+sprite.positionx) ; on peut ne lire que 16 bits à la même adresse, merci le little endian ld hl,(iy+sprite.positiony) ld de,(iy+sprite.donnees) ld a,(iy+sprite.sprlargeur) : ld xl,a ld a,(iy+sprite.sprhauteur) : ld xh,a push iy ; car notre routine de sprite masqué utilise YL ... call AfficheSprite pop iy .horsEcran ; ajouter ici une condition de bouclage, par exemple sur les données... ld de,{sizeof}sprite : add iy,de ld a,(iy+sprite.sprlargeur) : or a ; tester la largeur du prochain sprite jp nz,.loop ; on reboucle si le prochaine sprite n'a pas une largeur nulle 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 bit 7,h : jr z,.pasClipHaut ; si le Y est positif ou nul, pas de clipping en haut ; XH est la hauteur affichable, on soustrait le Y négatif push hl : ld a,xh : add l : ld l,a : ld a,h : adc 0 : ld h,a : ld a,l : ld xh,a : pop hl ; vu qu'on saute des lignes, il faut avancer le pointeur de données ld a,l : ex hl,de : ld d,0 : ld e,xl .sauteLignes add hl,de : inc a : jr nz,.sauteLignes ; +simple qu'une multiplication ex hl,de ; remettre le pointeur dans DE ld hl,0 ; tout est corrigé, on dit qu'on part maintenant de la ligne 0 .pasClipHaut ; pour rester sur des comparaisons 8 bits, on peut soustraire sprHauteur à hauteur et comparer à sprY ld a,200 : sub xh : cp l : jr nc,.pasClipBas ; ld a,200 : sub l : ld xh,a ; nouvelle hauteur +petite .pasClipBas xor a ; offsetDebut par défaut bit 7,c : jr z,.pasClipGauche ld a,c : add xl : ld xl,a ; nouvelle largeur ld a,c : neg ; nouvel offsetDebut ld c,0 ; nouveau X en zéro .pasClipGauche ld (offsetDebut),a ; soit zéro, soit celui calculé lors du clipping ld a,c : add xl : cp 81 ; largeur+1 rapport à la comparaison souhaitée ld a,0 : jr c,.pasClipDroite ; A=0 au cas où on n'ait pas de clipping, pour reset offsetFin ld a,xl : exa ; on met la largeur du sprite de côté ld a,80 : sub c : ld xl,a ; nouvelle largeur de sprite exa ; on récupère la largeur d'origine sub xl .pasClipDroite ld (offsetFin),acall CalculeAdressePixel ;---------------------------------------------------------------------------------------------------- ; ici le clipping est terminé, on a des coordonnées dans l'écran et des tailles qui ne débordent pas ; IY n'est pas encore altéré, on peut inscire nos infos de sauvegarde du fond ld (iy+sprite.adresseEcran),hl ld a,xl : ld (iy+sprite.resLargeur),a ld a,xh : ld (iy+sprite.resHauteur),a call SauvegarderPortionEcran ;---------------------------------------------------------------------------------------------------- ; HL=destination écran ; DE=toujours l'adresse source des données ld bc,hl ; on utilise BC comme destination écran ld h,hi(tableTransparence) .afficheLignes ld a,xl : ld yl,a ; chargeur la largeur d'une ligne dans YL push bc ; on met l'adresse de début de la ligne de côté ld a,e : add #12 : offsetDebut=$-1 : ld e,a : ld a,d : adc 0 : ld d,a .pixelMasque ld a,(de) ; lire le sprite ld l,a ; octet du sprite dans L ld a,(bc) ; on récupère l'octet de l'écran and (hl) ; on applique le masque or l ; on fusionne avec la donnée du sprite ld (bc),a ; on remet dans l'écran inc bc inc de ; et on incrémente data+ecran dec yl jr nz,.pixelMasque ld a,e : add #12 : offsetFin=$-1 : ld e,a : ld a,d : adc 0 : ld d,a pop bc ; 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 BC ld a,b : add 8 : ld b,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 c : ld c,a ; on ajoute 80 (largeur d'une ligne en octets) pour passer au bloc suivant ld a,#C0 : adc b : ld b,a ; et on enlève #4000 (additionner #C000 c'est comme enlever #4000) .nextLine dec xh ; notre compteur de lignes jr nz,.afficheLignes ret ;---------------------------------------------------- SauvegarderPortionEcran ;---------------------------------------------------- ; HL=source écran ; DE=tampon de sauvegarde push de,hl ld de,(iy+sprite.adresseSauvegarde) ld a,xh : ld yh,a ; sauvegarde de XH 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 ld a,yh : ld xh,a ; restitution de XH pop hl,de ret ;---------------------------------------------------- RestituerPortionEcranReboucle ;---------------------------------------------------- ld bc,{sizeof}sprite : add iy,bc ; sprite suivant! ;---------------------------------------------------- RestituerPortionEcran ;---------------------------------------------------- ld a,(iy+sprite.sprLargeur) : or a : ret z ; terminé ld a,(iy+sprite.resLargeur) : or a : ret z ; terminé aussi ^_^ ld xl,a : ld a,(iy+sprite.resHauteur) : ld xh,a ld hl,(iy+sprite.adresseSauvegarde) ld de,(iy+sprite.adresseEcran) ld b,0 .restitueLignes 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 ld a,d : add 8 : ld d,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 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,.restitueLignes jp RestituerPortionEcranReboucle ;---------------------------------------------------- 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 align 256 ;------------------- tableTransparence ;------------------- repeat 256,x px=x-1 ; car le compteur va par défaut de 1 à 256 et non 0 à 255 masque=0 if (px & (128|32|8|2))==0 masque|=128|32|8|2 ; pour cet octet on conservera les données écran endif if (px & (64|16|4|1))==0 masque|=64|16|4|1 ; pour cet octet on conservera les données écran endif defb masque rend palette defb #4D,#54,#56,#5C,#46,#5E,#40,#47,#43,#4E,#4B,#4C,0 ; Rose en zéro donnees_sprite incbin 'hibouZero.bin' tamponTic defs 9*36 tamponToc defs 9*36 org #8000 : incbin 'foretZero.bin' org #C000 : incbin 'foretZero.bin'Comme d'habitude, la petite démo visuelle avec en bas nos deux tampons de sauvegarde du fond. On remarque que lorsque le sprite se déplace à l'horizontale, il consomme moins d'octets (le bas se fige) et lorsqu'illonge les bords gauche/droite, il occupe plus de place mais moins que la taille totale d'un sprite. 
Roudoudou CPCrulez[Content Management System] v8.732-desktop/c Page créée en 522 millisecondes et consultée 29 foisL'Amstrad CPC est une machine 8 bits à base d'un Z80 à 4MHz. Le premier de la gamme fut le CPC 464 en 1984, équipé d'un lecteur de cassettes intégré il se plaçait en concurrent du Commodore C64 beaucoup plus compliqué à utiliser et plus cher. Ce fut un réel succès et sorti cette même années le CPC 664 équipé d'un lecteur de disquettes trois pouces intégré. Sa vie fut de courte durée puisqu'en 1985 il fut remplacé par le CPC 6128 qui était plus compact, plus soigné et surtout qui avait 128Ko de RAM au lieu de 64Ko. |
|