meta données pour cette page
La pratique
On va maintenant taper notre 1er code du shoot-em-up.
On va enlever la partie du code correspondant à l’affichage du caractère (qui était notre exemple précédent), et donc repartir du fichier sources vierge exemple.asm.
Quelques informations sur le début du code :
Le programme est en SCREEN 1, 3 SPRITES vont être créés (la fusée du joueur, l’ennemi/envahisseur, le tir) On charge les formes des SPRITES dans la table TGS On charge les attributs des SPRITES dans la table TAS On va positionner 3 indicateurs qui nous serviront à déplacer les SPRITES plus tard. On spécifie la taille des SPRITES
Voici l’entête, j’ai juste modifié le nom du fichier qui sera généré via FNAME :
; **************************************************************** ; MSX ROM Cartridge Header and Function library ; **************************************************************** FNAME "test1.ROM" cpu z80 ORG 4000h INCLUDE "MSXRom-Include.asm" db "AB" DW INIT ; Fait démarrer la ROM au label INIT DW 0 DW 0 DW 0 DS 6
SCREEN 1
Passons au début du code proprement dit et par le label (appelé aussi “Etiquette”) ‘INIT:’ (c’est par lui que la .ROM démarrera) :
;------------------------------------------------------------------------------ ; Démarrage du programme à INIT ;------------------------------------------------------------------------------ INIT: LD A,1 ; 1 pour screen 1 ld [64687],A ; (bios)SCRMOD adresse $FCAF :mode courant de l'ecran call 95 ; (bios) CHGMOD : change le mode l'écran
Pas de modification par rapport aux précédent exemple, on se place en SCREEN 1.
CHARGEMENT DE LA TGS
;------------------------------------------------------------------------------ ; Charge la TGS avec la forme des 3 sprites ;------------------------------------------------------------------------------ ld hl,donnee ; va chercher les données TGS au 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
Je commence par la fin, on va donner un ordre de transfert de données (les formes des SPRITES) qui seront écrites dans notre code (donc dans la RAM) vers la table TGS (donc la VRAM), on ne va pas pouvoir utiliser un ordre du style “écrire dans la VRAM” car cela est limité à une adresse mémoire et une donnée.
Pour de gros blocs de données entre RAM et VRAM, il existe une routine BIOS qui fait le boulôt : LIDRVM ou en langage assembleur CALL 92 (en décimal) ou CALL $5C (en hexadécimal). Cette routine, pour être déclenchée, a besoin de 3 paramètres en entrée :
HL, pour les données des formes de sprites DE, l’adresse de la TGS à partir de laquelle on veut écrire (une donnée = une case mémoire) BC, la longueurde bloc total à transférer (en octets)
Voici d’ailleurs une définition de cette routine :
LDIRVM Address : #005C Function : Block transfer to VRAM from memory Input : BC - blocklength DE - Start address of VRAM HL - Start address of memory Registers: All
Vu que l’on a 3 SPRITES à charger (8 pixels sur 8 pixels donc 8 octets) on a 3 x 8 = 24 données à envoyer en VRAM. On ne va pas les taper juste après la virgule du HL il ne saurait pas le traiter. On va plutôt lui dire où il peut les trouver et les “digérer à son rythme”, pour cela on va créer un nouveau label donnee qui abritera ces 24 données.
Les données en général sont stockées plutôt à la fin du programme codé et chaque ligne doit commencer par un DB.
Ce label donnee je l’ai placé juste avant le pied de page, voici comment il se présente :
;------------------------------------------------------------------------------ ; Les données ;------------------------------------------------------------------------------ donnee: db 24,24,126,126 ; sprite 0 pour la TGS db 255,255,255,0 ; sprite 0 db 60,126,153,255 ; sprite 1 db 102,60,66,36 ; sprite 1 db 24,24,24,24 ; sprite 2 db 24,24,24,24 ; sprite 2
Pour le SPRITE 0, les 2 premières lignes donnent 8 données qui forment un SPRITE joueur (la fusée) :
Pour le SPRITE 1, les 2 lignes suivantes qui donnent la forme suivante pour l’ennemi :
Pour le SPRITE 2, les 2 dernières lignes qui donnent la forme suivante pour le tir :
Du coup, tout devient simple:
- On dit à
HLde se charger (via l’instruction ‘LD’) en allant chercher les données que l’on a mis en fin de code dont le label estdonnee. - On charge en
DEle début de l’adresse de la TGS (je vous ai dit dans un des premiers articles qu’elle débutait à 14336 en SCREEN 1). - On charge en
BCla longueur du blocdonnee - On exécute
CALL 92qui fera le boulot vu qu’on lui a donné les entrants.
CHARGEMENT DE LA TAS
;------------------- ; 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 ; va chercher 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
J’y ai intégré un petit exercice de manière à progresser au fur et à mesure du code :
Je vous ai dit qu’en SCREEN 1 la table des attributs de SPRITE (TAS) débutait à l’adresse 6912. Sur MSX on pourrait changer cette adresse pour diverses raisons (et d’ailleurs, ces adresses de tables sont succeptibles de changer en fonction des modes SCREEN que l’on utilise).
Admettons que l’on ne sait pas si la TAS est bien positionnée pour débuter à 6912. Je vous ai expliqué précédemment qu’en VRAM, il existait des tables et des registres, et bien chaque table est gérée via un de ces registres.
Pour notre TAS c’est le registre 5.
Toute valeur que l’on écrira dans ce registre sera automatiquement multipliée par 128 et définira l’adresse de début de la table TAS. Donc pour obtenir 6912, on le divise par 128 et cela donne 54.
Finalement, pour définir la TAS à 6912, il suffit d’écrire dans le registre 5 la valeur 54.
C’est l’objet des 3 premières lignes du code ci-dessus jusqu’au CALL 71 (ou 47 en hexa) qui est la routine BIOS qui existe pour écrire dans les registres du VDP :
WRTVDP Address : #0047 Function : write data in the VDP-register Input : B - data to write C - number of the register Registers: AF, BC
Fin de l’exercice.
Passons au chargement de la TAS : d’abord la procédure va ressembler au PUTSPRITE du BASIC MSX donc cela devrait vous paraître simple et ensuite on va procéder au codage assembleur de la même manière que pour la TGS.
On va donner 4 infos pour chaque SPRITE créé :
La position verticale du SPRITE à l’écran La position horizontale du SPRITE à l’écran Le numéro du SPRITE La couleur du SPRITE
Mais avant de décrire le bout de code, il faut préciser une particularité importante des SPRITES, ils ne se localisent pas comme les caractères (32 colonnes sur 24 lignes) mais sur des coordonnées plus fines.
En SCREEN 1 c’est 256 pixels sur 192 pixels :
Du coup, on va pas trop s’attarder sur la méthode car c’est la même que pour la TGS et le commentaire associé au code est explicite. On va plutôt détailler le label donne1 associé (que j’ai placé au niveau du code, après le label donnee et avant le pied de page).
Résolution graphique SCREEN 1
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)
Je vous avais dit qu’il y a 4 informations à rentrer par SPRITE dans la TAS.
Du coup, pour le SPRITE de la 1ere ligne, il aura au 1er affichage à l’écran la coordonnée vertical=170 horizontal=100 pour le SPRITE numéro 0 et qui aura la couleur 15 (blanc), vous avez reconnu le sprite joueur/fusée.
Je vous laisse décrypter les 2 autres lignes pour les 2 autres SPRITES.
Juste pour finir sur ce code, on voit que l’on a 12 données à rentrer, cela tombe bien c’est ce que l’on charge en BC avant de lancer le CALL 92 qui devient familier pour vous maintenant.
INDICATEURS DE DEPLACEMENT
Au début de l’article j’ai listé le fait que l’on allait positionner 3 indicateurs nécessaire au déplacement des sprites. On ne va pas les utiliser de suite mais un minimum d’information quand même :
indcol : un indicateur de collision indbal : un indicateur de balle tirée inddir : un indicateur de direction pour l’ennemi
Pour que le programme nous autorise à manipuler des valeurs avec ces termes, on doit d’abord les déclarer comme suit :
indcol: EQU 41000 ; défini l'adresse de la variable indcol indbal: EQU 41001 ; défini l'adresse de la variable indbal inddir: EQU 41002 ; défini l'adresse de la variable inddir
Ainsi on dit au programme qu’à chaque fois que l’on utilisera indcol cela voudra dire en fait que l’on sollicite l’adresse 41000 de la RAM (un choix arbitraire d’adresse libre en RAM). On pourrait traduire EQU par ‘équivaut’.
Idem pour les 2 autres déclarations. ce code je l’ai placé dans le bas de page, mais vous pourriez le mettre dans l’entête, cela n’a pas grande importance.
Ensuite, je vais assigner des valeurs à ces adresses, pour cela je place le code suivant à la suite du code sur le chargement de la TAS puisque cela fait partie de l’initialisation du jeu :
;------------------------------------------------------------------------------ ; Initialise les 3 variables ; indcol: indicateur de collision 41000 (adresse) ; indbal: indicateur de balle tirée 41001 (adresse) ; inddir: indicateur de direction de l'ennemi 41002 (adresse) ;------------------------------------------------------------------------------ ld a,1 ld [indcol],a ; indcol = 1 ld [indbal],a ; indbal = 1 ld [inddir],a ; inddir = 1
Rien d’exceptionnel, je charge ces pseudo adresses (pour cela, je les passe entre crochets) avec un A qui vaut 1 puisque dans la 1ere ligne j’ai assigné 1 à l’accumulateur A.
TAILLE DES SPRITES
;------------------------------------------------------------------------------ ; Taille des sprites agrandis (registre 1) ;------------------------------------------------------------------------------ ld c,1 ; registre 1 gère la talle des sprites ld b,225 ; 225 c'est la valeur pour agrandir un sprite 8 x 8 call 71 ; WRTVDP Ecrire dans le VDP (VRAM)
Sur MSX on peut agir sur la taille des SPRITES, pour cela on utilise le registre 1 de la VRAM qui accepte 4 valeurs possibles :
SPRITES taille 8 x 8 normal = 224 SPRITES taille 8 x 8 agrandis = 225 SPRITES taille 16 x 16 normal = 226 SPRITES taille 16 x 16 agrandis = 227
Vu que l’on a déclaré dans la TGS des SPRITES de taille 8 x 8, on peut utiliser la valeur 224 ou 225 (vous pouvez vous amuser à changer la valeur pour voir la différence), dans notre jeux on va utiliser 225.
Le code devrait donc vous parler maintenant : on charge ‘C’ avec le numéro du registre 1 et ‘B’ avec la valeur à écrire dans ce registre, ce sont les 2 entrants nécessaires au CALL 71 qui permet d’exécuter un ordre d’écriture dans un registre VRAM.
C’était le dernier bout de code du label INIT:
Si on veut voir le résultat il suffit de rajouter une boucle infinie, pour que le programme enchaine dessus après avoir exécuté toutes les actions du INIT:, comme cela par exemple :
LOOP: jp LOOP
Vous pouvez faire F9 dans CONTEXT, pour lancer la compilation, le fichier généré va s’appeler test1.ROM et c’est celui-là que vous faites glisser dans l’émulateur blueMSX.
Vous allez voir apparaître ceci :
Mais où est passé le SPRITE représentant la balle (ou le laser si vous voulez) ?
Le premier réflex est de regarder ce que l’on a déclaré comme coordonnées pour le SPRITE 2 qui le représente dans la TAS.
Et que voit-on ? Position verticale = 200….alors que la résolution nous permet d’aller à un maximum de 191. En le positionnant au delà il disparait dans la bordure de l’écran mais il est déjà présent et il sera plus rapide à positionner correctement lorsqu’on tirera que si on devait le charger dans la TAS puis calculer sa position au moment du tir.
Si vous voulez le voir apparaitre et bien jouez avec ses coordonnées, compilez et essayez votre .ROM dans l’émulateur.
Voici le code complet de cet article :
; **************************************************************** ; 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 ;------------------------------------------------------------------------------ ; Démarrage du programme à INIT ;------------------------------------------------------------------------------ INIT: LD A,1 ; 1 pour screen 1 ld [64687],A ; (bios)SCRMOD adresse $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 ; va chercher les données TGS au 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 ; va chercher 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 41000 (adresse) ; indbal: indicateur de balle tirée 41001 (adresse) ; inddir: indicateur de direction de l'ennemi 41002 (adresse) ;------------------------------------------------------------------------------ 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 talle des sprites ld b,225 ; 225 c'est la valeur pour agrandir un sprite 8 x 8 call 71 ; WRTVDP Ecrire dans le VDP (VRAM) LOOP: jp LOOP ;------------------------------------------------------------------------------ ; 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 x, y,n°sprite,couleur) db 0,0,1,12 ; Attributs du sprite 1 (position x, y,n°sprite,couleur) db 200,0,2,1 ; Attributs du sprite 2 (position x, y,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 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