Revenir au plan du site

Scrolling multi-directionnel en double buffer

Alors ce qu'on va voir ici pour le scroll multi-directionnel peut s'appliquer de la même façon à un simple scrolling vertical ou horizontal.

Le passage au double buffer va nécessité de cumuler chaque déplacement avec la différence de position du buffer précédent. Ce n'est pas clair? Prenons un exemple :
- Les écrans 1 et 2 sont à la même position verticale
- L'écran 1 se déplace de 2 lignes
- L'écran 2 se déplace de 2 lignes, il se retrouve donc à nouveau à la même position que l'écran 1

Si on veut se déplacer de 2 lignes visuellement à chaque changement de buffer, il va falloir se déplacer de... ...4 lignes! Sauf au début du déplacement.
- Les écrans 1 et 2 sont à la même position verticale
- L'écran 1 se déplace de 2 lignes
- L'écran 2 se déplace de 2 lignes PLUS le déplacement de l'écran précédent, soit 4 lignes
- L'écran 1 se déplace de 2 lignes PLUS le nouveau déplacement de l'écran précédent, soit toujours 4 lignes
- L'écran 2 ne se déplace pas MAIS on ajoute toujours le nouveau déplacement de l'écran précédent, soit 2 lignes
- L'écran 1 ne se déplace pas, et comme il n'y avait pas de nouveau déplacement précédemment, on ne fait rien

Il faut donc qu'on ajoute quelque part cette information de déplacement à compenser et...   ...bêtement s'en servir!
depx defb 0 ; compensation de déplacement en X
depy defb 0 ; compensation de déplacement en Y

On va ajouter une information de buffer suivant dans notre structure écran pour la permutation des buffers. Ainsi, on utilisera un coup l'une, un coup l'autre sans faire de comparaison ou autre. Juste une affectation.

struct multi
nextBuffer defw ; adresse de la structure du buffer suivant
...

Mais d'abord, il faut initialiser nos DEUX buffers, puisqu'ils sont deux maintenant et ajouter l'information de buffer suivant (dans DE).
ld ix,ecran1 : ld a,#80 : ld de,ecran2 : call InitEcran ; premier buffer dans la zone #8000-#BFFF
ld ix,ecran2 : ld a,#C0 : ld de,ecran1 : call InitEcran ; second  buffer dans la zone #C000-#FFFF

Dans la routine InitEcran on ajoutera l'initialisation de nos deux déplacements (en cas de redémarrage!) ainsi que l'adresse du buffer suivant
InitEcran
ld (ix+multi.nextBuffer),de
ld hl,depx : ld (hl),0
ld hl,depy : ld (hl),0
...

Un petit oubli de ma part dans l'initialisation écran, le retour à la ligne de DE était claqué pour la dernière page vidéo seulement, on utilisera notre routine universelle NextLineDE afin de pouvoir initialiser l'autre buffer que nous avons choisi et situé en #8000.
...
afficheLigneTuile
push de : ldi 4 : pop de
exa : call NextLineDE : exa
dec a : jr nz,afficheLigneTuile
...

Au niveau de notre gestion du scrolling, qu'est-ce qui change?

Et bien il faut permuter les structures écran, compenser avant de tracer la nouvelle et monter nos flags.
call UpdateHardware
ld de,(ix+multi.nextBuffer) : ld ix,de ; permuter la structure de définition du buffer
; on compense rapport à l'écran précédent
.Xnegatif ld hl,depx : ld a,(hl) : and #80 : jr z,.XpasNegatif : inc (hl) : call ScrollGauche : jr .Xnegatif
.XpasNegatif
.Xpositif ld hl,depx : ld a,(hl) : and #7F : jr z,.XpasPositif : dec (hl) : call ScrollDroite : jr .Xpositif
.XpasPositif
.Ynegatif ld hl,depy : ld a,(hl) : and #80 : jr z,.YpasNegatif : inc (hl) : call ScrollBas : jr .Ynegatif
.YpasNegatif
.Ypositif ld hl,depy : ld a,(hl) : and #7F : jr z,.YpasPositif : dec (hl) : call ScrollHaut : jr .Ypositif
.YpasPositif

Dans la gestion des touches, on change nos infos depx et depy
ld a,(OCTET_CURSEUR_BAS) : and BIT_CURSEUR_BAS : jr nz,.noBas : call ScrollBas : ld hl,depy : dec (hl) : .noBas
ld a,(OCTET_CURSEUR_HAUT) : and BIT_CURSEUR_HAUT : jr nz,.noHaut : call ScrollHaut : ld hl,depy : inc (hl) : .noHaut
ld a,(OCTET_CURSEUR_DROITE) : and BIT_CURSEUR_DROITE : jr nz,.noDroite : call ScrollDroite : ld hl,depx : inc (hl) : .noDroite
ld a,(OCTET_CURSEUR_GAUCHE) : and BIT_CURSEUR_GAUCHE : jr nz,.noGauche : call ScrollGauche : ld hl,depx : dec (hl) : .noGauche

Au final, notre scrolling va prendre de l'amplitude en Y, et il n'y a plus d'artefacts sur les bords :)

Voici le source final
BUILDSNA : BANKSET 0
SNASET CPC_TYPE,4 ; modèle 6128+ conseillé
ORG #100 : RUN #100
;*** RMR2 tags + macro ***
ASICOFF equ 0 : ROM0000 equ 0 : ROM4000 equ %01000 : ROM8000 equ %10000 : ASICON equ %11000
ROM0 equ 0 : ROM1 equ 1 : ROM2 equ 2 : ROM3 equ 3 : ROM4 equ 4 : ROM5 equ 5 : ROM6 equ 6 : ROM7 equ 7
macro RMR2 tags
ld a,{tags}+%10100000
ld b,#7F
out (c),a
mend

struct multi
mapOffset defw
HSSR defb ; 0-12
VSSR defb ; #00-#70
colonne defb ; 0-3 dans la tuile
ligne defb ; 0-60 par pas de 4 dans la tuile
adresseDebut defw
crtc12 defb ; cache des bits de bank
crtcHL defw ; adresse CRTC
nextBuffer defw ; adresse de la structure du buffer suivant
endstruct
; définition des constantes pour le curseur
OCTET_CURSEUR_HAUT   equ matriceClavier+0 : BIT_CURSEUR_HAUT   equ 1
OCTET_CURSEUR_DROITE equ matriceClavier+0 : BIT_CURSEUR_DROITE equ 2
OCTET_CURSEUR_BAS    equ matriceClavier+0 : BIT_CURSEUR_BAS    equ 4
OCTET_CURSEUR_GAUCHE equ matriceClavier+1 : BIT_CURSEUR_GAUCHE equ 1

ld sp,#100 ; pile par défaut ailleurs qu'en #C000 car nous avons un buffer ici
ld bc,#7F00+%10001100+%00 : out (c),c ; MODE 0
call UnlockAsic : RMR2 ASICON
ld hl,palettePlus : ld de,#6400 : ld bc,32 : ldir : ld hl,#000 : ld (#6420),hl ; +border noir

ld bc,#BC00+1 : out (c),c : ld bc,#BD00+32 : out (c),c ; Notre map fait 128 pixels de large, adaptez selon VOS besoins
ld bc,#BC00+2 : out (c),c : ld bc,#BD00+42 : out (c),c ; Centrer l'écran en X
ld bc,#BC00+6 : out (c),c : ld bc,#BD00+32 : out (c),c ; Hauteur de l'écran visible en lignes de caractères
ld bc,#BC00+7 : out (c),c : ld bc,#BD00+34 : out (c),c ; Centrer l'écran en Y
ld bc,#BC00+12 : out (c),c : ld bc,#BD30 : out (c),c ; adresse par défaut
ld bc,#BC00+13 : out (c),c : ld bc,#BD00 : out (c),c

ld ix,ecran1 : ld a,#80 : ld de,ecran2 : call InitEcran
ld ix,ecran2 : ld a,#C0 : ld de,ecran1 : call InitEcran
ld ix,ecran1 ; notre structure écran par défaut pour aller scroller
LaBoucle
call lectureMatriceClavier
call UpdateHardware

ld de,(ix+multi.nextBuffer) : ld ix,de ; permuter la structure de définition du buffer
; on compense rapport à l'écran précédent
.Xnegatif ld hl,depx : ld a,(hl) : and #80 : jr z,.XpasNegatif : inc (hl) : call ScrollGauche : jr .Xnegatif
.XpasNegatif
.Xpositif ld hl,depx : ld a,(hl) : and #7F : jr z,.XpasPositif : dec (hl) : call ScrollDroite : jr .Xpositif
.XpasPositif
.Ynegatif ld hl,depy : ld a,(hl) : and #80 : jr z,.YpasNegatif : inc (hl) : call ScrollBas : jr .Ynegatif
.YpasNegatif
.Ypositif ld hl,depy : ld a,(hl) : and #7F : jr z,.YpasPositif : dec (hl) : call ScrollHaut : jr .Ypositif
.YpasPositif

ld a,(OCTET_CURSEUR_BAS) : and BIT_CURSEUR_BAS : jr nz,.noBas : call ScrollBas : ld hl,depy : dec (hl) : .noBas
ld a,(OCTET_CURSEUR_HAUT) : and BIT_CURSEUR_HAUT : jr nz,.noHaut : call ScrollHaut : ld hl,depy : inc (hl) : .noHaut
ld a,(OCTET_CURSEUR_DROITE) : and BIT_CURSEUR_DROITE : jr nz,.noDroite : call ScrollDroite : ld hl,depx : inc (hl) : .noDroite
ld a,(OCTET_CURSEUR_GAUCHE) : and BIT_CURSEUR_GAUCHE : jr nz,.noGauche : call ScrollGauche : ld hl,depx : dec (hl) : .noGauche
jp LaBoucle

lectureMatriceClavier
ld hl,matriceClavier
ld bc,#f782
out (c),c
ld bc,#f40e
ld e,b
out (c),c
ld bc,#f6c0
ld d,b
out (c),c
out (c),0
ld bc,#f792
out (c),c
ld a,#40
ld c,d
.loop ld b,d
out (c),a ; sélectionner la ligne
ld b,e
ini ; lire et stocker dans notre tableau
inc a
inc c
jr nz,.loop
ld bc,#f782
out (c),c
ret

matriceClavier defs 10,#FF
;---------------------------------------------------------------------
MACRO calculeAdresseTuile increment
exx : ld a,(hl) ; index de la tile dans A
if {increment}==0 : elseif {increment}==1 : inc hl : else : add hl,bc : endif
exx ; index de la tile dans A / HL'=tileMap
ld hl,tuiles : ld b,a : ld c,0 : srl bc : srl bc : add hl,bc ; HL=adresse du début de la tuile
MEND
MACRO calculeAdresseTuileLigne increment
calculeAdresseTuile {increment}
ld a,(ix+multi.ligne) : add l : ld l,a ; HL=adresse du début de la tuile à la bonne ligne
MEND
MACRO calculeAdresseTuileColonne increment
calculeAdresseTuile {increment}
ld a,(ix+multi.colonne) : add l : ld l,a ; HL=adresse du début de la tuile à la bonne colonne
MEND
MACRO calculeAdresseTuileLigneColonne increment
calculeAdresseTuile {increment}
ld a,(ix+multi.ligne) : add (ix+multi.colonne) : add l : ld l,a ; HL=adresse du début de la tuile à la bonne ligne ET bonne colonne
MEND
MACRO expandNextLineDE
ld a,d : add 8 : ld d,a : and #38 : jr nz,@termine
ld a,64 : add e : ld e,a : ld a,#C0 : adc d : ld d,a : res 3,d
@termine
MEND
;---------------------------------------------------------------------
ScrollGauche
;---------------------------------------------------------------------
ld a,(ix+multi.colonne) : or a : jr nz,.goForScroll
ld a,(ix+multi.mapOffset) : and 63 : ret z ; si on est sur la colonne zéro de la tile la plus à gauche... Bye!
.goForScroll
ld a,(ix+multi.HSSR) : bit 2,a : jr nz,.willDraw : add 4 : ld (ix+multi.HSSR),a : ret ; on ne change que le décalage et bye!
.willDraw
add 4 : and 15 : jr nz,.stillSSR
ld hl,(ix+multi.crtcHL) : dec hl : ld (ix+multi.crtcHL),hl ; each word
.stillSSR ld (ix+multi.HSSR),a
ld hl,(ix+multi.adresseDebut) :
ld a,h : and 7 : or l : jr nz,.skip : ld a,h : add 8 : ld h,a : .skip dec hl ; gérer le rebouclage à la décrémentation
ld (ix+multi.adresseDebut),hl
ld a,(ix+multi.colonne) : dec a : and 3 : ld (ix+multi.colonne),a : cp 3 : jr nz,.stillPosx : dec (ix+multi.mapOffset) : .stillPosx
ld hl,(ix+multi.mapOffset) : ld bc,64 : exx ; HL'=adresse de la tile BC'=saut de ligne dans la tileMap
ld de,(ix+multi.adresseDebut) ; DE=haut de la colonne à rafraichir
; accès direct à l'affichage d'une colonne
;------------------
afficheColonne
;------------------
ld a,(ix+multi.ligne) : or a : jp nz,.clipped
ld yh,16
.loopTiles
calculeAdresseTuileColonne 64
ld bc,#0804
repeat 7 : ld a,(hl) : ld (de),a : ld a,l : add c : ld l,a : ld a,d : add b : ld d,a : rend
ld a,(hl) : ld (de),a : ld a,l : add c : ld l,a
ld a,64 : add e : ld e,a : ld a,#C8 : adc d : ld d,a : res 3,d
repeat 7 : ld a,(hl) : ld (de),a : ld a,l : add c : ld l,a : ld a,d : add b : ld d,a : rend
ld a,(hl) : ld (de),a
ld a,64 : add e : ld e,a : ld a,#C8 : adc d : ld d,a : res 3,d
dec yh : jp nz,.loopTiles
ret
;----------
.clipped
calculeAdresseTuileLigneColonne 64
ld a,64 : sub (ix+multi.ligne) : rrca : rrca : and 15 : ld b,a : ld c,4
.loopColumnC1 ld a,(hl) : ld (de),a : expandNextLineDE (void) : ld a,l : add c : ld l,a : djnz .loopColumnC1
ld yh,15
call .loopTiles
; dernière tuile clippée
calculeAdresseTuileColonne 0
ld a,(ix+multi.ligne) : rrca : rrca : and 15 : ld b,a : ld c,4
.loopColumnC3 ld a,(hl) : ld (de),a : expandNextLineDE (void) : ld a,l : add c : ld l,a : djnz .loopColumnC3
ret
;---------------------------------------------------------------------
ScrollDroite
;---------------------------------------------------------------------
ld a,(ix+multi.mapOffset) : and 63 : cp 64-16 : ret z ; gérer le bord droit
ld a,(ix+multi.HSSR) : bit 2,a : jr z,.willDraw : sub 4 : ld (ix+multi.HSSR),a : ret
.willDraw
sub 4 : jr nc,.stillHSSR : jr z,.stillHSSR
ld hl,(ix+multi.crtcHL) : inc hl : ld (ix+multi.crtcHL),hl
ld a,12
.stillHSSR ld (ix+multi.HSSR),a

ld hl,(ix+multi.mapOffset) : ld bc,16 : add hl,bc : ld bc,64 : exx ; HL'=adresse de la tile BC'=saut de ligne dans la tileMap
ld hl,(ix+multi.adresseDebut) : ld bc,64 : ld a,h : and %11111000 : ld d,a : add hl,bc : ld a,h : and 7 : or d ; gère le rebouclage de bloc!
ex hl,de ; DE=haut de la colonne à rafraichir
call afficheColonne
ld a,(ix+multi.colonne) : inc a : and 3 : ld (ix+multi.colonne),a : jr nz,.stillPosx : inc (ix+multi.mapOffset) : .stillPosx
ld hl,(ix+multi.adresseDebut) : inc hl : ld a,h : and 7 : or l : jr nz,.skip : ld a,h : sub 8 : ld h,a : .skip : ld (ix+multi.adresseDebut),hl
ret
;---------------------------------------------------------------------
ScrollBas
;---------------------------------------------------------------------
ld hl,(ix+multi.mapOffset) : ld a,hi(tileMap+48*64) : cp h : ret z ; trop bas, on ne fait rien!
; la tuile en bas sera 16 lignes de tuiles plus bas, soit 1024 octets de tileMap de largeur 64
ld bc,16*64 : add hl,bc : exx ; ajouter 16x64 à l'offset de tuile car on va afficher en bas de l'écran + sauvegarde dans HL'
ld de,(ix+multi.adresseDebut) ; écran carré, calculs simples, le bas revient en haut et vice versa :)
call afficheTuilesHorizontalesDeuxLignes
; mettre à jour les informations courantes
ld a,(ix+multi.ligne) : add 8 : cp 64 : jr nz,.pasDeChangementDeTuile ; ligne contient l'offset de début de la ligne suivante
ld hl,(ix+multi.mapOffset) : ld bc,64 : add hl,bc : ld (ix+multi.mapOffset),hl : xor a ; passer à la tuile dessous
.pasDeChangementDeTuile
ld (ix+multi.ligne),a ; valider la ligne de tuile
ld hl,(ix+multi.adresseDebut) : call NextLineHL : call NextLineHL : ld (ix+multi.adresseDebut),hl
ld a,(ix+multi.VSSR) : add #20 : and #60 : ld (ix+multi.VSSR),a : jr nz,.pasDeChangementDeBloc
ld hl,(ix+multi.crtcHL) : ld bc,32 : add hl,bc : ld (ix+multi.crtcHL),hl ; adresse CRTC augmente de 32 mots si le bloc change
.pasDeChangementDeBloc
ret
;---------------------------------------------------------------------
ScrollHaut
;---------------------------------------------------------------------
ld hl,(ix+multi.mapOffset) : ld a,hi(tileMap) : cp h : jr nz,.okPourLeScroll ; si on est près du début on doit contrôler le "Y"
ld a,l : cp 63 : jr nc,.okPourLeScroll ; si on est sur la première ligne, on doit regarder la ligne de tuile!
ld a,(ix+multi.ligne) : or a : ret z ; tout en haut, aurevoir!
.okPourLeScroll ; on peut scroller!

; mettre à jour les informations courantes
ld a,(ix+multi.ligne) : sub 8 : jr nc,.pasDeChangementDeTuile ; ligne contient l'offset de début de la ligne suivante
ld hl,(ix+multi.mapOffset) : ld bc,-64 : add hl,bc : ld (ix+multi.mapOffset),hl : ld a,64-8 ; passer à la dernière ligne de la tuile dessous
.pasDeChangementDeTuile
ld (ix+multi.ligne),a ; valider la ligne de tuile
ld hl,(ix+multi.adresseDebut) : call PreviousLineHL : call PreviousLineHL : ld (ix+multi.adresseDebut),hl
ld a,(ix+multi.VSSR) : sub #20 : and #60 : ld (ix+multi.VSSR),a : cp #60 : jr nz,.pasDeChangementDeBloc
ld hl,(ix+multi.crtcHL) : ld bc,-32 : add hl,bc : ld (ix+multi.crtcHL),hl ; adresse CRTC baisse de 32 mots si le bloc change
.pasDeChangementDeBloc
; on affiche après en remontant
ld hl,(ix+multi.mapOffset) : exx
ld de,(ix+multi.adresseDebut)
; on continue directement dans l'affichage des lignes
;-------------------------------
afficheTuilesHorizontalesDeuxLignes
;-------------------------------
push de
.Paires
ld a,(ix+multi.colonne) : or a : jp nz,.routineClippee
calculeAdresseTuileLigne 1 : ldi 4 : res 3,d : ld (suiteImpaire.imp0+1),hl ; adresse de la ligne suivante de la tuile à la bonne ligne
.loop
repeat 15,x
calculeAdresseTuileLigne 1 : ldi 4 : res 3,d : ld (suiteImpaire.imp{x}+1),hl ; adresse de la ligne suivante de la tuile à la bonne ligne
rend
jp suiteImpaire.NonClippee

;---------------
.routineClippee
calculeAdresseTuileLigneColonne 1
ld a,4 : sub (ix+multi.colonne) : ld c,a : ld (suiteImpaire.lenFirst+1),a
ld b,0 : ldir : res 3,d
ld a,(ix+multi.colonne) : add l : ld l,a : ld (suiteImpaire.impFirst+1),hl ; adresse de la ligne suivante de la tuile à la bonne colonne
repeat 15,x
calculeAdresseTuileLigne 1 : ldi 4 : res 3,d : ld (suiteImpaire.imp{x}+1),hl ; adresse de la ligne suivante de la tuile à la bonne ligne
rend
calculeAdresseTuileLigne 0
ld c,(ix+multi.colonne)
ld b,0 : ldir : res 3,d
ld a,4 : sub (ix+multi.colonne) : add l : ld l,a : ld (suiteImpaire.impLast+1),hl ; adresse de la ligne suivante de la tuile à la bonne colonne

suiteImpaire
.clippee
pop de : ld a,d : add 8 : ld d,a
.impFirst ld hl,#1234
.lenFirst ld c,#12
ld b,0 : ldir : dec de : res 3,d : inc de : set 3,d
call .loop
.impLast ld hl,#1234
ld c,(ix+multi.colonne)
ld b,0 : ldir
ret
.NonClippee
pop de : ld a,d : add 8 : ld d,a
.imp0 ld hl,#1234 : ldi 3 : ld a,(hl) : ld (de),a : res 3,d : inc de : set 3,d
.loop
repeat 15,x
.imp{x} ld hl,#1234 : ldi 3 : ld a,(hl) : ld (de),a : res 3,d : inc de : set 3,d
rend
ret
;---------------------------------------------------------------------
InitEcran
; IX = structure buffer
; A = poids fort de la page vidéo
ld (ix+multi.nextBuffer),de
ld hl,depx : ld (hl),0
ld hl,depy : ld (hl),0
ld hl,tileMap
ld (ix+multi.mapOffset),hl
ld hl,0
ld (ix+multi.HSSR),h
ld (ix+multi.VSSR),h
ld (ix+multi.colonne),h
ld (ix+multi.ligne),h
ld (ix+multi.crtcHL),hl
ld h,a : ld (ix+multi.adresseDebut),hl
rrca : rrca : ld (ix+multi.crtc12),a

; Afficher les tuiles sur tout l'écran

ld de,(ix+multi.adresseDebut)
ld ix,tileMap ; on n'utilise plus la structure à partir de là
ld yh,16 ; 16 tuiles en hauteur
afficheLigne
ld yl,16 ; nombre de tuiles en largeur
push de ; sauvegarder l'adresse de début de ligne de l'écran
afficheTuile
push de ; sauvegarder l'adresse du début de la tuile à l'écran
ld h,(ix+0) : inc ix : ld l,0 : srl hl : srl hl ; x 256 / 4 c'est la taille de nos tuiles (64 octets)
ld bc,tuiles : add hl,bc ; on a l'adresse de la tile
ld a,16
afficheLigneTuile
push de : ldi 4 : pop de
exa : call NextLineDE : exa
dec a : jr nz,afficheLigneTuile
pop hl : ld bc,4 : add hl,bc : ex hl,de ; se placer juste à côté de la tuile précédente
dec yl : jr nz,afficheTuile
ld bc,48 : add ix,bc ; revenir à la ligne de tuile (64-16 dans notre cas)
pop hl : ld bc,128 : add hl,bc : ex hl,de ; se placer sous les tuiles à gauche
dec yh : jr nz,afficheLigne
ret
;---------------------------------------------------------------------
NextLineHL ld a,h : add 8 : ld h,a : and #38 : ret nz ; tester le changement de bloc sur n'importe quelle page
ld a,64 : add l : ld l,a : ld a,#C0 : adc h : ld h,a : res 3,h : ret
PreviousLineHL ld a,h : sub 8 : ld h,a : and #38 : cp #38 : ret nz ; test du chgt de bloc (bis)
ld a,l : add #C0 : ld l,a : ld a,#3F : adc h : ld h,a : set 3,h : ret
NextLineDE ld a,d : add 8 : ld d,a : and #38 : ret nz ; tester le changement de bloc sur n'importe quelle page
ld a,64 : add e : ld e,a : ld a,#C0 : adc d : ld d,a : res 3,d : ret
;---------------------------------------------------------------------
UpdateHardware
call WaitVBL
RMR2 ASICON
ld a,(ix+multi.HSSR) : or (ix+multi.VSSR) : or #80 : ld (#6804),a
ld hl,(ix+multi.crtcHL) : ld a,h : and 3 : or (ix+multi.crtc12) : ld bc,#BC00+12 : out (c),c : inc b : out (c),a
inc c : dec b : out (c),c : inc b : out (c),l
RMR2 ASICOFF
ret
UnlockAsic
ld bc,#BCFF
out (c),c
out (c),0
ld hl,%1001000011101010
.loop
out (c),c
ld a,h:rlca:ld h,l:ld l,a
srl c:res 3,c
and #88
or c
ld c,a
cp #4D
jr nz,.loop
ld a,#CD
out (c),a : out (c),a
ret

WaitVBL
ld b,#F5 : noVBL in a,(c) : rra : jr c,noVBL
VBL in a,(c) : rra : jr nc,VBL : ret

paletteplus: defw #323,#646,#5B3,#967,#A57,#B95,#6E4,#9B7,#C89,#ABA,#CE2,#DB8,#DB9,#EC6,#EC9,#EED
align 256 : tuiles incbin 'mapMulti.bin'
align 256 : tileMap include 'mapMulti.tilemap'
; et on se déclare deux structures pour un double buffer
struct multi ecran1
struct multi ecran2
depx defb 0
depy defb 0

Voici notre scrolling sans artefact, on voit que malgré les optimisations, le passage au double buffer est assez coûteux.


Mais c'est propre!