Obligement - L'Amiga au maximum

Dimanche 14 septembre 2025 - 01:05  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

Actualité (récente)
Actualité (archive)
Comparatifs
Dossiers
Entrevues
Matériel (tests)
Matériel (bidouilles)
Points de vue
En pratique
Programmation
Reportages
Quizz
Tests de jeux
Tests de logiciels
Tests de compilations
Trucs et astuces
Articles divers

Articles in English


Réseaux sociaux

Suivez-nous sur X




Liste des jeux Amiga

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z,
ALL


Trucs et astuces

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Glossaire

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Galeries

Menu des galeries

BD d'Amiga Spécial
Caricatures Dudai
Caricatures Jet d'ail
Diagrammes de Jay Miner
Images insolites
Fin de jeux (de A à E)
Fin de Jeux (de F à O)
Fin de jeux (de P à Z)
Galerie de Mike Dafunk
Logos d'Obligement
Pubs pour matériels
Systèmes d'exploitation
Trombinoscope Alchimie 7
Vidéos


Téléchargement

Documents
Jeux
Logiciels
Magazines
Divers


Liens

Associations
Jeux
Logiciels
Matériel
Magazines et médias
Pages personnelles
Réparateurs
Revendeurs
Scène démo
Sites de téléchargement
Divers


Partenaires

Annuaire Amiga

Amedia Computer

Relec


A Propos

A propos d'Obligement

A Propos


Contact

David Brunet

Courriel

 


Programmation : Assembleur - Afficher des sprites et des BOB sur Amiga OCS et AGA (partie 1)
(Article écrit par Yragael et extrait de www.stashofcode.fr - juillet 2018, mis à jour en octobre 2018)


Quoi de plus confortable qu'un sprite ? Le coprocesseur graphique l'affiche en laissant voir le décor derrière ses pixels transparents, et il préserve le décor derrière ses autres pixels pour le restaurer quand le sprite est déplacé. Par ailleurs, il découpe le sprite s'il sort de l'écran.

Malheureusement, les capacités de l'Amiga 500 sont en la matière très limitées. Huit sprites de 16 pixels de large, même de hauteur infinie, en quatre couleurs dont une transparente, seulement ? C'est la dèche...

Les sprites n'en conservent pas moins une certaine utilité, à condition d'en utiliser pleinement le potentiel. Ainsi, il est possible de les attacher pour former jusqu'à un bitmap de 64 pixels de large, de hauteur infinie, en 16 couleurs dont une transparente, notamment. Ou alors, il est possible d'en réutiliser pour créer un champ de jeu ("playfield") supplémentaire, dont le contenu devra toutefois être un motif répétitif qui, dans le meilleur des cas, occupera 48 pixels de large sur une hauteur infinie, en peu de couleurs. Par ailleurs, l'Advanced Graphics Architecture, dont l'Amiga 1200 est doté, dispose de fonctionnalités un peu étendues en matière de sprites. En particulier, leur largeur unitaire passe de 16 à 64 pixels.

Afficher des sprites et des BOB sur Amiga OCS et AGA
Des sprites un peu plus mieux en AGA

Comment afficher des sprites ? Et utiliser les sprites pour afficher un gros et beau bitmap, ou un champ de jeu supplémentaire ? Et enfin utiliser les sprites sur AGA, sachant que Commodore n'a jamais documenté ce matériel ? Tout cela et plus en encore dans ce premier article consacré à l'affichage de bitmaps sur Amiga OCS et AGA.
  • Mise à jour du 08/07/2018 : correction d'un bogue dans triplePlayfield.s.
  • Mise à jour du 01/10/2018 : tous les sources ont été modifiés pour intégrer une section "StingRay's stuff" qui permet d'assurer le bon fonctionnement sur tous les modèles d'Amiga, notamment dotés d'une carte graphique.
Cliquez ici pour télécharger l'archive contenant le code et les données des programmes présentés dans cet article. Cette archive contient plusieurs sources :
  • spriteCPU.s pour le simple affichage d'un sprite au processeur (c'est-à-dire sans DMA).
  • sprites.s pour le simple affichage et le déplacement d'un sprite.
  • sprites16.s pour le simple affichage et le déplacement d'un sprite en 16 couleurs.
  • spritesField.s pour la réutilisatoin de sprites comme pour un fond étoilé.
  • spritesCollision.s pour la détection de collisions entre deux sprites, et entre ces sprites et un plan de bits.
  • triplePlayfield.s pour l'affichage d'un plan de sprites (avec double jeu de jeu tant qu'on y est).
  • spritesAGA.s pour l'affichage de sprites exploitant le jeu de puces AA.
NB : cet article se lit mieux en écoutant l'excellent module composé par Spirit/LSD pour le magazine sur disquette Graphevine #14, mais c'est affaire de goût personnel...

Le B-A-BA : afficher un sprite, pour commencer

Cet article présume une certaine familiarité, pour ne pas dire une familiarité certaine, avec la programmation en assembleur du matériel de l'Amiga. Pour s'initier à cette dernière, et notamment installer un environnement de développement, le mieux est se reporter à la série d'articles publiés ici, qui portent sur la programmation d'un défilement sinusoïdal : 1, 2, 3, 4, et 5.

L'Amiga dispose d'un coprocesseur graphique nommé Copper. Le Copper exécute une série d'instructions à chaque rafraîchissement de l'écran. Ces instructions sont notamment des écritures dans des registres du matériel, dont certains contrôles les sprites.

Le matériel peut afficher huit sprites de 16 pixels de large en quatre couleurs dont une couleur transparente, sur une hauteur illimitée. Les sprites sont couplés (le 0 avec le 1, le 2 avec le 3, etc.). Dans un couple, les sprites partagent la même palette de quatre couleurs, qui n'est qu'un sous-ensemble de la palette de 32 couleurs utilisée pour afficher des pixels à l'écran. Dans ces conditions, la structure de cette dernière palette est la suivante (la mention aux champs de jeu sera expliquée plus tard) :

Couleurs Usage
00 à 07 Champ de jeu 1 (plans de bits 1, 3 et 5)
08 à 15 Champ de jeu 2 (plans de bits 2, 4 et 6)
16 à 19 Sprites 0 et 1
20 à 23 Sprites 2 et 3
24 à 27 Sprites 4 et 5
28 à 31 Sprites 6 et 7

Un sprite est intégralement décrit par une suite de mots. Les deux premiers sont des mots de contrôle, les suivants sont des mots décrivant ligne par ligne de petits plans de bits, et les derniers sont des 0. Par exemple, pour un sprite de deux lignes :

sprite:		DC.W $444C, $5401			;Mots de contrôle
		DC.W $5555, $3333			;Première ligne de 16 pixels
		DC.W $1234, $5678			;Seconde ligne de 16 pixels
		DC.W 0, 0				;Fin

Pour chaque ligne du sprite, les deux mots sont combinés entre eux pour en déduire les indices des couleurs des 16 pixels de la ligne. Pour poursuivre sur cet exemple, le résultat pour la première ligne est celui représenté ici :

Afficher des sprites et des BOB sur Amiga OCS et AGA
Combinaison des mots d'une ligne de données du sprite 0

Ainsi qu'il est possible de le constater, le premier mot de la ligne fournit les bits 0 de la ligne, et le second mot en fournit les bits 1.

Les coordonnées (X, Y) et la hauteur d'un sprite (déduite de l'ordonnée Y + DY de la ligne qui suit la dernière ligne du sprite) sont codées dans les mots de contrôle de manière assez exotique (le bit 7 du second mot est réservé pour l'attachement, ce dont il sera question plus loin). Par exemple, pour afficher un sprite en haut à gauche de l'écran qui débute en classiquement en ($81, $2C) :

Afficher des sprites et des BOB sur Amiga OCS et AGA
Codage des mots de contrôle d'un sprite de DY pixels de hauteur affiché en (X, Y)

Oui, contrairement à ce que prétend l'Amiga Hardware Reference Manual, il faut bien coder X - 1 et non X dans les mots de contrôle. C'est une erreur de la documentation.

Concrètement, il faut donc coder les mots de contrôle ainsi (dans ASM-One, le point d'exclamation est un OR, ">>" est un décalage non signé sur la droite) :

sprite:			DC.W ((Y&$FF)<<8)!(((X-1)&$1FE)>>1)
				DC.W (((Y+DY)&$FF)<<8)!((Y&$100)>>6)!(((Y+DY)&$100)>>7)!((X-1)&$1)

Pour s'éviter de tels calculs chaque fois qu'il faut déplacer un sprite, une technique consiste à précalculer des mots à combiner pour chacune des 320 positions horizontales, d'une part, et chacune des positions verticales, d'autre part. Pour en savoir plus, référez-vous à ce fil de discusion sur le forum d'English Amiga Board.

La suite des mots constituant les données d'un sprite étant écrite, il suffit d'en communiquer l'adresse (qui doit être paire) au matériel pour qu'il affiche le sprite. Chaque sprite dispose de registres SPRxPTH et SPRxPTL pour cela. Par exemple :

	lea #dff000,a5
	move.l #sprite,d0
	move.w d0,SPR0PTL(a5)			;$DFF122
	swap d0
	move.w d0,SPR0PTH(a5)			;$DFF120

Dans les faits, les adresses des sprites ne sont jamais communiquées ainsi par le processeur. Des instructions MOVE sont rajoutées dans la liste Copper pour que le Copper les communique à chaque trame :

	lea copperList,a0
	;... (début de la liste Copper)
	move.l #sprite,d0
	move.w #SPR0PTL,(a0)+
	move.w d0,(a0)+
	swap d0
	move.w #SPR0PTH,(a0)+
	move.w d0,(a0)+
	;... (suite de la lite Copper)

Les sprites ne sont affichés que si le matériel peut accéder à leurs données, ce pour quoi il bénéficie d'un accès direct en mémoire (DMA). L'usage du DMA n'est pas incontournable - il est possible de s'y substituer en écrivant au processeur dans les divers registres où le DMA écrit les données des sprites pour les communiquer au matériel : SPRxPOS, SPRxCTRL, SPRxDATB et SPRxDATA. Le programme spriteCPU.s procède ainsi :

	;Attendre la première ligne du sprite pour commencer à l'afficher

_waitSpriteStart:
	move.l VPOSR(a5),d0
	lsr.l #8,d0
	and.w #$01FF,d0
	cmpi.w #SPRITE_Y,d0
	blt _waitSpriteStart

	;Afficher le sprite. Il faut écrire dans SPRxDATA en dernier, car c'est le moyen de déclencher l'affichage du sprite.

	move.w #((SPRITE_Y&$FF)<<8)!(((SPRITE_X-1)&$1FE)>>1),SPR0POS(a5)
	move.w #(((SPRITE_Y+SPRITE_DY)&$FF)<<8)!((SPRITE_Y&$100)>>6)!(((SPRITE_Y+SPRITE_DY)&$100)>>7)!((SPRITE_X-1)&$1),SPR0CTL(a5)
	move.w #$0F0F,SPR0DATB(a5)
	move.w #$00FF,SPR0DATA(a5)

	;Attendre la ligne de milieu du sprite pour le repositionner horizontalement (8 pixels plus à droite) et modifier ses données

_waitSpriteMiddle:
	move.l VPOSR(a5),d0
	lsr.l #8,d0
	and.w #$01FF,d0
	cmpi.w #SPRITE_Y+(SPRITE_DY>>1),d0
	blt _waitSpriteMiddle
	move.w #((SPRITE_Y&$FF)<<8)!(((SPRITE_X+8-1)&$1FE)>>1),SPR0POS(a5)

	;Écrire dans SPRxCTL arrête l'affichage du sprite, ce qui interdit de repositionner le sprite horizontalement
        ;à la précision d'un pixel, à moins de le réarmer par une écriture dans SPRxDATA, car le bit 0 de cette
        ;position est dans SPRxCTL. Autrement dit, les trois lignes qui suivent ne sont nécessaires que si la
        ;nouvelle position horizontale est impaire.

	move.w #(((SPRITE_Y+SPRITE_DY)&$FF)<<8)!((SPRITE_Y&$100)>>6)!(((SPRITE_Y+SPRITE_DY)&$100)>>7)!((SPRITE_X+9-1)&$1),SPR0CTL(a5)
	move.w #$F0F0,SPR0DATB(a5)
	move.w #$FF00,SPR0DATA(a5)

	;Attendre la dernière ligne du sprite pour cesser de l'afficher en écrivant n'importe quoi dans SPRxCTL.

_waitSpriteEnd:
	move.l VPOSR(a5),d0
	lsr.l #8,d0
	and.w #$01FF,d0
	cmpi.w #SPRITE_Y+SPRITE_DY,d0
	blt _waitSpriteEnd
	move.w #$0000,SPR0CTL(a5)

Toutefois, cette technique présente peu d'intérêt, sinon aucun. C'est que pour afficher un sprite, il faudrait donc écrire une boucle qui attendrait le faisceau d'électron à chaque ligne à laquelle une ligne du sprite devrait être affichée avant de modifier le contenu des registres SPRxDATB et SPRxDATA avec les données de la nouvelle ligne du sprite, ce qui serait extrêmement contraignant.

Partant, le DMA n'est pas émulé, mais utilisé. Pour afficher les sprites, il est donc nécessaire d'activer aux moins les canaux DMA du Copper, des plans de bits et des sprites :

	move.w #$83A0,DMACON(a5)		;DMAEN=1, BPLEN=1, COPEN=1, SPREN=1

Comme cela peut le suggérer, il n'existe pas de mécanisme pour activer sélectivement tel ou tel des huit sprites. Pour ne pas afficher un sprite, il faut spécifier au matériel que la hauteur du sprite est nulle, ce qui s'effectue en faisant pointer ses données sur des mots de contrôle à 0 :

spriteVoid:		DC.W 0, 0

Tant qu'il est question de DMA, un point de détail, mais qui a tout de même son importance, doit être mentionné. Il faut attendre un blanc vertical pour couper le canal DMA des sprites. A défaut, si un sprite était en cours d'affichage, ses données continuent d'être affichées. Cela produit un effet bien connu de "sprite qui bave verticalement".

En effet, comme expliqué précédemment lorsqu'il a été question de l'émuler au processeur, le DMA ne sert qu'à alimenter des registres SPRxPOS, SPRxCTL, SPRxDATA et SPRxDATB dans lesquels le matériel lit systématiquement les données des sprites qu'il combine à celle des plans de bits. S'il est coupé avant d'avoir écrit le dernier jeu de mots à 0 dans SPRxPOS et SPRxCTL (ce qu'il fait lorsqu'il sait que la hauteur du sprite a été parcourue, ce qui explique pourquoi ces deux mots doivent figurer à la fin des données d'un sprite), le DMA ne peut donc interrompre l'affichage du sprite. Partant, ce dernier continue d'être affiché sur tout le reste de la hauteur de l'écran avec les données figurant dans SPRxDATA et SPRxDATB au moment où le DMA a été coupé.

C'est pourquoi les programmes proposés attendent le blanc vertical (VERTB), c'est-à-dire le moment où le faisceau d'électrons a terminé de tracer l'écran, pour couper le DMA (la boucle est factorisée dans la sous-routine _waitVERTB) :

_waitVERTBLoop:
	move.w INTREQR(a5),d0
	btst #5,d0
	beq _waitVERTBLoop
	move.w #$07FF,DMACON(a5)

La cinétique : déplacer les sprites et détecter les collisions

Pour déplacer un sprite, il suffit de modifier ses coordonnées dans ses mots de contrôle à la fin d'une trame, avant que le matériel n'en refasse la lecture pour afficher la nouvelle trame. Le programme "sprite.s" fait ainsi se déplacer un sprite 0 de 16 pixels de hauteur en quatre couleurs, sur un décor composé d'un unique plan de bits représentant un damier :

Afficher des sprites et des BOB sur Amiga OCS et AGA
Le sprite 0, en quatre couleurs, circulant paisiblement sur un plan de bits

Le matériel permet de gérer la profondeur via un système de priorités :
  • Entre sprites d'abord, les priorités sont figées : le sprite 0 est toujours affiché devant le sprite 1, qui est toujours affiché devant le sprite 2, etc.
  • Entre sprites et champ de jeu ensuite (pour rappel, le matériel peut afficher un champ de jeu de 1 à 6 plans de bits, ou deux champs de jeu de 1 à 3 plans de bits chacun - c'est le double champ de jeu), les priorités peuvent être ajustées via le registre BPLCON2.
BPLCON2 se compose ainsi :

Bits Usage
15 à 7 Inutilisé (mettre à 0)
6 PF2PRI
5 à 3 PF2P2 à PFR2P0
2 à 0 PF1P2 à PF1P0

Dans le cas d'un seul champ de jeu, PF2P2-0 (et non PF1P2-0, comme on l'aurait présumé) sont utilisés. Ils permettent de programmer sur 3 bits le numéro du couple de sprites (et non du sprite !) derrière lequel se trouve le champ de jeu :

Afficher des sprites et des BOB sur Amiga OCS et AGA
Ah ! Je vous ai bien eu. Cet article traite d'Amiga et non d'Androïd !

En double champ de jeu, PF1P2 à PF1P0 permettent de gérer pareillement la priorité du second champ de jeu et des couples de sprites. Noter que ces bits traitent alors bien du champ de jeu 1 (plans de bits 1, 3 et 5) tandis que PF2P2-0 traitent du champ de jeu 2 (plans de bits 2, 4 et 6). Le bit PF2PRI permet de passer le champ de jeu 2 devant le 1, ce qui rajoute aux possibilités de priorisation entre sprites et champs de jeu.

Dans les exemples déjà cités des sprites en 4 et 16 couleurs, le sprite 0 est positionné devant l'unique champ de jeu en stockant donc $0008 dans BPLCON2.

Enfin, il faut mentionner que le matériel permet de détecter facilement une collision entre sprites, ou entre sprite et plans de bits, et cela au pixel près. Le programme "spritesCollision.s" montre comment il est possible de détecter les collisions entre le sprite 0 et le sprite 2, et entre chacun de ces sprites et le plan de bits 1 :

Afficher des sprites et des BOB sur Amiga OCS et AGA
Détection de collisions entre sprites et entre sprites et plan de bits

La détection des collisions est assez subtile. Elle repose sur l'emploi de deux registres :
  • CLXCON permet de spécifier quels sprites et quels plans de bits impliquer.
  • CLXDAT permet de récupérer l'état de la détection de collisions qui en résulte.
La détection de collisions implique toujours les sprites pairs. Dans CLXCON :
  • Les quatre bits ENSP7-1 permettent de spécifier que les sprites impairs doivent aussi être impliqués. Noter que cela ne permet pas de détecter distinctement les collisions du sprite 0 et les collisions du sprite 1, mais de détecter les collisions de la combinaison par OR des sprites 0 et 1 : ce n'est donc vraiment utile que si ces sprites sont attachés pour afficher un sprite en 16 couleurs.
  • Les six bits ENBP6-1 permettent de spécifier si les plans de bits correspondants doivent être impliqués (si tous sont exclus, une collision avec les plans de bits est systématiquement rapportée, quelles que soient les valeurs des bits MVBP6-1).
  • Les six bits MVBP6-1 permettent de spécifier la valeur du bit dans un plan de bits impliqué dans la détection de collisions qui permettra de déterminer s'il y a ou non collision avec ce plan de bits.
Dans le programme "spritesCollision.s", la valeur de CLXCON est $0041, ce qui revient à positionner le bit ENBP1 pour impliquer le plan de bits 1 dans la détection de collisions, et le bit MVBP1 pour spécifier qu'une collision avec le plan de bits 1 a lieu quand un bit à 1 est rencontré dans ce dernier.

Il suffit de lire CLXDAT (attention, car cela le remet à 0) pour récupérer à chaque trame l'état des collisions détectées entre deux entités (entre sprites, ou entre sprite et plan de bits, ou entre plans de bits). Sur cette figure, on trouve à côté de chaque bit une case par sprite (0 à 7) ainsi qu'une case pour les plans de bits impairs (I) et une pour les plans de bits pairs (P). Les cases sont colorées pour indiquer la collision détectée (première entité en rouge, seconde en vert).

Afficher des sprites et des BOB sur Amiga OCS et AGA
La composition du registre CLXDAT

Par exemple, le bit 7 sera positionné si une collision est détectée entre :
  • Le sprite 4, éventuellement combiné par OR avec le sprite 5 si ENSP14 a été positionné.
  • Les plans de bits impairs, parmi ceux dont les bits ENBP ont été positionné, et pour le cas où un bit de valeur MVBP a été rencontré.
Dans le programme "spritesCollision.s", les bits 9, 1 et 2 sont ainsi testés.

En permettant de spécifier sur quelle valeur de bit une collision doit être détectée entre un sprite et certains plans de bits, le matériel permet très facilement de spécifier qu'une collision ne doit être détectée que si un sprite rencontre un pixel d'un certaine couleur (éventuellement transparente) dans le décor. Puissant.

Toutefois, cette détection au pixel près n'est pas nécessairement intéressante. Un codeur de jeu vidéo sur Amiga m'a expliqué qu'il convenait mieux d'utiliser des zones de collisions sommaires, incluses dans les entités et le décor, pour moins frustrer le joueur. A cet égard, la détection de collisions du matériel reste rudimentaire. Peut-être a-t-elle été intégrée en référence à une époque où les pixels étaient si gros que cela ne posait pas de problème de jouabilité.

Plus de sprites : attacher et/ou réutiliser (multiplexer) les sprites

Avec quatre couleurs, dont une transparente, la palette d'un sprite est particulièrement limitée. Sans doute, il est possible de superposer des sprites pour multiplier les couleurs, mais superposer deux sprites ne permet d'étendre la palette qu'à huit couleurs, dont deux transparentes. Par ailleurs, il faut se rappeler que les sprites sont couplés, et que les sprites d'un même couple partagent la même palette de quatre couleurs. Au mieux, il serait donc possible de superposer quatre sprites, étendant la palette à 16 couleurs, dont qutre transparentes. Sachant qu'il y a huit sprites, il ne serait donc possible que d'afficher deux sprites en douze couleurs ?

Non. Comme mentionné plus tôt, les deux sprites formant un couple peuvent être combinés pour former un unique sprite, toujours de 16 pixels de large, mais cette fois affiché en 16 couleurs (les couleurs 16 à 31 de la palette de l'écran, la couleur 16 étant transparente). Les lignes du premier sprite fournissent les bits des petits plans de bits 1 et 2 du sprite, tandis que les lignes du second sprite fournissent les bits des petits plans de bits 3 et 4.

Pour combiner ainsi deux sprites d'un couple, il faut positionner le bit 7 du second mot de contrôle du second sprite du couple : c'est l'attachement. Par ailleurs, les deux sprites doivent être affichés à la même position, ce qui implique donc de les déplacer simultanément - là où ils ne se chevauchent pas, chacun est affiché dans la palette commune de quatre couleurs.

Le programme "sprite16.s" fait ainsi se déplacer un sprite de 16 couleurs composé des sprites 0 et 1 sur le même décor que précédemment :

Afficher des sprites et des BOB sur Amiga OCS et AGA
Les sprites 0 et 1 combinés, en 16 couleurs, circulant paisiblement sur un plan de bits

Les possibilités d'enrichir l'affichage des sprites ne s'arrêtent pas là. Si le matériel ne peut afficher un sprite qu'une fois par ligne de l'écran, il est parfaitement possible de lui demander de modifier la position d'un sprite d'une ligne à l'autre. Cela permet de démultiplier les sprites. Cette technique, aussi dite "multiplexage", est utilisée pour produire notamment des fonds étoilés, comme l'illustre le programme "spritesField.s" :

Afficher des sprites et des BOB sur Amiga OCS et AGA
Réutiliser les sprites d'une ligne à l'autre pour produire un fond étoilé (multiplexage vertical)

Noter que ce programme est doublement limité, car non seulement seul le sprite 0 est réutilisé, ce qui ne permet pas de produire un effet de profondeur, mais de plus le motif du sprite n'est pas modifié d'une occurrence à l'autre, ce qui rend l'apparence très monotone. En jouant sur ces deux paramètres, il est facile de faire mieux en un instant.

Pour y parvenir, il suffit de modifier la structure des données du sprite. Les deux derniers mots à 0 doivent être remplacés par de nouveaux mots de contrôle, qui précisent les coordonnées où afficher de nouveau le sprite. Par exemple :

$2C40, $2D00	;Afficher le sprite sur 1 pixel de hauteur en ($81, $2C)
$8000, $0000	;Petits plans de bits 1 et 2 du sprite (1 pixel tout à gauche)
$2E72, $2F00	;Afficher le sprite sur 1 pixel de hauteur en ($81 + 100 = $E5, $2C + 2 = $2E)
$0008, $0000	;Petits plans de bits 1 et 2 du sprite (1 pixel tout à droite)
0, 0			;Fin du sprite

L'ordonnée de la nouvelle occurrence du sprite est contrainte. A la base, elle est nécessairement supérieure à celle de la dernière ligne affichée de la précédente occurrence du sprite. Mais il y a plus : il faut attendre une ligne à l'écran entre deux occurrences du sprite. En effet, à chaque ligne de l'écran, le DMA ne dispose que du temps d'alimenter le matériel avec deux mots par sprite. Ainsi, s'il lit les nouveaux mots de contrôle du sprite durant une ligne, il n'a pas le temps de lire les mots de la première ligne des petits plans de bits du sprite durant cette dernière ; c'est à la ligne suivante qu'il le pourra. En conséquence, un sprite affiché jusqu'en Y ne peut être de nouveau affiché qu'à partir de Y+2. Ce qui explique pourquoi dans le programme "spritesField.s", il n'y a que 256/17=15 occurrences de 16 pixels de hauteur à l'écran.

L'astuce : cherche sprites pour plan sympa à plusieurs

S'ils sont peu nombreux et peu colorés, les sprites n'en disposent donc pas moins d'atouts pour séduire le codeur. C'est d'autant plus vrai que leur potentiel s'étend au-delà de ce qui est documenté dans l'Amiga Hardware Reference Manual. En particulier, il est possible de réutiliser des sprites horizontalement sur toute la largeur du champ de jeu.

C'est énorme, car cela permet tout simplement de rajouter un champ de jeu. Comme Codetapper l'a documenté sur son excellent site, l'astuce a été mise à profit avec diverses variantes dans de nombreux jeux à succès.

Le programme "triplePlayfield.s" en fournit une illustration en mettant en place un triple champ de jeu. Dans cette configuration, deux champs de jeu sont affichés en double champ de jeu, en suivant les instructions prodiguées dans l'Amiga Hardware Reference Manual. Par-dessus, un champ de jeu de sprites est affiché en exploitant l'astuce de la réutilisation horizontale. Ce troisième champ de jeu est composé de trois couples de sprites en 16 couleurs, réutilisés horizontalement. Si les sprites n'occupent que 32 pixels de hauteur (de quoi afficher un chiffre et un damier de couleurs), il est entendu qu'ils peuvent s'étendre sur toute la hauteur de l'écran :

Afficher des sprites et des BOB sur Amiga OCS et AGA
Un triple champ de jeu, dont un champ de jeu de sprites (multiplexage horizontal)

Le codeur de jeu vidéo sur Amiga déjà cité m'a rapporté avoir utilisé la technique pour réaliser un décor dont la topologie correspondait à un tore. En effet, le décor était répété horizontalement et verticalement, si bien que la surface de l'écran affichait effectivement une partie rectangulaire d'un décor plaqué sur un tore. Réaliser un tore avec un plan composé de sprites : je pense qu'il s'est arraché les cheveux...

Pour comprendre comment parvenir à notre bien plus modeste résultat, il faut regarder dans la liste Copper. Cette dernière contient notamment une section générée ainsi :

	move.w #(DISPLAY_Y<<8)!$38!$0001,d0
	move.w #DISPLAY_DY-1,d1
_copperListSpriteY:
	move.w d0,(a0)+
	move.w #$FFFE,(a0)+
	move.w #((SPRITE_Y&$FF)<<8)!((SPRITE_X&$1FE)>>1),d2
	move.w #SPR0POS,d3
	move.w #(DISPLAY_DX>>4)-1,d4
_copperListSpriteX:
	move.w d3,(a0)+
	move.w d2,(a0)+
	addq.w #8,d3
	move.w d3,(a0)+
	move.w d2,(a0)+
	addq.w #8,d3
	cmpi.w #SPR6POS,d3
	bne _copperListSpriteNoReset
	move.w #SPR0POS,d3
_copperListSpriteNoReset:
	addi.w #16>>1,d2
	dbf d4,_copperListSpriteX
	addi.w #$0100,d0
	dbf d1,_copperListSpriteY

Le codage des instructions WAIT et MOVE du Copper a été détaillée cet article. Il suffit donc de noter que ce code génère un WAIT au début de chaque ligne de l'écran, suivi de 20 séries de MOVE qui modifient successivement les registres SPR0POS à SPR5POS.

Comme expliqué plus tôt, un registre SPRxPOS contient le premier mot de contrôle d'un sprite, donc les 8 bits de poids fort de sa position horizontale. Dans sa description du fonctionnement du DMA des sprites, l'Amiga Hardware Reference Manual précise que la position horizontale du faisceau d'électron est comparée à chaque pixel à celle qui figure dans SPRxPOS pour décider d'afficher ou non les données chargées dans SPRxDATA et SPRxDATB.

Autrement dit, si l'on change la position horizontale d'un sprite dans SPRxPOS après que ce sprite a été affiché, il est possible de provoquer de nouveau l'affichage de ce sprite sur la même ligne. C'est tout l'objet des séries de MOVE.

Prenons le cas des sprites 0 et 1, qui sont attachés pour former un sprite en 16 couleurs, et donc affichés à la même position, en haut à gauche de l'écran. Le matériel lit les données des plans de bits et des sprites à afficher par paquet de 16 pixels. Le premier WAIT attend le faisceau d'électrons à la position $38, qui correspond à la première de ces lectures qui précède le début de l'affichage (la position horizontale stockée dans DDFSTRT). Tandis que le matériel affiche les 16 pixels des sprites 0 et 1, SPR0POS et SPR1POS sont modifiés par deux MOVE qui reportent la position horizontale des sprites de 16 pixels sur la droite. Comme le Copper prend 16 pixels à exécuter ces MOVE, les nouvelles valeurs des registres sont prises en compte par le matériel à la lecture suivante qu'il fait d'un paquet de 16 pixels à afficher. Ainsi, ces sprites sont de nouveau affichés à leur nouvelle position commune !

La difficulté serait de bien synchroniser les MOVE sur la lecture des données des 16 pixels que le matériel va afficher. Toutefois, cela tombe parfaitement : nul besoin d'intercaler des MOVE inutiles, l'équivalent du NOP pour le Copper, histoire de faire une pause en cours de ligne. Noter que c'est beaucoup plus acrobatique pour d'autres effets qui consistent pareillement à duper le matériel en cours de ligne, mais à des positions précises, notamment pour le zoom matériel horizontal, dont il sera question dans un éventuel prochain article.

A ce stade, le lecteur attentif peut se poser deux questions. Pourquoi afficher un double champ de jeu à l'aide de deux plans de bits par champ de jeu (quatre couleurs par champ de jeu, dont une transparente), et non de trois (huit couleurs par champ de jeu, dont une transparente), comme le matériel le permet ? Et pourquoi réutiliser trois couples de sprites et non quatre, ici encore comme le matériel le permet ?

C'est que l'astuce a ses limites. Tout accroc à l'accès direct au matériel ("metal basher") qui a lu son Amiga Hardware Reference Manual connaît le fameux schéma intitulé "DMA time slot allocation". Ce schéma permet de visualiser l'attribution de cycles disponibles durant une ligne que le matériel trace à l'écran. Certains cycles sont réservés à des fonctions impératives, comme la lecture des données des plans de bits. Les autres sont disponibles, notamment pour le Copper qui trouve ainsi le temps d'exécuter des MOVE (accessoirement, ceux qui se demandent pourquoi un MOVE prend huit pixels en basse résolution pour être exécuté trouveront l'explication dans le schéma).

Le problème, c'est qu'au-delà de quatre plans de bits, le matériel commence à voler des cycles auxquels le Copper aurait pu prétendre pour lire les données des plans de bits supplémentaires :

Afficher des sprites et des BOB sur Amiga OCS et AGA
Les plans de bits 5 et 6 volent des cycles au Copper

Par conséquent, une séquence de 40 MOVE se trouve régulièrement interrompue, ce qui fait tomber la possibilité de réutiliser horizontalement autant de sprites qu'on pourrait le souhaiter. Pour disposer de tous les cycles pour réutiliser les huit sprites, il faut se limiter à quatre plans de bits, soit deux champs de jeu de deux plans de bits chacun.

Par ailleurs, dans le programme "triplePlayfield.s", les champs de jeu défilent horizontalement en exploitant les possibilités du matériel via le registre BPLCON1. Or, cet effet repose sur une lecture anticipée des données des plans de bits (16 pixels avant le début effectif de leur affichage), ce qui vient ici encore voler des cycles, mais cette fois en interdisant de lire via DMA les données du sprite 7 :

Afficher des sprites et des BOB sur Amiga OCS et AGA
Le défilement des champs de jeu volent les cycles du DMA pour les sprites 6 et 7

De ce fait, le couple de sprites 6 et 7 ne peut être utilisé.

L'AGA : des sprites un peu plus mieux

Alors que les codeurs avaient particulièrement apprécié la qualité du Amiga Hardware Reference Manual documentant l'Original Chip Set (OCS) dans ses moindres détails, Commodore fit le choix de ne pas documenter l'Advanced Graphics Architecture (alias "AGA", ou "AA" pour son appellation d'origine). Cette politique visait à assurer la compatibilité des logiciels dans la perspective d'une révolution du jeu de puces qui, comme on le sait, ne survint jamais.

La décision fut conspuée par les codeurs. Dans leur présentation de l'Amiga 1200 publiée dans Grapevine #14, Ringo Star/Classic et Animal & Goofy/Silents ne firent qu'exprimer le sentiment général... non sans faire preuve de la toujours distrayante vantardise des membres s'estimant en vue de la scène !

Afficher des sprites et des BOB sur Amiga OCS et AGA
Ringo Star/Classic et Animal & Goofy/Silents, pas contents !

Or, ce numéro de Grapevine contient un autre article, moins plastronnant et plus constructif, publié par votre serviteur et son acolyte Junkie/PMC (ainsi qu'un certain Spencer Shanson dont le rôle dans cette affaire m'échappe désormais...).

L'excellent Junkie/PMC ayant eu l'idée de désassembler la liste Copper du Workbench de l'Amiga 1200, nous avions passé un bon moment à faire la rétro-ingénierie du matériel en testant bit à bit ses registres. Tout cela couché sur le papier, il en avait résulté une documentation officieuse expliquant comment tirer parti des principales fonctionnalités de l'AGA par accès direct au matériel comme on l'aime. Cette documentation fut ensuite complétée et corrigée par de gentils contributeurs, notamment Randy/Comax dont sa version reste visiblement à ce jour la plus aboutie.

En ce qui concerne les sprites, les capacités de l'AGA surpassent sans conteste celles de l'OCS, mais c'est plus d'une évolution que d'une révolution dont il s'agit. En effet, il n'est toujours possible d'afficher que huit sprites en quatre couleurs (palettes différentes pour les sprites pairs et impairs), ou quatre sprites en 16 couleurs (même palette pour tous les sprites). Toutefois :
  • La largeur d'un sprite peut être de 16, 32 ou 64 pixels.
  • Les groupes de sprites pairs et impairs peuvent partager la même palette de 16 couleurs ou chaque groupe peut disposer de la sienne (lorsque les sprites sont attachés, c'est la palette des sprites impairs qui est utilisée).
  • La résolution des sprites peut être basse, haute ou super-haute.
  • La position des sprites peut être ajustée à l'équivalent d'un pixel en basse, haute ou super-haute résolution.
  • Chaque ligne d'un sprite peut être doublée sans que cela nécessite de doubler la ligne dans les données de ses petits plans de bits.
  • Les sprites peuvent être affichés sur les bords de l'écran, c'est-à-dire au-delà des plans de bits, dans la zone normalement tracée en couleur 0.
Il ne sera pas question de rentrer plus dans les détails ici, car ce serait grosso modo redire tout ce qui a déjà été dit sur les sprites. Pour en savoir plus, le lecteur peut toujours se reporter au programme "spritesAGA.s". Ce programme exploite ces fonctionnalités en affichant quatre sprites de 64 pixels de large en 16 couleurs sur un décor en 256 couleurs, c'est-à-dire à base de huit plans de bits, y compris dans les bords de l'écran :

Afficher des sprites et des BOB sur Amiga OCS et AGA
Des sprites un peu plus mieux en AGA

Pour en finir avec les sprites...

Étant pris en charge par le matériel, les sprites présentent plusieurs avantages pour le codeur. En effet, ce dernier n'a pas à gérer la préservation et la restauration du décor sur lequel ils sont affichés (le recover), pas plus que le découpage pouvant aller jusqu'à l'élimination quand ils débordent ou sortent du champ de jeu (le "clipping").

Toutefois, les sprites sont comme les hobbits : pratiques et colorés, mais petits et peu nombreux. C'est pourquoi les codeurs utilisent aussi, voire alternativement, des BOB. L'acronyme correspond à "Blitter OBject", c'est-à-dire des bitmaps dessinés au Blitter. Parce que les BOB sont si souvent utilisés, le second article de cette série consacrée à l'affichage de bitmaps sur Amiga en exposera les détails.


[Retour en haut] / [Retour aux articles] [Article suivant]