Revenir au plan du site
Scroller dans toutes les directions avec l'ASIC de la gamme Plus
Dans les pages sur le
[scrolling horizontal],
et le
[scrolling vertical], j'ai pris le cas le plus simple en suivant le tableau, celui où on incrémente
et quand on déborde du compteur, on revient à zéro et on incrémente l'autre compteur. Pour aller en "marche arrière", on ne déborde pas dans le même sens donc les
comparaisons sont un peu moins intuitives et les réinitialisations de compteur ne se font pas à zéro mais aux maximums respectifs. Rien d'insurmontable ;)
Pour commencer, on va se définir un écran purement carré. Il n'a que des avantages, c'est pour ça que vous le trouvez dans beaucoup de jeux/démos :
- Largeur multiple de 256 donc facilité de calculs de position à l'écran
- Tous les octets sont visibles ce qui -en cas de scrolling- est un cadeau pour savoir où écrire les nouvelles données (la ligne ou la colonne qui disparait, réapparait de l'autre côté)
Étapes de scrolling horizontal |
position X en pixel mode 1 | scroll CPC | scroll CPC et registre 3 | scroll ASIC + SSR | adresse écran |
0 | CRTC Adr = #0000 | CRTC Adr = #0000 R3 = #8C | CRTC Adr = #0000 SSR = 0 | #C000 |
1 | | | CRTC Adr = #0000 SSR = 2 | #C000 |
2 | | | CRTC Adr = #0000 SSR = 4 | #C000 |
3 | | | CRTC Adr = #0000 SSR = 6 | #C000 |
4 | | CRTC Adr = #0000 R3 = #85 | CRTC Adr = #0000 SSR = 8 | #C001 |
5 | | | CRTC Adr = #0000 SSR = 10 | #C001 |
6 | | | CRTC Adr = #0000 SSR = 12 | #C001 |
7 | | | CRTC Adr = #0000 SSR = 14 | #C001 |
8 | CRTC Adr = #0001 | CRTC Adr = #0001 R3 = #8C | CRTC Adr = #0001 SSR = 0 | #C002 |
9 | | | CRTC Adr = #0001 SSR = 2 | #C002 |
10 | | | CRTC Adr = #0001 SSR = 4 | #C002 |
... | ... | ... | ... | ... |
Étapes de scrolling vertical |
position Y | scroll CPC | scroll ASIC + SSR | adresse écran |
0 | CRTC Adr = #0000 | CRTC Adr = #0000 SSR = #00 | #C000 |
1 | | CRTC Adr = #0000 SSR = #10 | #C800 |
2 | | CRTC Adr = #0000 SSR = #20 | #D000 |
3 | | CRTC Adr = #0000 SSR = #30 | #D800 |
3 | | CRTC Adr = #0000 SSR = #40 | #E000 |
5 | | CRTC Adr = #0000 SSR = #50 | #E800 |
6 | | CRTC Adr = #0000 SSR = #60 | #F000 |
7 | | CRTC Adr = #0000 SSR = #70 | #F800 |
8 | CRTC Adr = largeur / 2 | CRTC Adr = largeur / 2 SSR = #00 | #C000 + largeur |
9 | | CRTC Adr = largeur / 2 SSR = #10 | #C800 + largeur |
... | ... | ... | ... |
Nul besoin d'avoir un tableau pour le multi-directionnel, il suffit de réaliser chaque direction l'une après l'autre et tout se passera bien.
Pour illustrer notre programme du jour, j'ai converti quelques tuiles d'une planche en libre accès et créé une carte avec le logiciel
[ Tiled ]. J'imagine
que beaucoup d'entre vous le connaisse, c'est un logiciel gratuit, opensource et multi-plateforme. Il est assez simple d'importer des données, d'indiquer à l'éditeur quelle est la taille des tuiles et
ensuite de construire une carte avec ces tuiles.
Bon, moi j'suis pas graphiste alors vous me pardonnerez le coup de truelle sur cette carte de démonstration ;)

Les tuiles sont obtenues avec la commande suivante sur ce
[fichier image]
convgeneric.exe -m 0 -g -tiles -size 8x16 mapMulti.png
|
On récupère la palette, la petite tileMap (qui s'assemble en 4k) et le petit binaire des graphismes (3K). Seulement 7K de données pour cette map, il faut dire qu'il y avait peu de sprites sur cette planche (64) et je
ne me suis servi que de 47 d'entre eux!
Pour réaliser notre scrolling, nous allons avoir besoin :
- d'une position X et d'une position Y (une position en tuiles ou une position absolue. On peut aussi prendre l'offset direct dans la table )
- d'une sous-position X et d'une sous position Y (à l'intérieur des tuiles, donc limité par la taille d'une tuile)
- d'une adresse écran correspondant au coin supérieur gauche
- de l'adresse du CRTC correspondant à ce coin
- des décalages verticaux et horizontaux dans des compteurs séparés qu'on recombinera avant d'envoyer à l'ASIC
On va se créer une structure pour gérer notre double buffer facilement, contenant toutes ces informations
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
endstruct
; et on se déclare deux structures pour un double buffer
struct multi ecran1
struct multi ecran2
|
On va déjà commencer par limiter notre scrolling aux contours de la map. C'est assez simple, il faut compter les tuiles et connaitre le nombre de tuiles présentes sur un écran :
Notre écran est carré pour 128 pixels sur 256, nos tuiles font 8 pixels sur 16 lignes. Un écran contient 16 tuiles sur 16 tuiles.
Notre tileMap fait 64 tuiles sur 64 donc quand on arrive à la tuile 48, que ce soit en hauteur ou en largeur, on arrête de scroller respectivement en bas et à droite.
Pour l'autre côté, nous avons besoin d'une double comparaison, nous arrêtons de scroller en haut et à gauche quand :
- L'index de tuile vaut zéro
- Le décalage vaut zéro
Pour terminer avec les chiffres, notre tileMap correspond à 16 écrans (4 en largeur, 4 en hauteur).
On attaque les routines? On commence par une routine qui initialise l'écran et les structures avec nos tiles (déjà vue dans
[l'article sur les tiles])
InitEcran
; IX = structure buffer
; A = poids fort de la page vidéo
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
ld bc,#800 : ex hl,de : add hl,bc : jr nc,.novf : ld bc,64-#4000 : add hl,bc : .novf ex hl,de
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
|
Qu'on se déplace horizontalement ou verticalement, on va avoir besoin à chaque fois d'aller récupérer l'adresse de la tuile courante, d'incrémenter
son déplacement dans la tileMap. En gérant tous les cas de clipping, on va partir parfois d'une tile entière, d'une ligne de tuile, d'une
colonne de tuile et même en plein milieu d'une tuile.
C'est le bon moment pour préparer quelques macros qui vont nous 'éclaircir' le code d'affichage sans qu'on passe notre temps à relire et relire
des calculs adresse qui se ressemblent comme deux gouttes d'eau. Une des sources d'erreur en programmation est de recopier un peu trop
rapidement des morceaux de code pour les utiliser ailleurs.
D'ailleurs, devinez qui s'est planté en écrivant cet article et aurait dû les écrire de suite? ^_^
La routine qui calcule l'index primaire peut ensuite avancer (ou pas) dans la table, avec un incrément simple ou une valeur plus grande
pour parcourir la table en Y.
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
|
J'allais oublier, on modifie l'écran, on modifie nos compteurs, mais on n'a toujours pas envoyé au hardware!
UpdateHardware
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 : ld h,a : ld a,(ix+multi.crtc12) : or h : 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
|
On va écrire un premier programme qui n'affiche QUE notre écran (besoin des fichiers
mapMulti.bin et
mapMulti.tilemap)
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
endstruct
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,#C0 : call InitEcran
jr $
;---------------------------------------------------------------------
InitEcran
; IX = structure buffer
; A = poids fort de la page vidéo
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
ld bc,#800 : ex hl,de : add hl,bc : jr nc,.novf : ld bc,64-#4000 : add hl,bc : .novf ex hl,de
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
;---------------------------------------------------------------------
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
tuiles incbin 'mapMulti.bin'
align 2 : tileMap include 'mapMulti.tilemap'
; et on se déclare deux structures pour un double buffer
struct multi ecran1
struct multi ecran2
|
Nos tuiles s'affichent, c'est un bon début, et il est essentiel!

La préparation étant faite, on va pouvoir ajouter nos directions dans la
[ deuxième partie ]