Obligement - L'Amiga au maximum

Vendredi 19 avril 2024 - 19:45  

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 - Programmation au Blitter
(Article écrit par Frédéric Mazué et extrait d'Amiga News Tech - mars 1990)


A l'intérieur du composant baptisé Agnus par ses concepteurs, se trouve un circuit tout à fait remarquable : le Blitter.

Ce circuit a pour rôle de transférer des mots, c'est-à-dire des groupes de deux octets. Ceci est apparemment peu de chose. Mais comme le Blitter est doué d'une rapidité exubérante (traitement de 16 millions de bits par seconde) d'une part et que d'autre part il est capable de faire des "mélanges" de données sans en être ralenti pour autant, il est ainsi tout désigné pour la gestion de graphismes "gros calibre".

Je vois des petits futés qui ont déjà compris que pendant que le Blitter travaille, le microprocesseur 68000 peut s'occuper d'autres choses et que voilà un moyen de programmer des démos qui en mettent plein la vue. A ceux-là nous pouvons répondre que le Copper est capable d'écrire dans les registres du Blitter et donc que la programmation du Blitter peut être incluse dans une liste Copper. Voilà de quoi réaliser des démos encore plus délirantes, de celles que vous n'auriez jamais pu imaginer.

A une condition cependant : maîtriser la programmation de ce fameux Blitter. Sa programmation n'est pas très aisée au premier abord et cette difficulté est aggravée par le fait que la documentation française disponible est souvent peu claire et/ou donne des renseignements erronés. Cette série d'articles est une tentative pour remédier à cette situation. Le plan d'étude proposé est le suivant :
  • Les registres du Blitter en mode transfert.
  • Transfert de mots : permet par exemple de recopier une image dans un plan de bits.
  • Transfert de mots avec opération logique : permet d'éviter ou de provoquer à volonté le recouvrement d'une image par une autre et offre aussi la possibilité de dessiner des trames dans un motif.
  • Transfert de mots avec décalage de bit : permet la programmation de défilement !
  • Remplissage de surfaces.
  • Les registres du Blitter en mode tracé de droite.
  • Tracé de droite. Eh oui, le Blitter sait faire ça aussi, cette fois à une vitesse de 1 million de points par seconde. Il est absolument impossible de programmer une routine de tracé de droite sur le 68000 d'une telle rapidité.
Il y a deux façons de programmer le Blitter

La première façon, le mode "normal", est d'installer des structures Blitter-Node personnelles parmi celles du système d'exploitation de l'Amiga. Le Blitter traitera ces structures quand le système lui en donnera la permission.

La deuxième façon est de s'accaparer le Blitter et d'interdire au système d'exploitation d'y accéder (fini le multitâche). On programme alors directement les registres au Blitter et celui-ci exécute les ordres immédiatement. C'est ce deuxième procédé qui a été choisi pour ces articles, celui qu'utiliseront tous ceux qui veulent programmer leur propre démo.

Les registres du Blitter en mode transfert

Remarque préliminaire : tous les registres du Blitter sont en écriture seulement. On peut y écrire tout ce que l'on veut, mais il est impossible d'en lire le contenu.

L'adresse de base des registres des circuits spécialisés (le Blitter est l'un de ces circuits) est $dff000. Pour chaque registre sera donné un décalage positif, c'est-à-dire à ajouter à l'adresse de base ci-dessus.

BLTDPTH offset $54 : il doit être chargé avec l'adresse de début de la zone mémoire dans laquelle on veut transférer des données (zone cible) à l'aide du Blitter. Ce registre est un registre 19 bits (0-18). Si tous les bits sont mis, le registre contient alors la valeur $7ffff. Le Blitter peut donc adresser une zone de 512 ko maximum. La zone cible devra donc se situer en mémoire CHip. Il faut, en assembleur, charger ce registre de ce type avec une instruction move.l les bits de 18 à 31 n'ayant alors aucune importance. Il est évidemment possible de charger seulement le mot de poids faible (bit 0-15) du registre en utilisant une instruction move.w et en utilisant un décalage de $02.

BLTCPTH offset $48 : le Blitter peut "mélanger" les données de trois zones sources différentes nommées source A, source B, source C. Ce registre qui est un registre 19 bits doit être chargé avec l'adresse de départ de la source C. Mêmes remarques que pour le registre précédent (mémoire Chip).

BLTBPTH offset $4C : registre 19 bits. Il doit être chargé avec l'adresse de départ de la source B. Mêmes remarques que précédemment.

BLTAPTH offset $50 : registre 19 bits. Il doit être chargé avec l'adresse de départ de la source A. Mêmes remarques que précédemment.

BLTDDAT offset $00 : registre 16 bits. Sortie de données. Attention, ce registre est le seul qui ne soit pas accessible au Copper. Toute donnée, avant d'être transférée dans la zone cible, passe par ce registre. L'utilisation de ce registre n'a qu'un intérêt très limité.

BLTCDAT offset $70 : registre 16 bits, données de la source C. Toute donnée de la source C, avant d'être traitée par le Blitter, passe par ce registre (c'est le canal DMA qui s'en occupe). Remarque : charger un registre de données avec une valeur quelconque puis bloquer le canal DMA correspondant permet de remplir la zone cible avec ladite donnée quelconque.

BLTBDAT offset $72 : registre 16 bits, données de la source B, avant d'être traitée par le Blitter, il passe par ce registre.

BLTBDAT offset $74 : registre 16 bits, données de la source A. Toute donnée de la source A, avant d'être traitée par le Blitter, il passe par ce registre.

BLTAFWM offset $46 : registre 16 bits. Ce registre doit contenir la valeur du masque pour le premier mot de chaque "ligne" traitée par le Blitter (source A seulement).

BLTALWM offset $48 : registre 16 bits. Ce registre doit contenir la valeur du masque pour le dernier mot de chaque "ligne" traitée par le Blitter (source A seulement).

BLTDMOD offset $68 : registre 16 bits. Ce registre doit contenir la valeur modulo pour la zone cible. La fonction modulo du Blitter sera expliquée plus loin.

BLTCMOD offset $60 : registre 16 bits. Ce registre doit contenir la valeur modulo de la source C.

BLTBMOD offset $62 : registre 16 bits. Ce registre doit contenir la valeur modulo de la source B.

BLTAMOD offset $64 : registre 16 bits. Ce registre doit contenir la valeur modulo de la source A.

BLTSIZE offset $58 : registre 16 bits. Ce registre doit contenir la taille de la fenêtre Blitter.

Fenêtre Blitter

Tout d'abord, qu'est-ce qu'une fenêtre Blitter ? Une zone (source A par exemple), est une zone de mémoire continue, linéaire. Mais le Blitter, parce qu'il a été conçu pour s'occuper de graphisme, verra les choses différemment et considérera que notre zone est rectangulaire, c'est-à-dire structurée comme un plan de bits. La hauteur du rectangle sera exprimée en nombre de lignes, sa largeur en nombre de mots (un mot = une colonne).

Prenons un exemple pour être plus clair : soit une zone source à transférer, l'adresse de début est $100, l'adresse de fin est $110, la longueur de la source est donc de 16 octets soit 8 mots. On peut dans ce cas définir les fenêtres suivantes :
  • Fenêtre 1 ligne 8 colonnes.
  • Fenêtre 2 lignes 4 colonnes.
  • Fenêtre 4 lignes 2 colonnes.
  • Fenêtre 8 lignes 1 colonne.
Il est important de remarquer que dans cet exemple, il n'est pas possible d'avoir une fenêtre de trois lignes car la longueur de la source n'est pas divisible par trois. Il est également important de savoir que le Blitter traite une zone ligne après ligne en se référant à la fenêtre qui lui a été définie.

Il faut maintenant savoir quelle valeur inscrire dans ce registre. Celui-ci est constitué de la manière suivante :
  • Les six premiers bits (0-5) correspondent à la largeur. Avec 6 bits, on peut écrire toutes les valeurs de 0 à 63. Cependant, si tous ces bits sont annulés, on n'aura pas une fenêtre de largeur 0 (ça ne sera pas très utile), mais une super fenêtre de 64 mots de large. Un mot de 16 bits permet évidemment d'allumer 16 points. Il est donc possible d'avoir une fenêtre de 64*16=1024 points de large.

  • Les dix derniers bits correspondent à la hauteur. De même, avec 10 bits on peut écrire toutes les valeurs de 0 à 1023. Cependant, si tous ces bits sont annulés, on n'aura pas une fenêtre de hauteur 0 (ça n'est pas toujours très utile), mais une super fenêtre de 1024 lignes de haut.
De par cette constitution du registre, il faudra donc utiliser la formule suivante :

valeur pour BLTSIZE=hauteur*64+largeur

Exemple : pour une fenêtre de deux lignes et de quatre colonnes, on a valeur pour BLTSIZE=2*64+4=132.

Attention : dès qu'une valeur est chargée dans BLTSIZE, le Blitter commence son travail. Ce registre doit donc être chargé en dernier.

Le Blitter possède également deux registres de contrôle :

BLTCON0 offset $40 : registre 16 bits définis comme suit :

Bit Nom Fonction
15 ASH3 Ces quatre bits renferment la valeur de...
14 ASH2 ...décalage des données de la source A
13 ASH1 Pas de décalage si les bits sont à 0
12 ASH0
11 USEA Active le canal DMA de la source A si mis
10 USEB Active le canal DMA de la source B si mis
9 USEC Active le canal DMA de la source C si mis
8 USED Active le canal DMA de la source D si mis
7 LF7 Choix Minterm ABC (combinaison de bit 111)
6 LF6 Choix Minterm ABc (combinaison de bit 110)
5 LF5 Choix Minterm AbC (combinaison de bit 101)
4 LF4 Choix Minterm Abc (combinaison de bit 100)
3 LF3 Choix Minterm aBC (combinaison de bit 011)
2 LF2 Choix Minterm aBc (combinaison de bit 010)
1 LF1 Choix Minterm abC (combinaison de bit 001)
0 LF0 Choix Minterm abc (combinaison de bit 000)

L'utilisation des Minterms sera expliquée en détail tout au long de cette étude.

BLTCON1 offset $42 : registre 16 bits définis comme suit :

Bit Nom Fonction
15 BSH3 Ces quatre bits renferment la valeur de...
14 BSH2 ...décalage des données de la source B
13 BSH1 Pas de décalage si les bits sont à 0
12 BSH0
10-5 Ces six bits ne sont jamais utilisés
4 EFE Exlusive Fill Enable
3 IFE Inclusive Fill Enable. Ces deux bits sont utilisés lors de
remplissage de surface. Ils doivent être à 0 pour
un simple transfert d'octets
2 FCI Ce bit concerne aussi le remplissage de surface.
Il doit être à 0 pour un simple transfert
1 DESC Commute le mode décrémentation
0 LINE Active le mode tracé de lignes

Explication du bit DESC

Normalement, le Blitter travaille par incrémentation. Si nous avons par exemple chargé BLTAPTH avec l'adresse de début de la source A et BLTDPTH avec l'adresse de début de la zone cible, le Blitter prendra le mot à l'adresse pointée par BLTAPTH pour 1 et mettre à l'adresse pointée par BLTDPTH puis incrémente de 2 (n'oublions pas que le Blitter travaille avec des mots) les registres BLTAPTH et BLTDPTH et ainsi de suite autant de fois qu'il est demandé dans BLTSIZE. En mode décrémentation, le Blitter décremente les registres. Il faudrait donc dans notre exemple charger respectivement BLTAPTH et BLTDPTH avec l'adresse de fin de la source A et l'adresse de fin de la zone cible. Ceci peut être très utile pour éviter des recouvrements de zone mémoire.

Voilà, nous en avons fini pour l'instant avec les registres du Blitter. Néanmoins, nous devons aussi nous intéresser à quelques bits du registre DMACON (décalage $96 pour l'écriture en $02 pour la lecture).

Le bit 6 nommé BLTEN. Si ce bit est mis, le Blitter a accès aux canaux DMA et peut travailler. Sinon le Blitter ne peut rien faire.

Le bit 10 nommé BLTPRI. Si ce bit est mis, le Blitter a priorité sur le 68000 en ce qui concerne l'accès à la mémoire Chip. Donc le Blitter travaille à pleine vitesse mais, par contre, le 68000 ne peut pas non plus mettre une valeur dans un des registres spécialisés, ce qui peut présenter aussi quelques inconvénients. Par contre, le 68000 peut toujours travailler avec le contenu de la mamoire Fast. Mon avis est qu'il est très rarement valable de mettre ce bit car le Blitter est déjà tellement rapide sans cela que le gain n'est pas palpable. Par contre, le fait de ne pas pouvoir programmer de sons par exemple pendant que le Blitter travaille (dès qu'il a fini) on peut parfois être embêtant.

Le bit 13 nommé BZERO. Ce bit peut seulement être lu, on ne peut pas le forcer. C'est le Blitter lui-même qui place ce bit quand il a remarqué que tous les bits résultats sont nuls. On peut utiliser ceci, à la condition de très bien manipuler les Minterms pour faire des tests de collisions ou autre. En fait, l'intérêt de ce bit n'apparaît que quand on maîtrise très bien la programmation du Blitter, ce n'est donc pas la peine d'en parler davantage pour l'instant.

Le bit 14 nommé BBUSY. Ce bit peut seulement être lu, on ne peut pas le forcer. C'est le Blitter lui-même qui place ce bit dès qu'il commence à travailler. Il l'enlèvera dès qu'il aura fini. Il faut toujours tester ce bit avant de programmer le Blitter car si l'on modifie un de ses registres pendant qu'il travaille, on risque de provoquer un plantage spectaculaire.

Enfin, pour être complet, il faut signaler que lorsque le Blitter a terminé un travail, il libère une interruption de niveau 3 du point de vue du microprocesseur 68000 et de priorité 6 du point de vue du système d'exploitation de l'Amiga.

La pratique

Ouf ! Nous en avons fini avec la théorie et nous pouvons passer à la pratique. Tous les programmes d'exemple ont été écrits avec Devpac 2 mais ne doivent pas présenter de problèmes de compatibilité avec K-Seka. J'ai choisi pour chaque exemple de travailler avec des écrans ouverts sous Intuition, afin de ne pas introduire de difficultés supplémentaires du type RastPort et autres. Lorsqu'on ouvre un écran sous Intuition, celle-ci renvoie un pointeur sur la structure de l'écran ouvert. Dans cette structure, on trouve aux décalages $C0, $C4, $C8, $CC l'adresse des plans de bits, c'est pratique.

Copie d'une image dans un plan de bits (NDLR : il manque le listing !)

Le programme commence par ouvrir les bibliothèques Intuition et Graphics. Puis il réserve de la mémoire en mémoire Chip et y transfère les données de l'image. Évidemment, les heureux possesseurs de Devpac 2 pourront supprimer ces lignes et demander à leur assembleur préféré de faire en sorte que ces données soient chargées directement en mémoire Chip. Puis le programme ouvre un écran Intuition à 1 plan de bits.

Le Blitter est un circuit qui est en permanence sollicité par le système d'exploitation de l'Amiga pour accomplir les tâches les plus diverses. Pour pouvoir le programmer tranquillement, il faut s'en réserver l'usage. C'est ce que permet la routine OwnBlitter de la graphics.library. L'inconvénient est que dès que le Blitter est ainsi accaparé, il n'est plus possible de tracer le programme pas à pas car le système d'exploitation ne peut même plus afficher un simple caractère à l'écran.

Puis le début des données de l'image est placé dans BLTAPTH. Comme il n'est pas question pour l'instant de mélanger plusieurs images, il n'y a pas à s'occuper de BLTBPTH et BLTCPTH.

Puis on récupère depuis la structure de l'écran à l'adresse du plan de bits à laquelle on ajoute de quoi placer l'image approximativement l'image au milieu de l'écran (une ligne de 320 points compte 40 octets). Nous avons ainsi l'adresse de la zone cible qui est chargée dans BLTDPTH.

Puis vient l'instruction "move.w=%0000100111110000, BLTCON06a0)". On voit ici que les bits 12-15 sont annulés ; c'est normal car on ne veut pas faire de décalage dans les données. On voit également que seuls les transferts DMA source A et zone cible sont autorisés : c'est normal puisqu'on n'a pas initialisé les registres BLTBPTH et BLTCPTH.

Puis viennent les bits Minterms. Mais comment déterminer quels bits doivent être placés ? Pour bien comprendre ce qui suit, il faut toujours se rappeler que si on doit communiquer un nombre de mots à traiter au Blitter dans BLTSIZE, celui-ci effectue en fait un test logique sur chacun des 16 bits de chaque mot.

Adoptons la convention suivante :
  • Une lettre majuscule correspond à un bit mis.
  • Une lettre minuscule correspond à un bit annulé.
  • Chacun des bits Minterms défini au Blitter à quelles conditions il devra mettre un bit dans la zone cible.
Attention : pour choisir les Minterms, il ne faut absolument pas tenir compte du fait que certains canaux DMA puissent être bloqués par les bits 8-11 du registre BLTCON0, ceci n'ayant rien à voir.

Voyons maintenant quelques exemples : si seul LF7 est mis dans BLTCON0, LF7 correspond à la combinaison de bits ABC. Ceci signifie que le Blitter mettra un bit dans la zone cible si et seulement s'il trouve un bit placé dans chaque zone source.
  • Si la source A contient "%0000111101101011" pour le premier mot.
  • Si la source B contient "%1100000110011101" pour le premier mot.
  • Si la source C contient "%1111111001100111" pour le premier mot.
...alors, après travail du Blitter, la zone cible D contiendra "%0000000000000001" pour le premier mot.

Autre exemple : seul le bit LF0 est activé ce qui correspond à la combinaison abc dans ce cas :
  • Source A : %1111111100000000.
  • Source B : %1111111100000000.
  • Source C : %1111111100000000.
...donne cible D : %0000000011111111.

Autre exemple : LF7 et LF6 sont activés ce qui correspond aux combinaisons ABC et ABc, le Blitter placera un bit dans la cible à condition que :
  • Les bits sources A et B soient placés.
  • Les bits source C soient ou ne soient pas placés.
Moralité : le Blitter ne tient dans ce cas aucun compte du contenu de la source C.

Très important : il faut toujours utiliser cette possibilité d'annihiler une source car même si on bloque les canaux DMA, les registres BLTADAT, BLTBDAT et BLTCDAT contiennent toujours quelque chose et le Blitter ferait une opération logique avec ce quelque chose.

Exemple du programme : on a LF7, LF6, LF5 et LF4 mis, ce sont les combinaisons ABC, ABc, AbC et Abc. On voit ainsi que les sources B et C sont annihilées et on aura une simple copie de la source A (facile, non ?).

Puis, tous les bits de BLTCON1 sont annulés puisqu'on ne fait rien de spécial là-dedans aujourd'hui. Puis vient l'instruction move.w=$00,BLTAMOD(a0) car le Blitter doit transférer chaque mot de données de l'image de façon continue.

Puis vient l'instruction move.w=(20-9)2,BLTDMOD(a0).

Explication : la zone cible, tout le plan de bits de l'écran Intuition, est beaucoup plus grande que la source A, l'image. Si l'on mettait également 0 dans BLTDMOD, le Blitter placerait les bits de façon continue dans la cible sans tenir compte du fait qu'une ligne d'image est plus petite qu'une ligne d'écran. Ainsi l'image serait déformée.

La valeur modulo est fait pour avertir le Blitter de cette différence afin que celui-ci sache aller "à la ligne". La largeur de l'écran dans notre exemple est de 20 mots, la largeur de l'image dans notre exemple est de 9 mots, la différence est de 11 mots. Il faut encore multiplier le résultat par 2 car (très bizarrement), le Blitter travaille avec des octets pour les valeurs modulos.

En résumé, la valeur modulo est la différence entre la largeur de la zone cible et largeur de la source, cette différence étant exprimée en octets.

Passons sur les deux instructions suivantes qui seront expliquées la prochaine fois. Enfin, le registre BLTSIZE est chargé conformément à la formule donnée plus haut et le Blitter commence son travail.

Quand ce travail est terminé, le programme restitue le Blitter au système d'exploitation (DisownBlitter) puis attend un clic de la souris. Avant de rendre la main, le programme referme l'écran Intuition et libère la mémoire.

C'est terminé pour cette fois. Je vous propose, histoire de vous faire la main, d'essayer de dessiner l'image de votre choix en modifiant ce que bon vous semble au label "data" du programme. N'oubliez pas de modifier le cas échéant la valeur dans BLTSIZE. Rassurez-vous, avec un peu de pratique la programmation du Blitter vous deviendra assez aisée.

Le mois prochain, nous verrons les transferts avec opérations logiques.


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