Suivez-nous sur X

|
|
|
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
|
|
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
|
|
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
|
|
A propos d'Obligement
|
|
David Brunet
|
|
|
|
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 :
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.
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.
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.
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 :
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...
|