Obligement - L'Amiga au maximum

Vendredi 23 mai 2025 - 18:13  

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 - Programmer un défilement sinusoïdal ("sine scroll") sur Amiga (partie 4)
(Article écrit par Yragael et extrait de www.stashofcode.fr - juillet 2017)


Cet article est le quatrième d'une série de cinq consacrés à la programmation d'un "one pixel sine scroll" sur Amiga, un effet très utilisé par les codeurs de démos et autres cracktros durant un temps. Par exemple, dans cette intro, aussi magnifique que vintage, du groupe Miracle :

Programmer un défilement sinusoïdal
Défilement sinusoïdal dans une intro du groupe Miracle

Dans le premier article, nous avons vu comment installer en environnement de développement sur un Amiga émulé avec WinUAE et programmer la liste Copper de base pour afficher quelque chose à l'écran. Dans le deuxième article, nous avons vu comment préparer une police de caractères 16x16 pour en afficher facilement les colonnes de pixels des caractères, précalculer les valeurs du sinus requises pour déformer le texte en modifiant l'ordonnée des colonnes, et mettre en place un triple tampon mémoire pour alterner proprement les images à l'écran. Enfin, dans le troisième article, nous avons vu comment dessiner et animer le défilement sinusoïdal, d'abord au processeur, puis au Blitter.

Dans ce quatrième article, nous allons enjoliver le défilement sinusoïdal avec quelques effets peu coûteux en cycles car assurés par le Copper, et rendre la main aussi proprement que possible au système d'exploitation.

Cliquez ici pour télécharger l'archive contenant le code et les données du programme présenté dans cet article - c'est la même que dans les autres articles.

NB : cet article se lit mieux en écoutant l'excellent module composé par Nuke/Anarchy pour la partie magazine de Stolen Data #7, mais c'est affaire de goût personnel...
  • Mise à jour du 17/07/2017 : attente du Blitter avant de procéder à la finalisation.
Ajouter ombre et miroir grâce au Copper

Qu'est-ce qu'une ombre portée au sud-est, sinon un plan de bits qu'on affiche par-dessous lui-même en le décalant légèrement sur la droite et vers le bas ? Et qu'est-ce qu'un miroir, sinon un plan de bits qu'on continue d'afficher à partir d'une certaine ligne, mais en remontant plutôt qu'en descendant ligne à ligne dans ce dernier ?

Exposés ainsi, les effets d'ombre et de miroir semblent triviaux. Facile à dire ? Facile à faire grâce au Copper, qui permet très simplement de modifier au début de n'importe quelle ligne l'adresse à laquelle le matériel doit lire les données du plan de bits et le retard avec lequel il doit les afficher.

Comme d'habitude, commençons par définir les paramètres des effets à produire :
  • SHADOW_DX et SHADOW_DY correspondent à l'ampleur de l'ombre vers la droite et vers le bas, respectivement, et SHADOW_COLOR, à la couleur de cette dernière.
  • MIRROR_Y correspond à l'ordonnée à laquelle le miroir débute, et MIRROR_COLOR et MIRROR_SCROLL_COLOR, à la couleur du fond et à la couleur du défilement dans le miroir, respectivement.
SHADOW_DX=2	;Compris entre 0 et 15
SHADOW_DY=2
SHADOW_COLOR=$0777
MIRROR_Y=SCROLL_Y+SCROLL_DY
MIRROR_COLOR=$000A
MIRROR_SCROLL_COLOR=$000F

Essayons alors d'y voir plus clair dans la manière dont la liste Copper doit se présenter. L'expérience montre que plutôt que de se lancer tête baissée dans son écriture, il vaut mieux schématiser le déroulement des opérations ligne à ligne - le Copper permet d'effectuer des MOVE en cours de ligne, mais ce ne sera pas utile ici. Pour ne pas surcharger le schéma, la mention à une constante entre crochets signifie qu'il faut lui ajouter DISPLAY_Y.

Programmer un défilement sinusoïdal
WAIT et MOVE pour l'ombre et le miroir

L'ombre tout d'abord

Le matériel lit les données de la ligne du plan de bits à afficher à l'adresse 32 bits figurant dans les registres 16 bits BLT1PTH et BPL1PTL, qu'il incrémente tandis qu'il progresse le long de la ligne. A la fin de la ligne, avant de débuter la suivante, il ajoute BPL1MOD à ces registres pour obtenir l'adresse à laquelle il commence à lire les données de la ligne suivante.

Jusqu'à présent, l'affichage se résumait à un plan de bits, le plan de bits 1. Nous rajoutons un plan de bits 2, en indiquant au matériel que les données de ce second plan de bits sont les mêmes que celle du premier :

	move.l bitplaneA,d0
	move.w #BPL1PTL,(a0)+
	move.w d0,(a0)+
	move.w #BPL2PTL,(a0)+
	move.w d0,(a0)+
	swap d0
	move.w #BPL1PTH,(a0)+
	move.w d0,(a0)+
	move.w #BPL2PTH,(a0)+
	move.w d0,(a0)+

L'ajout d'un plan de bits entraîne quelques modifications supplémentaires à la liste Copper :
  • Spécifier le modulo des plans de bits pairs, et non plus seulement celui des plans de bits impairs :

    	move.w #BPL2MOD,(a0)+
    	move.w #0,(a0)+
    

  • Spécifier deux couleurs supplémentaires, car la palette est étendue à 4 couleurs :

    	move.w #COLOR02,(a0)+
    	move.w #SCROLL_COLOR,(a0)+
    	move.w #COLOR03,(a0)+
    	move.w #SCROLL_COLOR,(a0)+
    

En passant DISPLAY_DEPTH à 2, nous modifions incidemment la valeur que le Copper stocke dans BPLCON0 pour indiquer le nombre de plans de bits, donc rien à modifier ici.

Jusqu'à la ligne [SCROLL_Y+SHADOW_DY-1], les deux plans de bits sont superposés. Le défilement sinusoïdal est donc affiché avec la couleur 3, ce qui explique pourquoi SCROLL_COLOR est stocké dans COLOR03.

A partir de [SCROLL_Y+SHADOW_DY], les deux plans de bits sont décalés :
  • Horizontalement, en passant à SHADOW_DX dans BPLCON1 la valeur du retard avec lequel le matériel affiche les plans de bits pairs. Cette modification doit être demandée au Copper au début de la ligne [SCROLL_Y+SHADOW_DY]. Notez que SHADOW_DX ne peut dépasser 15 ; au-delà, il faut jouer sur BPL2PTH, BPL2PTL et BPLCON1 simultanément.
  • Verticalement, en passant à [-SHADOW_DY*(DISPLAY_DX>>3)] le modulo des plans de bits pairs dans BPL2MOD. Cette modification doit être demandée au Copper au début de la ligne [SCROLL_Y+SHADOW_DY -1] pour affecter la ligne suivante, comme expliqué plus tôt.
Programmer un défilement sinusoïdal
Décalage des plans de bits pour générer l'ombre

A la ligne [SCROLL_Y+SHADOW_DY], l'adresse de la ligne du plan de bits 2 devient celle du plan de bits 1 moins SHADOW_DY lignes. Par la suite, il est nécessaire que l'affichage du plan de bits 2 se poursuive normalement, et c'est pourquoi BPL2MOD doit repasser à 0 au début de la ligne [SCROLL_Y+SHADOW_DY]. A défaut, la ligne répétée le serait indéfiniment.

Le miroir ensuite

Ainsi, BPLxMOD peut être mis à profit pour demander au matériel de revenir en arrière pour répéter l'affichage de plans de bits à partir d'une certaine ligne. Si DISPLAY_DX correspond à la largeur des plans de bits, alors :
  • Au début de la ligne [MIRROR_Y-1], passer le modulo à -(DISPLAY_DX>>3) permettra de répéter la ligne [MIRROR_Y-1] qui va être tracée à la ligne suivante [MIRROR_Y].
  • Au début de la ligne [MIRROR_Y], passer le modulo à -2*(DISPLAY_DX>>3) permettra de répéter la ligne [MIRROR_Y-2] déjà tracée à la ligne suivante [MIRROR_Y+1], produisant un effet de miroir.
  • Tant que ce modulo sera maintenu, il permettra de répéter à la ligne y la ligne tracée en 2*MIRROR_Y-1-y, ce qui perpétuera l'effet de miroir.
Après avoir choisi de faire coïncider le début de l'effet de miroir avec la fin de l'effet d'ombre, il nous reste à modifier les couleurs 0 et 3 via COLOR00 et COLOR03 au début de la ligne [MIRROR_Y] pour que le défilement sinusoïdal se reflète dans un autre milieu.

Programmer un défilement sinusoïdal
Répétition de lignes déjà tracées pour générer le miroir

Tous les MOVE que le Copper doit accomplir pour réaliser ce qui vient d'être décrit ne doivent se produire qu'au tout début de certaines lignes. Ces instructions sont donc précédées de WAIT qui instruisent le Copper d'attendre que le faisceau d'électron a atteint ou dépassé certaines lignes.

Pour rappel, une instruction WAIT portant sur une position (x, y) à l'écran prend la forme de deux mots, un mot (y<<8)!((x>>2)<<1)!$0001 suivi d'un autre servant de masque pour indiquer au Copper sur quels bits des coordonnées la comparaison entre la position indiquée et celle du faisceau d'électrons doit porter.

En l'espèce, nous souhaitons que le Copper se contente de comparer des ordonnées. Aussi tous nos WAIT prennent-ils la forme d'un mot (y<<8)!$0001 suivi d'un mot $FF00.

Nous commençons donc par le décalage vertical de l'ombre... :

	move.w #((DISPLAY_Y+SCROLL_Y+SHADOW_DY-1)<<8)!$0001,(a0)+
	move.w #$FF00,(a0)+
	move.w #BPL2MOD,(a0)+
	move.w #-SHADOW_DY*(DISPLAY_DX>>3),(a0)+

...suivi du décalage horizontal... :

	move.w #((DISPLAY_Y+SCROLL_Y+SHADOW_DY)<<8)!$0001,(a0)+
	move.w #$FF00,(a0)+
	move.w #BPL2MOD,(a0)+
	move.w #0,(a0)+
	move.w #BPLCON1,(a0)+
	move.w #SHADOW_DX<<4,(a0)+

...suivi du de la fin du décalage vertical de l'ombre et du démarrage du miroir... :

	move.w #((DISPLAY_Y+MIRROR_Y-1)<<8)!$0001,(a0)+
	move.w #$FF00,(a0)+
	move.w #BPL1MOD,(a0)+
	move.w #-(DISPLAY_DX>>3),(a0)+
	move.w #BPL2MOD,(a0)+
	move.w #(SHADOW_DY-1)*(DISPLAY_DX>>3),(a0)+

...suivi de la fin du décalage horizontal de l'ombre, et de la répétition des lignes dans le miroir ainsi que de la palette qui s'applique dans ce dernier :

	move.w #((DISPLAY_Y+MIRROR_Y)<<8)!$0001,(a0)+
	move.w #$FF00,(a0)+
	move.w #BPLCON1,(a0)+
	move.w #$0000,(a0)+
	move.w #BPL1MOD,(a0)+
	move.w #-(DISPLAY_DX>>2),(a0)+
	move.w #BPL2MOD,(a0)+
	move.w #-(DISPLAY_DX>>2),(a0)+
	move.w #COLOR00,(a0)+
	move.w #MIRROR_COLOR,(a0)+
	move.w #COLOR03,(a0)+
	move.w #MIRROR_SCROLL_COLOR,(a0)+

Rendre la main au système d'exploitation

Quand l'utilisateur clique sur le bouton de la souris, nous devons rendre la main au système d'exploitation proprement - du moins, aussi proprement que possible, car rien ne garantit qu'il se remette de ce que nous lui avons fait subir en le coupant aussi brutalement du matériel.

Pour commencer, nous nous assurons que le Blitter n'est pas en train de travailler :

	WAITBLIT

Ensuite, nous coupons les interruptions et les canaux DMA... :

	move.w #$7FFF,INTENA(a5)
	move.w #$7FFF,INTREQ(a5)
	move.w #$07FF,DMACON(a5)

...et nous les réactivons après les avoir restaurés dans l'état dans lequel nous les avions trouvés :

	move.w dmacon,d0
	bset #15,d0
	move.w d0,DMACON(a5)
	move.w intreq,d0
	bset #15,d0
	move.w d0,INTREQ(a5)
	move.w intena,d0
	bset #15,d0
	move.w d0,INTENA(a5)

Nous rétablissons alors la liste Copper du Workbench. Son adresse réside à un certain décalage de l'adresse de base de la bibliothèque Graphics. Pour y accéder, nous ouvrons cette bibliothèque par un appel à la fonction OldOpenLib() d'Exec. Une fois l'adresse de la liste Copper récupérée, nous demandons au Copper de l'utiliser désormais :

	lea graphicslibrary,a1
	movea.l $4,a6
	jsr -408(a6)
	move.l d0,a1
	move.l 38(a1),COP1LCH(a5)
	clr.w COPJMP1(a5)
	jsr -414(a6)

Nous rétablissons le fonctionnement normal du système par un appel à la fonction Permit() d'Exec :

	movea.l $4,a6
	jsr -138(a6)

Nous libérons les espaces alloués en mémoire par autant d'appels à la fonction FreeMem() d'Exec :

	movea.l font16,a1
	move.l #256<<5,d0
	movea.l $4,a6
	jsr -210(a6)
	movea.l bitplaneA,a1
	move.l #(DISPLAY_DX*DISPLAY_DY)>>3,d0
	movea.l $4,a6
	jsr -210(a6)
	movea.l bitplaneB,a1
	move.l #(DISPLAY_DX*DISPLAY_DY)>>3,d0
	movea.l $4,a6
	jsr -210(a6)
	movea.l bitplaneC,a1
	move.l #(DISPLAY_DX*DISPLAY_DY)>>3,d0
	movea.l $4,a6
	jsr -210(a6)
	movea.l copperlist,a1
	move.l #COPSIZE,d0
	movea.l $4,a6
	jsr -210(a6)

De là, il ne nous reste plus qu'à dépiler les registres et à rendre la main :

	movem.l (sp)+,d0-d7/a0-a6
	rts

Reste à savoir si tout cela tient dans la trame, et à optimiser le code si tel n'est pas le cas...


[Retour en haut] / [Retour aux articles] [Article précédent] / [Article suivant]