09-Déplacement du joueur
Maintenant que les SPRITES sont affichés, on va commencer à faire bouger la fusée du joueur.
Pour cela on va un peu modifier la structure du code assembleur. Il y surement d’autres méthodes, mais c’est celle présentée par le livre, donc je l’ai suivi.
Jusqu’à présent la ROM démarre et appelle tout de suite le sous-programme INIT
. Pourquoi ? Ben, parce qu’on l’a spécifié dans l’entête du code, souvenez vous le DW INIT
:
; ****************************************************************
; MSX ROM Cartridge Header and Function library
; ****************************************************************
FNAME "test1.ROM"
cpu z80
ORG 4000h
INCLUDE "MSXRom-Include.asm"
db "AB"
DW INIT
DW 0
DW 0
DW 0
DS 6
Bon et bien maintenant on ne veut pas simplement faire une initialisation (accéder au SCREEN 1 et afficher les SPRITES), on veut installer une partie de code qui va devenir le programme principal, c’est par lui que la ROM va débuter.
C’est encore lui qui va se charger d’initialiser l’affichage la première fois, et qui ensuite, va continuellement surveiller le clavier (via une boucle infinie) et appeler momentanément des sous-programmes de déplacement en fonction de la touche qu’il a détecté.
Bon, on va mieux visualiser par un schéma :

Ainsi la ROM ne va plus appeler INIT
mais un label que j’appelle DEBUT
qui va se charger d’appeler le sous-programme (ou routine) INIT
. Ensuite DEBUT
se termine et donne la main à un nouveau label (ou étiquette) qui s’appelle BOUCLE
(c’est notre boucle sans fin) qui va tourner en boucle justement en appelant une routine de temporisation (qui ralenti l’affichage écran) et qui surveille le clavier.
Si pendant cette boucle infinie, une touche directionnelle est enfoncée alors on fait appel à la routine de déplacement de la fusée.
Tous les sous-programmes sont codés de façon à retourner au programme principal une fois leur boulot exécuté.
Voilà à quoi va ressembler notre programme principal:

Dans l’article précédent nous avons déjà codé la routine INIT:
(INITIALISATION), Il reste à coder le programme principal, les routines de temporisation et de déplacement. Mais avant, modifions notre entête pour que la ROM lance DEBUT
au démarrage et non plus INIT
:
; ****************************************************************
; MSX ROM Cartridge Header and Function library
; ****************************************************************
FNAME "test2.ROM"
cpu z80
ORG 4000h
INCLUDE "MSXRom-Include.asm"
db "AB"
DW DEBUT ; la ROM lance le sous-programme DEBUT au démarrage
DW 0
DW 0
DW 0
DS 6
PROGRAMME PRINCIPAL – DEBUT
Je vous livre le code par morceaux, ne vous occupez pas de savoir où le placer dans le fichier, je vous ferai une copie intégrale du code à la fin pour que vous voyez l’organisation.
;------------------------------------------------------------------------------
; Boucle du jeu : Le programme démarre à DEBUT puis va à BOUCLE
;------------------------------------------------------------------------------
DEBUT: ; c'est ici que débute le programme !!!
call INIT ; on appelle le sous-prog INIT
Rien de plus simple pour commencer. Vous remarquez qu’il n’y a pas d’instruction ret
(RETURN) à la fin de cette routine. Ainsi le code va continuer au label déclaré juste en dessous puisqu’il n’est pas arrêté ni re-routé. Ca tombe bien puisque le prochain label que l’on va déclarer ensuite est BOUCLE
PROGRAMME PRINCIPAL – BOUCLE
BOUCLE: ; c'est la boucle principale du programme !!!
call DELAI ; on appelle le sous-prog DELAI afin de ralentir l'exécution
ld a,8 ; on teste la touche gauche = ligne 8 de la matrice du clavier
call 321 ; (bios) SNSMAT retourne dans la valeur de la ligne 8
bit 4,a ; bit teste la colonne 4 (0=pressé 1=non pressé)
call z,FUSGAU ; si résultat=0 on va au sous-prog FUSGAU (déplacer à droite)
call DELAI ; on ralenti l'exécution
ld a,8 ; la touche droite est aussi sur la ligne 8 de la matrice
call 321 ; on appelle le bios pour qu'il donne la valeur de la ligne 8
bit 7,a ; la colonne 7 est testée (0=pressé 1=non pressé)
call z,FUSDRO ; si résultat=0 on va au sous-prog FUSDRO (déplacer àgauche)
call DELAI ; on ralenti l'exécution
jp BOUCLE ; on redémarre la boucle principale
C’est bien le coeur du programme et il va tourner en boucle (comme son label l’indique), d’ailleurs la dernière ligne de cette partie du code se termine par un JP BOUCLE
: Saute au début du label BOUCLE:
.
Voici ce que fait la boucle :
- Appelle une routine
DELAI
. Verifie si la touche gauche est enfoncée, si tel est le cas, elle appelle la routine de déplacement du sprite vers la gaucheFUSGAU
. Cette routine lui rendra la main à la fin. - Appelle la routine
DELAI
. Verifie si la touche droite est enfoncée, si tel est le cas, elle appelle la routine de déplacement du sprite vers la droiteFUSDRO
. Cette routine lui rendra la main à la fin. Appelle la routineDELAI
. - Retourne au début de la boucle.
0n voit déjà que l’on aura 3 routines à écrire par la suite :
-Routine DELAI
- Routine FUSGAU
- Routine FUSDRO
Mais revenons à notre boucle pour l’instant. On voit qu’après avoir appelé la routine DELAI
, on vérifie l’enfoncement de la touche gauche, comment ça marche ?
En fait, on se base sur une matrice de clavier et une instruction du BIOS qui surveille cette matrice :

Et l’instruction SNSMAT du BIOS c’est le CALL 321
(ou call 141 en hexa) :
SNSMAT
Address : #0141
Function : Returns the value of the specified line from the keyboard matrix
Input : A - for the specified line
Output : A - for data (the bit corresponding to the pressed key will be 0)
Registers: AF
Cette instruction BIOS nous dit : Donnez-moi dans A la ligne qui vous intéresse, et je vous charge dans A les infos des bit0 à 7 de cette ligne.
Nous on veut la touche gauche, elle correspond à la ligne 8, donc si on charge A avec 8 et qu’on appelle la routine, on récupère toutes les infos de cette ligne dans A.
La touche gauche c’est le bit 4 (colonnes de la matrice), l’instruction BIOS nous dit que si la touche est enfoncée alors le bit correspondant égale 0.
ld a,8 ; on teste la touche gauche = ligne 8 de la matrice du clavier
call 321 ; (bios) SNSMAT retourne dans la valeur de la ligne 8
bit 4,a ; bit teste la colonne 4 (0=pressé 1=non pressé)
call z,FUSGAU ; si résultat=0 on va au sous-prog FUSGAU (déplacer à droite)
C’est exactement ce que l’on fait avec BIT 4,A
: on teste A avec bit 4 et la ligne suivante on appelle la routine FUSGAU
si A est à zéro call z,FUSGAU
.
Je vous laisse chercher pour le curseur à droite et l’appel à la routine correspondante.
DELAI
Cette routine permet de ralentir le déplacement de la fusée entre autre sinon cela irait trop vite et on ne verrait pas notre fusée se déplacer de façon douce.
Ce n’est rien d’autre qu’une boucle qui va compter de 255 à 0 sans rien faire d’autre avant de rendre la main au programme principal.
Cette routine a 2 labels:
- Le label
DELAI
, qui est initialisation du compteur à 255 passe le relai au labelDEL
qui va faire le travail de comptage. - Le label
DEL
va décrémenter A d’une unitéDEC A
, si A n’égale pas zéro (NZ=Non Zéro) alors on revient au début deDEL
- Si A=0 alors on ne fait plus de
JUMP
(puisque le résultat n’est pas vérifié) mais on fait un retour au programme qui l’a appelé (programme principal) via unRET
(return).
;------------------------------------------------------------------------------
; DELAI
;------------------------------------------------------------------------------
DELAI:
ld a,255 ; c'est la valeur de départ, plus elles est haute, plus c'est lent
; a ne peut contenir qu'une valeur entre 0 et 255
DEL:
dec a ; c'est la boucle qui va décrémenter de 1 jusqu'à a=0
jp nz,DEL ; si a n'est pas égale à 0 on repart au début de la boucle
ret ; si a=0 on retourne au programme principal
Vous pourrez vous amuser à changer la valeur 255 par 1 pour voir le résultat.
FUSGAU
;------------------------------------------------------------------------------
; Déplacement de la fusée à gauche
;------------------------------------------------------------------------------
FUSGAU:
ld hl,6913 ; récupère adresse de la position x du sprite dans la TAS
call 74 ; la fonction charge la valeur de l'adresse dans A donc x=A
dec a ; on teste A en le décrementant d'une unité x-1
ret z ; si le résultat de l'opération x=0 on ne fait rien
; et on quitte la sous-routine
; car cela veut dire que la fusée est à la limite de l'écran à gauche
call 77 ; sinon on charge la position x de la TAS avec la nouvelle valeur x-1
ret ; on retourne au programme principal
Quand on arrive dans cette routine c’est que la touche gauche a été enfoncée.
Donc on va devoir déplacer notre fusée vers la gauche d’un pixel. Pour cela il nous faut la coordonnée horizontale de notre fusée.
Rien de plus simple, quand on a chargé les attributs des sprites dans la TAS, on a commencée à les enregistrer à l’adresse 6912 (début de la TAS) et en plus le 1er sprite pour lequel on a chargé les infos (4 données: position y, position x, numéro de sprite, couleur) c’est notre fusée. Donc les données de notre sprite sont aux adresses suivantes :
- Adresse 6912 : position y
- Adresse 6913 : position x
- Adresse 6914 : numéro du sprite
- Adresse 6915 : couleur du sprite
Et ainsi de suite pour les sprites suivants (vous devriez donc connaître les coordonées du sprite ennemi…en tout cas leurs adresses).
Dans notre cas, la fusée, on sait que la position x est à l’adresse 6913.
Il suffit donc de l’appeler, de faire des calculs sur la valeur x et de la recharger dans la TAS pour que notre sprite prenne en compte la nouvelle position.
Pour lire la valeur d’une adresse de la VRAM on fait un CALL 74
(CALL 4A) qui chargera la valeur dans l’accumulateur A :
RDVRM
Address : #004A
Function : Reads the content of VRAM
Input : HL - address read
Output : A - value which was read
Registers: AF
On soustrait 1 unité, et oui pour aller à gauche on revient vers la coordonnée 0 de l’écran. On retourne au programme principale sans rien faire si A=0 (RET Z) car cela veut dire que l’on est déjà à l’extrémité gauche de l’écran. Sinon on continue dans le code, on a déjà décrémenté A, donc il nous reste plus qu’à écrire à l’adresse chargée dans l’accumulateur HL la nouvelle valeur via un CALL 77
(CALL 4D) :
WRTVRM
Address : #004D
Function : Writes data in VRAM
Input : HL - address write
A - value write
Registers: AF
Et on retourne au programme principal (RET).
FUSDRO
C’est pareil que pour la routine précédente. Seul change le test de l’extrémité de l’écran à droite qui est fixé à 240 (240+ 16 pixels de notre sprite =256…..un sprite de 8 mais agrandi x 2).
;------------------------------------------------------------------------------
; Déplacement de la fusée droite
;------------------------------------------------------------------------------
FUSDRO:
ld hl,6913 ; récupère adresse de la position x du sprite dans la TAS
call 74 ; la fonction charge la valeur de l'adresse dans A donc x=A
SUB 240 ; on teste A en lui enlevant 240
ret z ; si le résultat de l'opération=0 on ne fait rien
; et on quitte la sous-routine
; car cela veut dire que la fusée est à la limite de l'écran à droite
add a,241 ; rajoute les 240 pour retrouver le x initial et incrémente de 1(donc 241)
call 77 ; sinon on charge la position x de la TAS avec la nouvelle valeur x+1
ret ; on retourne au programme principal
Comme pour l’instant on pratique des calculs qui nous permettent de comparer à 0 ou non 0 , pour vérifier si x est déjà à 240, il suffit de luis soustraire 240 et de comparer à 0, sinon on lui rajoute les 240 pour le ramener à l’état dans lequel on la récupéré et on lui rajoute 1 pour qu’il se déplace à droite. Ce code devrait s’abstenir de plus de commentaires désormais.
Je vous livre le code en entier pour que vous voyez l’ordre des routines insérées bien qu’il n’y ait pas grande importance (sauf pour celles où on n’a pas mis de RET
pour qu’elles enchaînent sur le prochain label en dessous, ex :DEBUT
et BOUCLE
) puisqu’une fois la ROM démarrée, elle va appeler la routine ‘DEBUT’ et les sauts vont s’enchaîner comme on l’a codé. Compilez, jouez vous devriez pouvoir déplacer votre fusée :
; ****************************************************************
; MSX ROM Cartridge Header and Function library
; ****************************************************************
FNAME "test2.ROM"
cpu z80
ORG 4000h
INCLUDE "MSXRom-Include.asm"
db "AB"
DW DEBUT ; la ROM lance le sous-programme DEBUT au démarrage
DW 0
DW 0
DW 0
DS 6
;------------------------------------------------------------------------------
; Iinitialisation, appelé par le début du programme : DEBUT
;------------------------------------------------------------------------------
INIT:
LD A,1 ; 1 pour screen 1
ld [64687],A ; (bios)SCRMOD adresse hexa $FCAF :mode courant de l'ecran
call 95 ; (bios)CHGMOD : change le mode l'écran
;------------------------------------------------------------------------------
; Charge la TGS avec la forme des 3 sprites
;------------------------------------------------------------------------------
ld hl,donnee ; charge les données TGS via le label: donnee
ld de,14336 ; adresse du début de la TGS
ld bc,24 ; longueur du bloc = 3 sprites de 8 octets = 24
call 92 ; LIDRVM transfert de bloc de la RAM vers la VRAM
;------------------------------------------------------------------------------
; Charge la TAS avec les attributs des 3 sprites (registre 5)
; Registre 5 => valeur x 128 = Début de l'adresse de la TAS
;------------------------------------------------------------------------------
ld c,5 ; registre 5 gère l'adresse de début de la TAS
ld b,54 ; valeur 54 = positionne début de la TAS à l'adresse 6912
call 71 ; WRTVDP Ecrire dans le VDP (VRAM)
ld hl,donne1 ; charge les données TAS au label: donne1
ld de,6912 ; adresse du début de la TAS
ld bc,12 ; longueur du bloc = 4 données pour chaque sprite (3 sprites)
call 92 ; LIDRVM transfert de bloc de la RAM vers la VRAM
;------------------------------------------------------------------------------
; Initialise les 3 variables
; indcol: indicateur de collision adresse 41000
; indbal: indicateur de balle tirée adresse 41001
; inddir: indicateur de direction de l'ennemi adresse 41002
;------------------------------------------------------------------------------
ld a,1
ld [indcol],a ; indcol = 1
ld [indbal],a ; indbal = 1
ld [inddir],a ; inddir = 1
;------------------------------------------------------------------------------
; Taille des sprites agrandis (registre 1)
;------------------------------------------------------------------------------
ld c,1 ; registre 1 gère la taille des sprites
ld b,225 ; 225 c'est la valeur pour agrandir un sprite 8 x 8
call 71 ; WRTVDP Ecrire dans un registre du VDP (VRAM)
;------------------------------------------------------------------------------
; Positionne l'adresse de début de la TNP (registre 2)
; Registre 2 => valeur x 1024 = Début de l'adresse de la TNP
;------------------------------------------------------------------------------
ld c,2 ; registre 2
ld b,6 ; valeur 6 = positionne début de la TNP à l'adresse 6144
call 71 ; WRTVDP Ecrire dans un registre du VDP (VRAM)
ret ; fin de la boucle INIT
;------------------------------------------------------------------------------
; Déplacement de la fusée à gauche
;------------------------------------------------------------------------------
FUSGAU:
ld hl,6913 ; récupère adresse de la position x du sprite dans la TAS
call 74 ; la fonction charge la valeur de l'adresse dans A donc x=A
dec a ; on teste A en le décrementant d'une unité x-1
ret z ; si le résultat de l'opération x=0 on ne fait rien
; et on quitte la sous-routine
; car cela veut dire que la fusée est à la limite de l'écran à gauche
call 77 ; sinon on charge la position x de la TAS avec la nouvelle valeur x-1
ret ; on retourne au programme principal
;------------------------------------------------------------------------------
; Déplacement de la fusée droite
;------------------------------------------------------------------------------
FUSDRO:
ld hl,6913 ; récupère adresse de la position x du sprite dans la TAS
call 74 ; la fonction charge la valeur de l'adresse dans A donc x=A
SUB 240 ; on teste A en lui enlevant 240
ret z ; si le résultat de l'opération=0 on ne fait rien
; et on quitte la sous-routine
; car cela veut dire que la fusée est à la limite de l'écran à droite
add a,241 ; rajoute les 240 pour retrouver le x initial et incrémente de 1(donc 241)
call 77 ; sinon on charge la position x de la TAS avec la nouvelle valeur x+1
ret ; on retourne au programme principal
;------------------------------------------------------------------------------
; DELAI
;------------------------------------------------------------------------------
DELAI:
ld a,255 ; c'est la valeur de départ, plus elles est haute, plus c'est lent
; a ne peut contenir qu'une valeur entre 0 et 255
DEL:
dec a ; c'est la boucle qui va décrémenter de 1 jusqu'à a=0
jp nz,DEL ; si a n'est pas égale à 0 on repart au début de la boucle
ret ; si a=0 on retourne au programme principal
;------------------------------------------------------------------------------
; Boucle du jeu : Le programme démarre à DEBUT puis va à BOUCLE
;------------------------------------------------------------------------------
DEBUT: ; c'est ici que débute le programme !!!
call INIT ; on appelle le sous-prog INIT
BOUCLE: ; c'est la boucle principale du programme !!!
call DELAI ; on appelle le sous-prog DELAI afin de ralentir l'exécution
ld a,8 ; on teste la touche gauche = ligne 8 de la matrice du clavier
call 321 ; (bios) SNSMAT retourne dans la valeur de la ligne 8
bit 4,a ; bit teste la colonne 4 (0=pressé 1=non pressé)
call z,FUSGAU ; si résultat=0 on va au sous-prog FUSGAU (déplacer à droite)
call DELAI ; on ralenti l'exécution
ld a,8 ; la touche droite est aussi sur la ligne 8 de la matrice
call 321 ; on appelle le bios pour qu'il donne la valeur de la ligne 8
bit 7,a ; la colonne 7 est testée (0=pressé 1=non pressé)
call z,FUSDRO ; si résultat=0 on va au sous-prog FUSDRO (déplacer àgauche)
call DELAI ; on ralenti l'exécution
jp BOUCLE ; on redémarre la boucle principale
;------------------------------------------------------------------------------
; Les données
;------------------------------------------------------------------------------
donnee:
db 24,24,126,126 ; sprite 1 pour la TGS
db 255,255,255,0 ; sprite 1 " "
db 60,126,153,255 ; sprite 2 " "
db 102,60,66,36 ; sprite 2 " "
db 24,24,24,24 ; sprite 3 " "
db 24,24,24,24 ; sprite 3 " "
donne1:
db 170,100,0,15 ; Attributs du sprite 0 (position y, x, n°sprite,couleur)
db 0,0,1,12 ; Attributs du sprite 1 (position y, x, n°sprite,couleur)
db 200,0,2,1 ; Attributs du sprite 2 (position y, x, n°sprite,couleur)
;**************************************************************************************************
; Standard Libraries
;**************************************************************************************************
INCLUDE "MSXRom-Lib.asm" ; intègre la librairie MSX
END: EQU $
indcol: EQU 41000 ; défini l'adresse de la variable indcol (registre des collisions)
indbal: EQU 41001 ; défini l'adresse de la variable indbal
inddir: EQU 41002 ; défini l'adresse de la variable inddir
;**************************************************************************************************
; RAM Definitions
;**************************************************************************************************
;-------------------
; Définit que le programme démarrera au début de la RAM disponible
;-------------------
ORG RAMSTART