Obligement - L'Amiga au maximum

Vendredi 19 décembre 2025 - 22:33  

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 - Zoom matériel avec BPLXMOD et BPLCON1
(Article écrit par Yragael et extrait de www.stashofcode.fr - septembre 2018, mis à jour en octobre 2018)


Le choc de l'arrivée de la Super Nintendo fin 1991 pour les amateurs de micro-ordinateurs 16 bits, ce fut le Mode 7. Cette console était capable d'appliquer une rotation et un redimensionnement à un bitmap de grande taille dans la trame, et il était possible de trafiquer pour produire un effet de perspective.

L'Amiga 500 était doublement handicapé pour parvenir à produire de tels effets : pas de circuit spécialisé pour cela, et une organisation des données affichées sous forme de plans de bits exigeant, pour y parvenir, de nombreuses opérations au processeur et/ou au Blitter. Toutefois, cela n'empêcha pas certains de produire des effets du genre, par exemple le zoom au démarrage de la démo World Of Commodore 92 de Sanity :

Assembleur
Zoom dans la démo World Of Commodore 92 de Sanity

Parmi tous ces effets de zoom avec ou sans perspective, la plupart se sont appuyés sur le zoom vertical matériel, découvert dès les débuts de l'Amiga, et certains sur le zoom horizontal matériel, qui l'a été bien plus tard.

Comment procéder à l'un et l'autre de ces zooms avec le matériel ? En particulier, quel est ce fameux "$102 trick" régulièrement évoqué dans les forums de créateurs de démos, souvent succinctement, et parfois abusivement ? Et dans quelle mesure est-il possible de zoomer ainsi ? Tout cela et plus encore dans ce qui suit.
  • Mise à jour du 11/09/2018 (matin) : correction de la figure représentant le scénario du zoom horizontal matériel (une étape de trop !) et de la "magic table" (bogue dans le programme HTML5 générateur !).
  • Mise à jour du 11/09/2018 (soir) : ajout d'un paragraphe et d'une figure pour expliquer pourquoi le zoom horizontal matériel limite à quatre le nombre de plans de bits.
  • Mise à jour du 12/09/2018 (matin) : modification de la fin de la section sur le passage de la dissimulation à la suppression, pour expliquer pourquoi et comment il faut optimiser.
  • 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 :
  • zoom0.s pour dissimuler par zoom vertical matériel des lignes avant, au milieu et en bas d'une image et recentrer cette dernière verticalement à l'écran.
  • zoom1.s pour identifier quand utiliser le Copper pour modifier la valeur de BPLCON1 afin de dissimuler un certain nombre de colonnes formées des derniers pixels d'un groupe de seize pixels.
  • zoom2.s pour visualiser le résultat produit par le zoom horizontal matériel reposant sur la technique précédente généralisée pour dissimuler d'une à quinze colonnes de pixels d'une image.
  • zoom3.s pour tester un zoom reposant sur la technique précédente pour réduire une image de 306 à 15 pixels de largeur.
  • zoom4.s pour tester un zoom combinant zoom horizontal matériel et zoom vertical matériel pour réduire une image de 306x256 pixels à 15x15 pixels.
NB : cet article se lit mieux en écoutant l'excellent module Helmet For Sale composé par Jason/Kefrens pour R.A.W. #2, mais c'est affaire de goût personnel...

Le zoom vertical matériel, un usage banal des modulos

Comme expliqué ici, le matériel affiche une image composée de plans de bits dont les adresses lui sont fournies via les couples de registres BPLxPTH/BPLxPTL. Une fois qu'il a lu et affiché les données d'une ligne, le matériel rajoute l'équivalent en octets à ces adresses - 40 octets pour un écran de 320 pixels de large -, puis il y rajoute un modulo. Ce modulo est stocké dans BPL1MOD pour les plans de bits impairs (1, 3, etc.), et BPL2MOD pour les plans de bits pairs (2, 4, etc.).

Sachant que le matériel lit donc dans des registres que le Copper permet de modifier (au moins) à chaque ligne de l'écran, on conçoit facilement comment tirer profit de ce fonctionnement. Prenons l'exemple d'une image de 320x256 pixels en seize couleurs, donc quatre plans de bits.

Il est possible de demander au Copper d'attendre le début d'une ligne et d'écrire dans les registres BPLxPTH/L pour modifier l'adresse de la ligne qui sera affichée. Cette adresse sera déterminée en fonction du zoom, pour sauter ou répéter des lignes, ou simplement passer à la ligne suivante. La liste Copper contient alors notamment un bloc suivant par ligne N, ici présenté en pseudo-code pour en faciliter la lecture :

	WAIT <début de ligne N>
	MOVE <valeur>, BLTP1PTH
	MOVE <valeur>, BLTP1PTL
	MOVE <valeur>, BLTP2PTH
	MOVE <valeur>, BLTP2PTL
	MOVE <valeur>, BLTP3PTH
	MOVE <valeur>, BLTP3PTL
	MOVE <valeur>, BLTP4PTH
	MOVE <valeur>, BLTP4PTL

Qui est certain de l'alignement des données en mémoire peut se dispenser d'écrire dans BPLxPTH sachant que sa valeur ne changera pas quel que soit le décalage rajouté pour progresser dans les plans de bits, mais peu importe : c'est le principe qu'on illustre ici.

Il est aussi possible de simplement écrire dans BPL1MOD et BPL2MOD. Dans ce cas, c'est l'adresse de la ligne suivante, et non l'adresse de la ligne courante, qui sera impactée, le matériel utilisant les modulos en fin de ligne. La liste Copper contient alors notamment un bloc suivant par ligne N, ici encore présenté en pseudo-code pour en faciliter la lecture :

	WAIT <début de ligne N-1>
	MOVE <valeur>, BPL1MOD
	MOVE <valeur>, BPL2MOD

Utiliser BPLxMOD affecte tous les plans de bits impairs et/ou tous les plans de bits pairs, sans discrimination. Toutefois, ce n'est pas gênant, car le zoom à réaliser est généralement le seul effet à produire dans les plans de bits. Par ailleurs, utiliser ces registres est plus économique qu'utiliser BPLxPTH/L.

En effet, il ne faut pas oublier que le facteur de zoom variant d'une trame à l'autre, la liste Copper devra être modifiée au processeur ou au Blitter pour mettre à jour les valeurs que le Copper écrit dans les registres utilisés. Or, le calcul est vite fait :
  • Avec BPLxPTH/L, il faut modifier les valeurs de deux MOVE par plans de bits, soit huit valeurs au total (éventuellement quatre en jouant sur l'alignement en mémoire).
  • Avec BPLxMOD, il faut modifier les valeurs de deux MOVE tout court, soit deux valeurs au total.
Toutefois, il ne suffit pas de dissimuler des lignes pour produire un zoom : à toute étape, il faut que l'image reste centrée à l'écran. Or, modifier les modulos entraîne le tassement de l'image vers le haut de l'écran. Pour compenser, il faut repousser vers le bas l'image de la moitié du nombre de lignes dissimulées. C'est possible grâce au moins à deux solutions :
  • Modifier la position verticale de départ de l'affichage dans le registre DIWSTRT, et la hauteur de cet affichage dans le registre DIWSTOP.
  • Modifier la position verticale d'un WAIT du Copper, position à partir de laquelle des MOVE modifient les registres BPLxPTH/L pour pointer sur les adresses de départ des plans de bits de l'image zoomée.
Dans l'un et l'autre cas, il est inutile de modifier la position verticale des WAIT qui indiquent au Copper à quelle ligne il doit attendre avant d'exécuter le MOVE qui, en écrivant une valeur dans BPLxMOD, entraîne ou non la dissimulation d'une ou plusieurs lignes. En effet, à chaque étape du zoom, il suffit d'actualiser directement dans le code de la liste Copper les valeurs que ces MOVE écrivent dans BPLxMOD, en tenant compte de la nouvelle position verticale de l'image pour sélectionner ces MOVE. Bref, s'il faut réduire la hauteur d'une image de 256 à 0 pixels en N étapes, il suffit de construire une liste Copper qui contient toujours 256 WAIT suivis de MOVE sur BPLxMOD, et de modifier à chaque étape les valeurs que ces MOVE écrivent dans BPLxMOD pour animer le zoom.

Dans le programme final zoom4.s, c'est la première solution qui a été adoptée par simplicité. Il faut noter que cela implique de modifier BPLxPTH/L pour parvenir à dissimuler une ou plusieurs des premières lignes. En effet, le modulo est une valeur que le matériel ajoute à l'adresse de la ligne qui vient d'être affichée. Or, par définition, la première ligne ne vient après aucune autre ligne. Toutefois, c'est le seul cas où ces registres sont modifiés pour zoomer.

Par ailleurs, il faut noter que le faisceau d'électrons ne trace donc rien au-dessus ni en-dessous de l'image zoomée. Dans ces conditions, il est impossible d'afficher des bandeaux encadrant l'image zoomée. Pour ce faire, il faut opter pour la seconde solution.

La figure suivante récapitule ce qui se passe dans le cas où les lignes 0, 1, 2 et 5 de l'image doivent être dissimulées :
  • Comme il s'agit de dissimuler quatre lignes, DIWSTART est modifié pour que l'affichage démarre deux lignes plus bas, assurant ainsi que l'image reste verticalement centrée à l'écran.
  • En conséquence, DIWSTOP est aussi réduit pour que le matériel n'affiche pas du déchet après avoir affiché la 256e ligne de l'image.
  • Pour dissimuler les trois premières lignes, impossible d'utiliser BPLxMOD. C'est donc BPLxPTH/L qui est modifié avant le début de l'affichage de la ligne 3.
  • Pour dissimuler la ligne 5, BPLxMOD est passé à l'équivalent d'une ligne en octets, soit 40 octets, avant la fin de l'affichage de la ligne 4.
Assembleur
Principe du zoom vertical matériel

Le programme zoom0.s constitue un exemple de base où les seize premières lignes, les seize lignes médianes et les seize dernières lignes d'une image sont ainsi dissimulées - pour la beauté du geste, c'est l'image Dragon Sun de Cougar/Sanity qui est utilisée :

Assembleur
Zoom vertical matériel d'une image

A ce stade, il reste un point essentiel à régler : comment identifier les lignes à dissimuler à une étape du zoom donnée ? Cette question, qui se pose presque dans les mêmes termes pour le choix des colonnes à dissimuler, sera abordée plus loin.

Le zoom horizontal matériel, un détournement surprenant du défilement

L'histoire dira à qui revient le mérite d'avoir trouvé l'astuce (on évoque ici le génial Chaos/Sanity). Comme chacun sait, il est possible de demander au matériel de retarder l'affichage de l'image à l'écran d'un nombre de pixels allant de 0 à 15. La valeur de ce retard doit être spécifiée dans le registre BPLCON1, qui comporte quatre bits (PF1H3-0) pour le retard des plans de bits impairs (1, 3, etc.) et quatre bits (PF2H3-0) pour le retard des plans de bits impairs (2, 4, etc.). C'est le défilement matériel.

Ainsi, une valeur de $005F dans BPLCON1 va retarder l'affichage des plans de bits pairs de cinq pixels et celui des plans de bits impairs de quinze pixels. A moins d'utiliser le double champ de jeu ou de chercher à manipuler distinctement les plans de bits pairs et impairs, les retards spécifiés pour ces plans de bits seront identiques. Il s'agira de produire un défilement horizontal en jouant sur BPLCON1 et sur les couples BPLxPTH/BPLxPTL - on ne s'étendra pas sur ce sujet trivial.

Or le matériel lit les données qu'il va afficher par groupe de seize pixels. A chaque fois, il tient compte des décalages spécifiés dans BPLCON1 pour retarder plus ou moins l'affichage de ce groupe. Que se passerait-il si la valeur de ces décalages devait être réduite d'un groupe à un autre ? En particulier, le matériel n'afficherait-il pas plus tôt le second groupe, écrasant les derniers pixels du premier ?

C'est exactement ce qui se passe. Et comme dissimuler des colonnes de pixels dans l'image produit une réduction de la largeur de cette dernière, il y a lieu de parler de zoom horizontal matériel :

Assembleur
Dissimulation des deux derniers pixels d'un groupe en réduisant le défilement matériel de 2

Encore faut-il régler une question pratique : quand et comment modifier la valeur des décalages dans BPLCON1 ? Comme cela vient d'être suggéré, il faut que l'écriture dans BPLCON1 soit synchronisée sur le cycle de lecture et d'affichage des groupes de pixels par le matériel.

Chacun sait qu'il est toujours possible d'attendre une position du faisceau d'électron (le raster) à l'écran, et de modifier le contenu d'un registre matériel à cet instant. Cette manoeuvre peut être réalisée au processeur, mais s'il fallait réaliser le zoom matériel de cette manière, il serait impossible de rien faire d'autre durant une trame.

Fort heureusement, le Copper est là, qui exécute sa liste Copper parallèlement. Pourquoi ne pas utiliser ses instructions WAIT et MOVE, détaillées ici, pour parvenir au résultat souhaité ?

Le programme zoom1.s en donne une illustration. L'idée va être d'attendre la position à laquelle on souhaite dissimuler un certain nombre des derniers pixels d'un groupe donné.

Mais où attendre ? A vrai dire, il y aurait deux solutions : utiliser un WAIT puis un MOVE pour modifier BPLCON1. Ou alors, comme dans le cas d'un effet plasma, enchaîner les MOVE en sachant qu'un MOVE prend huit pixels en basse résolution pour être exécuté, dont un MOVE pour modifier BPLCON1. Dans les faits, seule la seconde solution est praticable.

Pourquoi ? Parce que c'est comme ça. Tous les codeurs qui utilisent le zoom horizontal matériel le font ainsi. Pourquoi, encore ? Parce qu'il faudrait vraiment, mais alors vraiment, se casser la tête pour parvenir à calculer précisément la position horizontale à spécifier dans le WAIT pour que le MOVE soit exécuté au bon moment - et encore, il n'est pas dit que ce serait possible. A ceux qui se lamenteraient de ce manque de rigueur, disons que pour aller au fond des choses, il faudrait savoir expliquer à tout instant le rapport entre la position horizontale du faisceau d'électron, telle qu'elle est formulée dans un WAIT du Copper, et la valeur horizontale telle qu'elle figure dans DDFSTRT. Or, si c'est certainement possible, cela reste à faire...

Ainsi le Copper est programmé pour attendre le début de chaque ligne à la position horizontale $3D, empiriquement déterminée, et procéder à 40 MOVE dont certains vont modifier la valeur de BPLCON1 en réduisant le retard initial, eux aussi empiriquement déterminés.

Il faut noter qu'une telle sollicitation du Copper contraint le nombre de plans de bits, donc le nombre de couleurs à l'écran. La raison en a déjà été présentée ici : au-delà de quatre plans de bits, le matériel vole des cycles au Copper, si bien que ce dernier perd la possibilité d'exécuter autant de MOVE par ligne :

Assembleur
Pour afficher les plans de bits 5 et 6, le matériel vole des cycles au Copper

Dans zoom1.s, la partie de la liste Copper concernant le zoom (après une initialisation de BPLCON1 à $00FF toutefois) se présente ainsi :

	;Zoom

	move.w #ZOOM_Y<<8,d0
	move.w #ZOOM_DY-1,d1
_zoomLines:

	;Attendre le début la ligne

	move.w d0,d2
	or.w #$00!$0001,d2
	move.w d2,(a0)+
	move.w #$8000!($7F<<8)!$FE,(a0)+

	;Initialiser BPLCON1 avec une retard de 15 pixels ($00FF)

	move.w #BPLCON1,(a0)+
	move.w #$00FF,(a0)+

	;Attendre la position sur la ligne correspondant au début de l'affichage
        ;(position horizontale $3D dans un WAIT)

	move.w d0,d2
	or.w #ZOOM_X!$0001,d2
	move.w d2,(a0)+
	move.w #$8000!($7F<<8)!$FE,(a0)+

	;Enchaîner des MOVE qui ne font rien jusqu'à celui qui doit passer le retard à ZOOM_BPLCON1

	IFNE ZOOM_MOVE	;Car ASM-One plante sur un REPT dont la valeur est 0...
	REPT ZOOM_MOVE
	move.l #ZOOM_NOP,(a0)+
	ENDR
	ENDC

	;Modifier BPLCON1 pour passer le retard à ZOOM_BPLCON1

	move.w #BPLCON1,(a0)+
	move.w #ZOOM_BPLCON1,(a0)+
	
	;Enchaîner des MOVE qui ne font rien jusqu'à la fin de la ligne

	IFNE 39-ZOOM_MOVE		;Car ASM-One plante sur un REPT dont la valeur est 0...
	REPT 39-ZOOM_MOVE
	move.l #ZOOM_NOP,(a0)+
	ENDR
	ENDC

	;Passer à la ligne suivante de la bande de lignes zoomées

	addi.w #$0100,d0
	dbf d1,_zoomLines

	;Réinitialiser BPLCON1 ($00FF) pour la fin de l'écran

	move.w #BPLCON1,(a0)+
	move.w #$00FF,(a0)+

La liste Copper contient alors notamment un bloc suivant par ligne N, ici encore présenté en pseudo-code pour en faciliter la lecture :

	WAIT ($00, N)
	MOVE #$00FF, BPLCON1
	WAIT ($3D, N)
	REPT ZOOM_MOVE
	MOVE #$000, ZOOM_NOP
	ENDR
	MOVE #ZOOM_BPLCON1, BPLCON1
	REPT 39-ZOOM_MOVE
	MOVE #$000, ZOOM_NOP
	ENDR

Ajuster le zoom horizontal matériel, un vrai défi

En modifiant ZOOM_MOVE dans le programme précédent, on indique l'indice du MOVE auquel on souhaite que le Copper écrive ZOOM_BPLCON1 dans BPLCON1, sachant que BPLCON1 est initialisé à $00FF. Il est alors possible d'observer le résultat à l'écran.

Pour que ce dernier soit bien visible, l'effet est répété sur une bande de ZOOM_DY de hauteur, en l'occurrence 20 pixels, précédée et suivie d'une bande de même hauteur sans effet. Par ailleurs, un motif blanc sur fond rouge est dessiné dans le plan de bits : c'est une succession de groupes de seize pixels : dans le premier groupe, le dernier pixel est blanc ; dans le deuxième groupe, les deux derniers pixels sont blancs ; etc. Ce motif permet de réaliser un effet du genre : "si je me positionne au niveau du MOVE correspondant au groupe comportant quatre pixels blancs et que je réduis BPLCON1 de 4, est-ce que j'observe la disparition de ces quatre pixels ?"

Ce qui peut être observé, c'est que très généralement, les résultats sont décevants. Par exemple, passer BPLCON1 à $0022 au 18e MOVE (c'est-à-dire ZOOM_MOVE valant 17) produit cela :

Assembleur
Les aléas de la modification de BPLCON1 en cours de ligne

Toutefois, ils peuvent être satisfaisants. A titre de contre-exemple, passer BPLCON1 à $00EE au 4e MOVE produit cela :

Assembleur
La dissimulation réussie d'une colonne par modification de BPLCON1 en cours de ligne

C'est en procédant de la sorte, mais à l'aide d'un programme plus élaboré qui permet de désigner plusieurs MOVE devant réduire le retard de 1 dans BPLCON1, qu'il est possible d'établir une liste des 40 MOVE permettant de réduire la largeur de l'image de un, deux, trois pixels, et ainsi de suite jusqu'à quinze pixels, pour une valeur initiale de BPLCON1 donnée.

"Une valeur initiale de BPLCON1 donnée" ? Ce n'est pas $00FF ? Non, car il faut gérer une contrainte : lorsqu'une colonne est dissimulée, les colonnes qui suivent sont décalées à l'écran d'un pixel sur la gauche. A ce train, une image dont quinze colonnes sont dissimulées se trouve tassée de quinze pixels sur la gauche. Or, ce n'est pas ce qui est attendu lors d'un zoom réduisant la largeur d'une image de 320 à 0 pixels : comme évoqué en présentant le zoom vertical matériel, l'image doit rester centrée à l'écran.

Pour y parvenir, il faut adopter le scénario suivant. Sur cette figure, chaque ligne représente une étape du zoom. A gauche, la valeur initiale de BPLCON1. A droite, sa valeur finale. Entre les deux, les vingt groupes de seize pixels composant une ligne d'une image de 320 pixels de large :
  • Les groupes dont les derniers pixels sont déjà dissimulés aux étapes précédentes sont en rouge clair.
  • Le groupe dont le dernier pixel doit être dissimulé à cette étape est en rouge foncé.
A chaque groupe rouge clair ou rouge foncé, la valeur de BPLCON1 est réduite de 1 pour dissimuler un pixel.

Assembleur
Scénario du zoom horizontal matériel

Comme il est possible de le constater, le scénario vise à compenser autant que possible le tassement sur la gauche de l'image en la décalant d'un pixel sur la droite chaque fois que deux pixels ont été dissimulés. C'est un scénario qui se construit en remontant les étapes depuis la dernière, car il faut que la valeur initiale de BPLCON1 soit alors $00FF pour pouvoir dissimuler quinze pixels.

Adopter ce scénario a une conséquence capitale. Pour le comprendre, il faut désormais bien distinguer quatre choses :
  • L'écran. C'est l'écran physique de l'ordinateur : s'agissant du repère de référence, il n'est jamais décalé, et sa résolution horizontale est toujours de 320 pixels.
  • L'image. C'est ce que le spectateur voit à l'écran : une colonne de couleur 0 sur la gauche créée par la valeur initiale de BPLCON1, puis les plans de bits dont la partie visible est amputée d'une colonne de même largeur sur la droite.
  • Les plans de bits. Ce sont des espaces en mémoire de 320 pixels de large dont une partie seulement est visible dans l'image à l'écran.
  • Le dessin. C'est ce que l'on souhaite donner à voir au spectateur, à savoir quelque chose dont la largeur se réduit progressivement tout en restant centré dans l'image.
C'est l'enchâssement de multiples repères (le dessin dans le plan de bits, le plan de bits dans l'image, l'image dans l'écran - mais on peut considérer que leurs repères sont confondus) qui fait toute la complexité de la gestion du zoom horizontal matériel.

Quand aucune colonne n'est dissimulée, il faut que le dessin soit centré à l'écran. Or BPLCON1 vaut alors $0077. Pour qu'un dessin décalé de sept pixels sur la gauche apparaisse centré dans l'écran de 320 pixels de large, il faut que la largeur de ce dessin ne dépasse pas 306 pixels. En effet, si sept pixels sont inutilisables à gauche, il faut que sept pixels le soient à droite : quatorze pixels en tout, à soustraire de 320, ce qui donne 306.

C'est donc un dessin de 306 pixels de large, calé sur la gauche dans des plans de bits de 320 pixels de large, qui peut être au plus utilisé pour produire le zoom, la colonne de quatorze pixels de large sur la droite devant être en couleur 0 :

Assembleur
Le dessin de 306 pixels de large dans un plan de bits de 320 pixels de large

Le scénario débouche sur une liste qui donne la valeur de BPLCON1 en début de ligne et identifie les MOVE qui décrémentent cette valeur pour dissimuler la dernière colonne de N groupes de 16 pixels, réduisant d'autant de pixels la largeur de l'image :

Assembleur
La "magic list" du zoom horizontal matériel

Cette liste est totalement spécifique :
  • La série de 40 MOVE d'une ligne doit être précédée d'un WAIT à la position horizontale $3D.
  • Les groupes ont été déterminés en appliquant le scénario, qui n'est qu'un scénario parmi d'autres.
Reconnaissons cette liste comme une des "magic list" de l'Amiga. Une autre "magic list" est celle qui indique comment organiser la liste Copper pour produire des boucles qui répètent, sur huit lignes, des séries de 40 MOVE, et ce sur toute la hauteur d'un écran PAL, c'est-à-dire sur 256 lignes. Cette autre liste sera présentée est expliquée dans la seconde partie de cet article, une fois que la cracktro qui l'exploite aura été diffusée - ce n'est pas que personne n'a jamais élaboré cette liste ; c'est qu'il faut pouvoir proposer le code de cette cracktro au téléchargement pour que l'article soit intéressant à lire... et de nos jours, il faut attendre qu'un jeu sorte pour sortir une cracktro !

Retour à notre liste. Sa mise en oeuvre dans le programme zoom2.s produit le résultat suivant, où un dessin de 306 pixels de large (lignes blanches sur fond gris) centré dans un plan de bits de 320 pixels de large (fond rouge) est réduit progressivement en dissimulant les derniers pixels de quinze groupes de seize pixels les uns après les autres. Pour que le zoom semble progressif s'il devait être animé, les dissimulations alternent entre la gauche et la droite du groupe de pixels du milieu. Le dessin est intact dans la moitié supérieure de l'écran ; il subit le zoom dans la moitié inférieure de l'écran, perdant un pixel toutes les huit lignes :

Assembleur
Réduire progressivement la largeur d'une image de quinze pixels, tout en la centrant

Au-delà de quinze pixels, passer de la dissimulation à la suppression

BPLCON1 permet d'introduire un retard initial de quinze pixels au plus au début de chaque ligne. Partant, c'est au plus quinze pixels qu'il est possible de dissimuler sur cette ligne, ce qui est clairement insuffisant pour produire un zoom où la largeur de l'image passerait de 320 à 0 pixels.

Quand les possibilités du zoom horizontal matériel sont épuisées (BPLCON1 valant $00FF au début d'une ligne, et étant réduit progressivement jusqu'à $0000), nulle autre solution que de prendre le relai avec un zoom horizontal logiciel. Il s'agit alors de reproduire dans les plans de bits ce que le spectateur voit à l'écran avant de réinitialiser le zoom matériel (BPLCON1 valant $0077 au début d'une ligne, et n'étant pas réduit). Cela revient à dire qu'il faut supprimer véritablement les colonnes de pixels dissimulées dans les plans de bits, et recentrer horizontalement le dessin ainsi réduit dans ces derniers.

Recentrer de combien de pixels ? Comme cela vient d'être rappelé, au moment où il faut prendre le relai du zoom matériel, la valeur de BPLCON1 est $00FF, ce qui correspond à un décalage de quinze pixels sur la droite. Par ailleurs, la valeur de BPLCON1 quand aucun pixel n'est dissimulé est de $0077, ce qui correspond à un décalage de sept pixels sur la droite. En conséquence, il faut produire un dessin qui, lorsque les plans de bits qui le contiennent seront ainsi décalés, présentera les caractéristiques suivantes :
  • Il fera 320-15=305 pixels de large.
  • Il apparaîtra décalé de quinze pixels sur la droite.
Bref, une fois réduit, le dessin doit être décalé de 15-7=8 pixels sur la droite dans les plans de bits. On parle bien du dessin et non des plans de bits. En effet, il est bien entendu que la largeur de ces derniers ne change pas ; c'est le dessin qu'ils contiennent qui est réduit, pas eux.

Assembleur
Supprimer des colonnes et décaler l'ensemble lors du zoom logiciel

Comment supprimer ? Il est possible d'utiliser le Blitter, qui s'entend à merveille pour décaler des mots sur la droite (en mode ascendant) comme sur la gauche (en mode descendant) après les avoir éventuellement masqués, ou le processeur, voire les deux.

Une optimisation est possible - en fait, elle est même nécessaire. En effet, au fil du zoom logiciel, la largeur du dessin dans les plans de bits se réduit, jusqu'au point où le dessin n'empiète plus sur certains groupes de seize pixels qui se trouvent à sa gauche et à sa droite.

Partant, il devient inutile de supprimer des colonnes dans ces groupes. De même, ces colonnes n'ont plus à être dissimulées. Ces optimisations sont obligatoires, car le spectateur ne comprendrait pas que le zoom semble faire une pause parce qu'une étape est consacrée à la dissimulation d'une colonne qui est vide. Il faut donc déterminer quelles colonnes doivent être dissimulées durant un cycle de zoom matériel, et supprimées par zoom logiciel au terme de ce cycle, en fonction de la largeur du dessin au début de ce cycle. C'est ce qui a été fait pour réaliser le programme zoom3.s à l'aide de la feuille Excel qui figure dans zoom.xlsx :

Assembleur
Calcul des groupes à traiter à chaque cycle de zoom horizontal matériel

Comme le nombre de colonnes à supprimer se réduit, en mode débogage (constante DEBUG passée à 1), il apparaît que le temps pris par la suppression diminue tandis que le dessin occupe de moins en moins de groupes dans les plans de bits. Au début, ce temps est assez considérable pour que la suppression ne tienne pas dans la trame sur Amiga 500. En fait, il faudrait simplement sortir la suppression de la boucle principale et l'utiliser avant pour précalculer les versions successives du dessin. Elles devraient toutes pouvoir tenir en mémoire.

Au final, le code de la suppression est assez complexe, et il serait trop long d'en présenter le détail ici. Qui veut en savoir plus peut se référer au source du programme indiqué. On y utilise le Blitter uniquement pour recopier en les décalant et en les masquant les colonnes de mots qui forment le dessin, et celles-là uniquement.

Le choix des lignes et des colonnes à dissimuler : un dilemme d'informaticien

Les techniques présentées pour dissimuler/supprimer des lignes et des colonnes fonctionnent bien, mais elles reposent sur des hypothèses dont il faut avoir conscience. Ces hypothèses portent sur la manière dont il convient d'identifier les lignes et les colonnes à dissimuler à une étape du zoom donnée. Elles ont notamment déterminé le scénario du zoom horizontal matériel présenté plus tôt.

S'il faut dissimuler N lignes et colonnes à une étape du zoom donnée, le premier réflexe est de chercher à répartir également ces lignes sur la hauteur et ces colonnes sur la largeur du dessin. A bien y réfléchir, cela repose sur l'hypothèse que le zoom sera plus réaliste si la dissimulation est diffuse, car le spectateur devrait toujours disposer d'assez d'informations en tout point du dessin pour reconnaître sinon le dessin initial, du moins le dessin de l'étape précédente.

Toutefois, ce n'est qu'une hypothèse. S'il s'agissait de zoomer un texte, il y a fort à parier que les caractères traités aussi arbitrairement deviendraient vite méconnaissables. En toute rigueur, la réduction d'un dessin, c'est le filtrage d'un message, lequel ne peut déboucher sur un résultat acceptable qu'à condition de tenir un minimum compte de la signification du message en question, quitte à reformuler ce dernier.

Cette considération sémantique peut paraître stratosphérique dans le cas du zoom en temps réel d'un dessin sur Amiga, tant il est vrai que la puissance de calcul disponible interdira d'en tenir compte rigoureusement - si tant est qu'il soit seulement possible d'en tenir compte ! Toutefois, il est bon de l'évoquer pour échapper à une remise en question sans issue de la pertinence de l'hypothèse adoptée. Par là, on veut dire qu'en cherchant à diffuser les dissimulations, on cherche à faire au mieux pour emballer le spectateur avec les moyens du bord. Ce qui fonde l'hypothèse, ce ne sont pas de savants calculs ; c'est que le résultat qui en découle est, pour le spectateur, assez convaincant.

Enfin, il faut tout de même questionner l'hypothèse du fait d'une contrainte technique qui limite sérieusement cette application. Cette contrainte porte sur le zoom horizontal matériel, dont on a vu qu'il n'est pas aussi souple que le zoom vertical matériel. En effet, il est impossible de choisir les colonnes à dissimuler aussi facilement que les lignes à traiter pareillement : c'est parmi les derniers pixels de groupes de seize pixels qu'il faut choisir.

Par exemple, pour réduire un dessin de 320 pixels à 318 en dissimulant deux colonnes, il pourrait paraître cohérent de dissimuler les colonnes 106 et 214, l'espace entre les colonnes dissimulés étant alors régulier :
  • 106 pixels entre les colonnes 0 et 105.
  • 107 pixels entre les colonnes 107 et 213.
  • 106 pixels entre les colonnes 215 et 320.
Or, la colonne 106 correspond au 10e pixel du 6e groupe de seize pixels d'une ligne. Autrement dit, elle tombe mal, car elle ne tombe pas sur le pixel d'un groupe qu'il est possible de dissimuler en réduisant le retard de 1 avant l'affichage de ce dernier. Bref, elle gêne car elle ne correspond pas au 16e pixel d'un groupe de seize pixels.

La distribution des colonnes de pixels qu'il est possible de dissimuler est contrainte. Qui veut réduire progressivement la largeur d'un dessin de 320 à 0 pixel en dissimulant une nouvelle colonne de pixels à chaque étape peut au mieux répartir les colonnes qu'il dissimule tous les seize pixels. Et encore, ce n'est pas tous les seize pixels du dessin, mais tous les seize pixels des plans de bits qui contiennent le dessin, comme on l'a vu.

Enfin, quelle que soit l'hypothèse adoptée, il faut aussi tout de même la questionner sur un autre point. Continuons sur l'exemple du zoom horizontal. Indépendamment de la contrainte qui vient d'être évoquée, s'il faut enchaîner des dissimulations de colonnes, comment vaut-il mieux procéder :
  • En considérant qu'une colonne dissimulée ne doit pas réapparaître, et donc en dissimulant une nouvelle colonne quitte à ce que les colonnes supprimées ne soient pas également réparties sur toute la largeur du dessin ?
  • En considérant qu'une colonne dissimulée peut réapparaître, et donc en dissimulant de nouvelles colonnes, qui seront nécessairement différentes, mais qui présenteront l'intérêt d'être toujours également réparties sur toute la largeur du dessin ?
Ici encore, c'est un questionnement sur lequel on passerait un temps infini, car au regard des moyens disponibles, il est impossible de retenir l'une ou l'autre de ces hypothèses sans tenir compte de l'effet que le résultat qui en découle produit sur le spectateur.

Pour un esprit cartésien, cette histoire de zoom matériel, c'est un peu la déconvenue à chaque étape. L'identification des MOVE pour modifier BPLCON1 au bon moment sur une ligne ? A déterminer empiriquement. L'identification des colonnes et des lignes à dissimuler ? A déterminer empiriquement. Le choix de faire réapparaître ou non des colonnes et des lignes dissimulées ? A déterminer empiriquement.

Mais c'est que l'esprit cartésien s'est trompé de domaine. Rappelons que le zoom matériel est employé dans une démo. Or, ainsi que l'a fort bien expliqué le formidable Navis/ASD, le principe de toute démo est de faire illusion : "We have to cheat. And you know, everybody does this. This is why demos are different to making games or making offline films." (Nous devons tricher. Et vous savez, tout le monde le fait. C'est pourquoi les démos sont différentes de la création de jeux ou de films hors ligne).

C'est parole de Maître, car on ne saurait mieux définir le genre. Or, il est bon qu'on nous rappelle régulièrement à quoi nous sommes censés nous consacrer. C'est le meilleur moyen pour ne pas être victime d'une illusion, celle de la poursuite d'une perfection sans intérêt pour celui auquel nous destinons les fruits de notre labeur, à savoir le spectateur. Et dans une démo, il s'agit de faire du Hollywood pour attirer les foules, pas du cinéma français pour les tenir à distance... Si vous ne travaillez que pour vous-même, à moins d'être un génie - mais qui se pose cette question a déjà la réponse -, vous ne resterez pas dans l'Histoire : le génie subjugue ; le tiers doit convaincre.

Sans donc nous faire d'illusion sur la catégorie dont nous relevons, concluons benoîtement notre exploration des délices du zoom matériel.

Le programme zoom4.s rajoute le zoom vertical matériel à la combinaison des zooms horizontaux matériel et logiciel. Dans le cas du zoom vertical, le scénario adopté est exactement le même que celui retenu pour la dissimulation des colonnes, imposé par les limitations du zoom horizontal matériel. Un outil en HTML5, réalisé pour l'occasion, a permis de générer les indices des lignes à dissimuler tandis que le zoom progresse :
Assembleur
Un outil réalisé en HTML pour tester un scénario de zoom vertical

Le zoom horizontal matériel, une trouvaille inutile ?

Il faut saluer la ténacité de ceux qui se sont lancés dans le zoom matériel pour en faire quelque chose, tant l'effet est pénible à maîtriser. Toutefois, il ne faut pas surestimer le gain qu'ils ont pu en tirer. Ce n'est pas parce qu'il y a zoom dans la trame bientôt en plein écran que ce dernier est matériel. En fait, quand le zoom horizontal est très réussi, il y a tout lieu de penser qu'il n'est pas matériel.

Ainsi, la fameuse démo Elysium de Sanity s'ouvre par zoom irréprochable d'une image sur quatre plans de bits. Mais le désassemblage de la liste Copper ne donne à voir que des WAIT à chaque ligne qui débouchent sur des modifications de BPL1MOD et BPL2MOD. Autrement dit, le zoom horizontal matériel n'est pas utilisé ici, mais uniquement le zoom vertical matériel :

Assembleur
Zoom irréprochable d'une image en quatre plans de bits dans la démo Elysium de Sanity

On trouve un exemple de zoom horizontal matériel dans une autre production de Sanity, la disquette musicale Jesterday. Ce type de zoom est utilisé pour produire le rouleau que l'utilisateur fait tourner pour sélectionner un morceau de musique à écouter :

Assembleur
Le rouleau de sélection dans la disquette musicale Jesterday de Sanity

Encore plus original, le défilement à la Star Wars dans The Fall par The Deadliners & Lemon. Le désassemblage de la liste Copper montre que non seulement le zoom horizontal matériel est utilisé, mais que l'effet est combiné à des changements d'adresse de plans de bits pour effectuer des sauts dans ces derniers :

Assembleur
Le défilement à la Star Wars dans The Fall de The Deadliners & Lemon

Ce défilement doit être mentionné pour l'ingéniosité dont a fait preuve le codeur, mais aussi à l'inverse pour sa médiocre qualité visuelle, tant les lettres sont déformées (*). On a vu des défilements de ce type bien plus agréables à contempler, tout particulièrement celui de l'invraisemblable jeu Stardust, qui n'utilise ni zoom horizontal matériel, ni zoom vertical matériel, en haute résolution qui plus est ! :

Assembleur
Le défilement à la Star Wars de Stardust, totalement parfait

Il y a quelques leçons à tirer de tout cela :
  • D'abord, c'est que la focalisation sur les coprocesseurs peut conduire à faire perdre de vue le fait qu'il est parfaitement possible d'accomplir bien des effets au processeur, éventuellement beaucoup plus réussis. C'est comme toujours : on se focalise sur les accessoires au détriment du principal, parce qu'aspiré par la technique, on en vient à négliger l'algorithmique.

  • "Legends never die", comme dit l'autre. The Fall remonte à... avril 2018 ! Franchement, qui aurait dit que plus de 30 ans après sa sortie, il se trouverait encore des codeurs pour imaginer de nouvelles exploitations du matériel de l'Amiga 500 ? Ici encore, c'est comme toujours : épuise-t-on jamais les possibilités d'une vieille casserole, dont le proverbe dit qu'on y fait toujours la meilleure confiture ?
* Cela n'enlève rien au mérite du codeur, qui a réussi une prouesse technique. Par ailleurs, la démo contient des effets visuellement très réussis, en particulier des enchaînements dont la réalisation a dû nécessiter un travail fou. Sur les enchaînements, lire d'ailleurs ici une entrevue de Chaos/Sanity, publiée dans The Jungle #2 en 1993. Codeur à juste titre très réputé, il prenait visiblement tout le monde de haut - lire "Chaos - Only superlatives please" publié dans R.A.W. #7 pour en rire -, mais son propos était pertinent.


[Retour en haut] / [Retour aux articles]