Une caméra de suivi - Ou comment asservir le scrolling au 'personnage'Jusqu'à présent, on avait le contrôle du scrolling avec le curseur. L'idée de ce tutorial va être de contrôler le véhicule et de laisser le scrolling rattraper la voiture. On a déjà les coordonnées du scrolling, on a les coordonnées du véhicule, on doit pouvoir automatiser des ordres de scrolling en fonction des différences entre coordonnées. Asservissement ld hl,(scroll_corner_posx) : ld bc,256+32 : add hl,bc : ld bc,(tuture.positionx) : sbc hl,bc : jr z,.testeHautBas : jp m,.allerDroite call ScrollGauche : jr z,.testeHautBas : ld hl,depx : dec (hl) : ld hl,(scroll_corner_posx) : ld bc,-4 : add hl,bc : ld (scroll_corner_posx),hl : jr .testeHautBas .allerDroite call ScrollDroite : jr z,.testeHautBas : ld hl,depx : inc (hl) : ld hl,(scroll_corner_posx) : ld bc,4 : add hl,bc : ld (scroll_corner_posx),hl .testeHautBas ld hl,(scroll_corner_posy) : ld bc,128+16 : add hl,bc : ld bc,(tuture.positiony) : sbc hl,bc : jr z,.finAsservissement : jp p,.allerHaut call ScrollBas : jr z,.finAsservissement : ld hl,depy : dec (hl) : ld hl,(scroll_corner_posy) : inc l : inc hl : ld (scroll_corner_posy),hl : jr .finAsservissement .allerHaut call ScrollHaut : jr z,.finAsservissement : ld hl,depy : inc (hl) : ld hl,(scroll_corner_posy) : dec hl : dec l : ld (scroll_corner_posy),hl .finAsservissementOn garde notre test de touche pour bouger le véhicule (en croix, comme un curseur pour le moment) ld a,(OCTET_CURSEUR_BAS) : and BIT_CURSEUR_BAS : jr nz,.noBas ld hl,(tuture.positiony) : ld bc,2 : add hl,bc : ld (tuture.positiony),hl : .noBas ld a,(OCTET_CURSEUR_HAUT) : and BIT_CURSEUR_HAUT : jr nz,.noHaut ld hl,(tuture.positiony) : ld bc,-2 : add hl,bc : ld (tuture.positiony),hl : .noHaut ld a,(OCTET_CURSEUR_DROITE) : and BIT_CURSEUR_DROITE : jr nz,.noDroite ld hl,(tuture.positionx) : ld bc,4 : add hl,bc : ld (tuture.positionx),hl : .noDroite ld a,(OCTET_CURSEUR_GAUCHE) : and BIT_CURSEUR_GAUCHE : jr nz,.noGauche ld hl,(tuture.positionx) : ld bc,-4 : add hl,bc : ld (tuture.positionx),hl : .noGaucheAvec ce premier jet, on peut voir le scrolling se figer sur le véhicule et le marquer au fer, sauf quand on approche des bords.
Maintenant, vu qu'on est parti sur un jeu de voiture, ce qui va être intéressant de faire, c'est de changer la cible du scrolling en fonction de l'orientation du véhicule. Il est peu intéressant niveaugameplay d'avoir un véhicule centré sur l'écran, et c'est en fait pareil pour un jeu d'arcade! Il est plus important de voir devant soi que derrière. Si on veut regarder derrière, bah on se retourne! On va fusionner le scrolling avec les données de l'article [ Animation des sprites hard ] En accord avec nos 32 orientations de voiture, on va créer un cercle (centré sur zéro) pour dire à la caméra de pointer devant la voiture. J'en profite pour ajouter le décalage correspondantau centre de l'écran rapport aux coordonnées du coin supérieur gauche, ainsi que le décalage à l'intérieur de notre assemblage de sprites (32/16). Ouf! directionVisuelle repeat 32,x defw cos(90-x*360/32)*80+256-32,sin(270-x*360/32)*40+128-16 rendL'asservissement doit utiliser l'orientation du véhicule pour modifier sa cible, la routine change un peu. Asservissement ld a,(position_angulaire) : add a : add a : ld hl,directionVisuelle : ld b,0 : ld c,a : add hl,bc ld c,(hl) : inc l : ld b,(hl) : inc l : ld e,(hl) : inc l : ld d,(hl) : ld (targetModifier),de ld hl,(scroll_corner_posx) : add hl,bc : ld bc,(tuture.positionx) : sbc hl,bc : jr z,.testeHautBas : jp m,.allerDroite call ScrollGauche : jr z,.testeHautBas : ld hl,depx : dec (hl) : ld hl,(scroll_corner_posx) : ld bc,-4 : add hl,bc : ld (scroll_corner_posx),hl : jr .testeHautBas .allerDroite call ScrollDroite : jr z,.testeHautBas : ld hl,depx : inc (hl) : ld hl,(scroll_corner_posx) : ld bc,4 : add hl,bc : ld (scroll_corner_posx),hl .testeHautBas ld hl,(scroll_corner_posy) : ld bc,#1234 : targetModifier=$-2 : add hl,bc : ld bc,(tuture.positiony) : sbc hl,bc : jr z,.finAsservissement : jp p,.allerHaut call ScrollBas : jr z,.finAsservissement : ld hl,depy : dec (hl) : ld hl,(scroll_corner_posy) : inc l : inc hl : ld (scroll_corner_posy),hl : jr .finAsservissement .allerHaut call ScrollHaut : jr z,.finAsservissement : ld hl,depy : inc (hl) : ld hl,(scroll_corner_posy) : dec hl : dec l : ld (scroll_corner_posy),hl .finAsservissementEn action! (Oui, il y a un petit problème)
Ça tremble de partout alors que ça ne tremblait pas tout à l'heure! C'est à dire que le modificateur d'orientation n'est pas arrondi comme les valeurs du scrolling. À moins d'avoirla chance de tomber juste (X multiple de 4 et Y multiple de 2), le scrolling va chercher à compenser en permanence, un coup trop en avant, un coup trop en arrière, sans jamaiss'arrêter. On ne va pas arrondir les valeurs de notre cercle, ça serait contreproductif car nous allons avoir besoin de précision pour les déplacements futurs du véhicules.Cet effet de bord est un travers courant de beaucoup de jeux. Pour faire simple, il faut ne rien faire si on est en dessous de nos marges (4 en X et 2 en Y). Voici le source final tenant compte de ces marges Il vous faudra les fichiers suivants pour le compiler : [binaire des sprites] | [tutureAlpha.asm] | [tutureAlphaBack.asm] | [la tileMap] | [les tuiles] BUILDSNA : BANKSET 0 SNASET CPC_TYPE,4 ; modèle 6128+ conseillé ORG #100 : RUN #100 MODE_0 equ 0 : MODE_1 equ 1 : MODE_2 equ 2 : MODE_3 equ 3 : CLEAR_INT equ %10000 ROM_OFF equ %1100 : ROM_BOTH equ 0 : ROM_UP equ %100 : ROM_LOW equ %1000 : INTRESET equ %10000 macro RMR tags : ld a,{tags}+%10000000 : ld b,#7F : out (c),a : mend ASICOFF equ 0 : ROM0000 equ 0 : ROM4000 equ %01000 : ROM8000 equ %10000 : ASICON equ %11000ROM0 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 : mendLARGEUR_MAP equ 128 : HAUTEUR_MAP equ 128 : LARGEUR_ECRAN_OCTET equ 64 : LARGEUR_ECRAN_MODE_2 equ 512 : HAUTEUR_ECRAN_LIGNE equ 256 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 struct sprite positionx defw : positiony defw : adresseDonnees defb : adresseConfiguration defb : sprlargeur defb : sprhauteur defb endstruct struct multi mapOffset defw : HSSR defb : VSSR defb : colonne defb : ligne defb : adresseDebut defw : crtc12 defb : crtcHL defw : nextBuffer defw endstruct 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,palette_fond : ld de,#6400 : ld bc,32 : ldir : ld hl,#000 : ld (#6420),hl ; +border noir ld hl,palette_sprite : ld de,#6422 : ld bc,30 : ldir ld hl,superCarInit : ld de,#4000 : ld bc,1024 : ldir ; copier notre meta sprite RMR2 ASICOFF ld bc,#BC00+1 : out (c),c : ld bc,#BD00+32 : out (c),c : ld bc,#BC00+2 : out (c),c : ld bc,#BD00+42 : out (c),c ld bc,#BC00+6 : out (c),c : ld bc,#BD00+32 : out (c),c : ld bc,#BC00+7 : out (c),c : ld bc,#BD00+34 : out (c),c ld bc,#BC00+12 : out (c),c : ld bc,#BD30 : out (c),c : ld bc,#BC00+13 : out (c),c : ld bc,#BD00 : out (c),c ld ix,tuture ld hl,410*4 : ld (ix+sprite.positionx),hl ld hl,405 : ld (ix+sprite.positiony),hl ld a,64 : ld (ix+sprite.sprLargeur),a ld a,32 : ld (ix+sprite.sprHauteur),a ld a,#40 : ld (ix+sprite.adresseDonnees),a ld a,0 : ld (ix+sprite.adresseConfiguration),a 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 call AfficherTableauSprite 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_DROITE) : and BIT_CURSEUR_DROITE : jr nz,.pasDroite ; sens horaire ld a,(position_angulaire) : ld b,0 : ld c,a : add a : add c : ld c,a ; BC = position x3 ld hl,rotation_horaire : add hl,bc : ld c,(hl) : ld b,#DF : out (c),c ; connexion ROM inc hl : ld a,(hl) : inc hl : ld h,(hl) : ld l,a : ld (AdresseRoutine),hl ld a,(position_angulaire) : inc a : and 31 : ld (position_angulaire),a jp AppelCodGenSprite .pasDroite ld a,(OCTET_CURSEUR_GAUCHE) : and BIT_CURSEUR_GAUCHE : jr nz,AppelCodGenSprite.suite ld a,(position_angulaire) : ld b,0 : ld c,a : add a : add c : ld c,a ; BC = position x3 ld hl,rotation_antihoraire : add hl,bc : ld c,(hl) : ld b,#DF : out (c),c ; connexion ROM inc hl : ld a,(hl) : inc hl : ld h,(hl) : ld l,a : ld (AdresseRoutine),hl ld a,(position_angulaire) : dec a : and 31 : ld (position_angulaire),a AppelCodGenSprite RMR2 ASICON RMR ROM_UP|MODE_0 ld h,#40 call #1234 : AdresseRoutine=$-2 RMR ROM_OFF|MODE_0 RMR2 ASICOFF .suite Asservissement ld a,(position_angulaire) : add a : add a : ld hl,directionVisuelle : ld b,0 : ld c,a : add hl,bc ld c,(hl) : inc l : ld b,(hl) : inc l : ld e,(hl) : inc l : ld d,(hl) : ld (targetModifier),de ld hl,(scroll_corner_posx) : add hl,bc : ld bc,(tuture.positionx) ld a,l : and #FC : ld l,a : ld a,c : and #FC : ld c,a : sbc hl,bc : jr z,.testeHautBas : jp m,.allerDroite call ScrollGauche : jr z,.testeHautBas : ld hl,depx : dec (hl) : ld hl,(scroll_corner_posx) : ld bc,-4 : add hl,bc : ld (scroll_corner_posx),hl : jr .testeHautBas .allerDroite call ScrollDroite : jr z,.testeHautBas : ld hl,depx : inc (hl) : ld hl,(scroll_corner_posx) : ld bc,4 : add hl,bc : ld (scroll_corner_posx),hl .testeHautBas ld hl,(scroll_corner_posy) : ld bc,#1234 : targetModifier=$-2 : add hl,bc : ld bc,(tuture.positiony) ld a,l : and #FE : ld l,a : ld a,c : and #FE : ld c,a : sbc hl,bc : jr z,.finAsservissement : jp p,.allerHaut call ScrollBas : jr z,.finAsservissement : ld hl,depy : dec (hl) : ld hl,(scroll_corner_posy) : inc l : inc hl : ld (scroll_corner_posy),hl : jr .finAsservissement .allerHaut call ScrollHaut : jr z,.finAsservissement : ld hl,depy : inc (hl) : ld hl,(scroll_corner_posy) : dec hl : dec l : ld (scroll_corner_posy),hl .finAsservissement jp LaBoucle position_angulaire defb 0 ; étape de départ ZERO ;--------------------------------------------------------------------- AfficherTableauSprite RMR2 ASICON ld iy,tableau_sprite .loop ld hl,(iy+sprite.positionx) : or a : ld de,(scroll_corner_posx) : sbc hl,de : ld (posx_compensee),hl bit 7,h : jr nz,.testGauche ; si c'est négatif on fait le test de gauche ; on teste le débordement à droite (512 pixels mode 2 en largeur) ld de,LARGEUR_ECRAN_MODE_2 : or a : sbc hl,de jp m,.hautBas jp .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 : jp 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) : or a : ld de,(scroll_corner_posy) : sbc hl,de : ld (posy_compensee),hl 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 256 lignes ld de,HAUTEUR_ECRAN_LIGNE : or a : sbc hl,de jp m,.affiche jp .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 : jp z,.horsEcran ; on fait un raccourci de calcul car on partait d'une position négative .affiche ; écriture peu optimisée des 4 sprites (X / Y / zoom) à cause de l'ordre des sprites, on verra plus tard ;) ld bc,#1234 : posx_compensee=$-2 ld de,#1234 : posy_compensee=$-2 ld h,#60 : ld l,(iy+sprite.adresseConfiguration) ld (hl),c : inc l : ld (hl),b : inc l : ld (hl),e : inc l : ld (hl),d : inc l : ld (hl),%1001 : ld a,l : add 4 : ld l,a ; sprite 1 push bc : ld a,c : add 32 : ld c,a : ld a,b : adc 0 : ld b,a ; X+32 ld (hl),c : inc l : ld (hl),b : inc l : ld (hl),e : inc l : ld (hl),d : inc l : ld (hl),%1001 : ld a,l : add 4 : ld l,a ; sprite 2 pop bc : ld a,e : add 16 : ld e,a : ld a,d : adc 0 : ld d,a ; Y+16 ld (hl),c : inc l : ld (hl),b : inc l : ld (hl),e : inc l : ld (hl),d : inc l : ld (hl),%1001 : ld a,l : add 4 : ld l,a ; sprite 3 ld a,c : add 32 : ld c,a : ld a,b : adc 0 : ld b,a ; X+32 ld (hl),c : inc l : ld (hl),b : inc l : ld (hl),e : inc l : ld (hl),d : inc l : ld (hl),%1001 ; sprite 4 jr .auSuivant .horsEcran ld h,#60 : ld l,4 : add (iy+sprite.adresseConfiguration) : ld bc,8 : xor a ; invisibiliser le sprite ld (hl),a : add hl,bc : ld (hl),a : add hl,bc : ld (hl),a : add hl,bc : ld (hl),a ; .auSuivant 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 RMR2 ASICOFF ret ;--------------------------------------------------------------------- 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 : ld b,e : ini : 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,LARGEUR_ECRAN_OCTET : 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 LARGEUR_MAP-1 : 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 : /* always NZ */ 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,LARGEUR_MAP : 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 LARGEUR_MAP 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,LARGEUR_ECRAN_OCTET : 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,LARGEUR_ECRAN_OCTET : add e : ld e,a : ld a,#C8 : adc d : ld d,a : res 3,d dec yh : jp nz,.loopTiles inc b ; reset Z ret ;---------- .clipped calculeAdresseTuileLigneColonne LARGEUR_MAP 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 inc b ; reset Z ret ;--------------------------------------------------------------------- ScrollDroite ;--------------------------------------------------------------------- ld a,(ix+multi.mapOffset) : and LARGEUR_MAP-1 : cp LARGEUR_MAP-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 : inc a /* reset Z */: 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),ald hl,(ix+multi.mapOffset) : ld bc,16 : add hl,bc : ld bc,LARGEUR_MAP : 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 inc b ; reset Z ret ;--------------------------------------------------------------------- ScrollBas ;--------------------------------------------------------------------- ld hl,(ix+multi.mapOffset) : ld a,hi(tileMap+(HAUTEUR_MAP-16)*LARGEUR_MAP) : cp h : ret z ; trop bas, on ne fait rien! ; la tuile en bas sera 16 lignes de tuiles plus bas, soit 2048 octets de tileMap de largeur 128 ld bc,16*LARGEUR_MAP : add hl,bc : exx ; ajouter 16x128 à 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,LARGEUR_MAP : 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 or 1 ; reset Z 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 LARGEUR_MAP-1 : 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,-LARGEUR_MAP : 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 inc b ; reset Z 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 or 1 ; reset Z 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 (scroll_corner_posx),hl ld (scroll_corner_posy),hl 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,LARGEUR_MAP-16 : add ix,bc ; revenir à la ligne de tuile suivante 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 palette_fond: defw #000,#600,#060,#006,#666,#0F0,#00F,#F06,#6F0,#FF0,#FFF align 256 : tuiles incbin 'carlosMapRatio.bin' palette_sprite defw #000,#002,#300,#040,#440,#550,#090,#4B0,#9B0,#0F0 superCarInit incbin 'tutureAlpha.bin',0,1024 ; seulement 1024 octets soit 4 sprites ou une étape ; et on se déclare deux structures pour un double buffer struct multi ecran1 struct multi ecran2 depx defb 0 depy defb 0 scroll_corner_posx defw 0 scroll_corner_posy defw 0 tableau_sprite struct sprite tuture struct sprite terminator startingindex 0 align 4 rotation_horaire repeat 32,x defb {bank}supercar{x} : defw supercar{x} rend align 4 rotation_antihoraire repeat 32,x defb {bank}supercar{x+32} : defw supercar{x+32} rend align 4 directionVisuelle repeat 32,x defw cos(90-x*360/32)*80+256-32,sin(270-x*360/32)*40+128-16 rend org #4000 : tileMap include 'carlosMapRatio.tilemap' ROMBANK 8 ; En ROM! org #C000 include 'tutureAlpha.asm' include 'tutureAlphaBack.asm' Roudoudou CPCrulez[Content Management System] v8.732-desktop/c Page créée en 783 millisecondes et consultée 44 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. |
|