Scrolling sous intuition en assembleur Plus l'Amiga se fait vieux et plus les limites de ses performances sont repoussées encore et toujours par les programmeurs (ou devrais-je dire 'codeurs') de démos et de jeux. Ainsi, on a d'abord vu l'apparition du scrolling horizontal et vertical, puis multi-directionnel et enfin en playfield, avec le paralaxe. Dans ce domaine, je pense qu'on a fait à peu près le tour de la question et il n'y a pas grand chose à rajouter. Mais il en est tout autrement sous intuition, ou plus exactement en multi-tâche. Pour ceux qui ne voient pas la différence, sachez que la programmation d'une démo ou d'un jeu passe par la gestion directe du hardware afin d'augmenter la vitesse d'exécution. Sous intuition, le programmeur doit appeler des fonctions systèmes qui se chargeront de gérer le hardware. C'est à la fois plus simple et plus compliqué ( c'est d'ailleurs ce qui fait son charme !) : plus simple car il est toujours plus facile de donner des ordres à quelqu'un d'autre (ici la fonction système) que d'exécuter le travail soi-même (en l'occurence gérer le hardware), mais c'est aussi plus dur car le programme doit être multi-tâche et on ne fait pas tout ce que l'on veut. On va donc s'amuser durant plusieurs numéros du Petit Amiga Illustré à se concocter des programmes (ou plutôt des bouts de programmes, car tous seuls il ne serviront quasiment à rien) qui scrollent quelque chose. On va d'abord commencer par un petit texte, puis on finira par une image bitmap en multi-directionnel (pensez aux applications pour les programmes de dessins avec les zooms). Dans ce premier article, on va se mettre dans l'ambiance du scrolling. Pour ce faire on va écrire un programme tout bête qui affiche le contenu de la mémoire en ASCII et qui offre la possibilité de se déplacer dedans. Le déplacement se fera par scrolle (comme par hasard !), lorsqu'on appuiera sur le bouton gauche de la souris et que l'on déplacera cette dernière. Pour quitter le programme, il suffira d'appuyer sur le bouton droit. Il nous faut donc : 1 écran sans menu (c'est toujours ça de moins). 1 fenêtre d'arrière plan (BACKDROP) où s'affichera le contenu de la mémoire. De plus elle n'aura pas de bord (BORDLESS). 1 fonction qui écrit du texte sur tout l'écran (pour l'initialisation). 1 fonction qui scrolle et qui écrit des bouts de texte (en haut ou en bas, suivant le sens du scrolle). et puis pour le reste on verra bien. Tout d'abord voici l'aspect du programme : - ouverture des bibliothèques : intuition.library graphics.library - ouverture de l'écran - ouverture de la fenêtre - initialisation de l'écran : on appelle RedrawDisplay - Attente et gestion des événements en provenance d'intuition ici il s'agit de l'appuie sur les boutons de la souris et du déplacement de cette dernière - traitement du scrolling - fermeture de la fenêtre - fermeture de l'écran - fermeture des bibliothèques : graphics.library intuition.library plus deux supers routines : RedrawDisplay qui redessine tout le contenu de la fenêtre. Dans notre cas, elle n'est appelée qu'une fois, à l'initialisation. RedrawTitle qui rafraîchit la barre de l'écran où est écrit l'adresse du début de la mémoire qui est actuellement affichée. Vous remarquerez que le programme est symétrique par rapport à la routine principale d'attente et aux routines annexes (traitement du scroll), ainsi la première library ouverte est la dernière fermée. Cette programmation symétrique permet d'éviter certains bugs. Revenons à la partie scrolling. La routine est très simple : à chaque mouvement de souris (quand le bouton gauche est appuyé) le programme déplace le contenu de l'écran à l'aide de la fonction système ScrollRaster. Ensuite il écrit la partie manquante du texte en bas ou en haut de l'écran. Pour terminer il appelle RedrawTitle pour réactualiser le pointeur. ScrollRaster est une fonction de la graphics.library. Lors de son appel, les registres doivent être initialisés. Ainsi A1 contient la structure RastPort de la fenêtre où doit avoir lieu l'action. Son adresse a été préalablement récupéré par le programme après l'ouverture de la fenêtre. Une structure RastPort contient un tas d'informations indispensable aux fonctions de la graphics.library pour connaître la couleur du fond, de l'écriture, les fonts utilisées, la position du "pinceau", etc. Elle contient aussi un pointeur sur la structure BitMap qui contient l'adresse de l'écran. C'est à dire l'adresse où se trouve les données qui déterminent l'affichage de l'écran. Un écran est caractérisé par sa longueur (en pixels), sa hauteur (en pixels) et son nombre de couleurs. Pour déterminer la couleur de chaque pixel de l'écran, on a recours aux bit-planes. Les bit-planes sont... des plans de bits. Ils ont pour chaque pixel un bit qui lui correspond. Ainsi dans un écran à deux bit-planes chaque pixel sera caractérisé par deux bits : un bit dans chaque bit-planes. Il pourra donc prendre quatre états : les deux bits sont à zéro, le premier est à zéro et l'autre à un, le premier est à un et l'autre à zéro ou les deux sont à un. Cas concret : un écran de 320 par 256 (320 pixels de long et 256 pixels de haut) de 8 couleurs (3 bit-planes) utilisera en mémoire 320*256*3 bits soit 245760 bits ou plus clairement 30 Ko. Mais attention, ses 30 Ko ne sont pas forcément regroupés en mémoire, car les bit-planes sont totalement dissociés les uns des autres. C'est là où la structure BitMap intervient : elle donne l'adresse de chaque bit-plane en mémoire, ainsi que le nombre total de bit-plane (Depth : la profondeur) et aussi le format de l'écran en précisant le nombre de lignes (Rows) et le nombre d'octets par ligne (BytesPerRow). Ainsi la longueur d'un écran ne peut être que multiple de 8, car il y a 8 bits par octet. Pour terminer avec les bit-planes, sachez que l'Amiga n'autorise que 5 bit-planes maximum en basse résolution, c'est à dire 32 couleurs. Cette limitation provient du... hardware. Car c'est la génialité de l'Amiga : le système a été conçu autour du hardware en exploitant au maximum ses possibilités. Revenons en à ScrollRaster. Cette fonction a donc besoin de connaître l'emplacement de l'écran. Elle a aussi besoin de connaître la partie de la fenêtre qui doit être scrollée. On met donc en d2,d3,d4,d5 les coordonnées de la partie à scroller. Et pour terminer d0 et d1 contiennent le nombre de lignes et de colonnes à scroller, en tenant compte du signe qui détermine le sens. Dans notre cas d0 contient 0 car il ne s'agit que d'un scrolling vertical. Pour en terminer avec ScrollRaster, sachez que cette fonction scrolle dans la partie qu'on lui a désigné, tout ce qui est autour reste inchangé. De plus l'espace libéré par le scroll est effacé avec la fonction RectFill. Passons à la seconde partie du scroll, à savoir le remplissage des espaces libérés. Pour ce faire, on utilise la seule fonction d'écriture qui existe : Text. Cette fonction est aussi de la graphics.library et ne doit pas être confondue avec PrintIText qui n'est autre qu'une fonction de l'intuition.library et qui fait appel à... Text ! Donc pour le texte, il n'y a de problème de choix, il n'y a qu'une fonction ! Text nécessite aussi l'adresse de la structure RastPort en A1. Cette structure lui fournit pas mal de renseignements sur l'écriture : font, couleur de l'écriture, du fond, mode d'écriture, endroit où l'on doit écrire (déterminé avec la fonction Move), etc. Petite anecdote : tous ces paramètres sont inclus dans la structure IntuiText utilisée par PrintIText, cette dernière se chargeant de les mettre dans la structure RastPort à l'aide d'autres fonctions de la graphics.library (Move, SetDrMd, SetAPen, SetBPen...). Text a aussi besoin de savoir où se situe le texte (adresse du texte en A0) et quel est sa longueur (en D0). La copine de Text c'est Move. Elle nécessite en A1 l'adresse de la structure RastPort et en D0 et D1 la nouvelle position du "pinceau". (Je dis "pinceau" et non pas curseur, car c'est aussi valable pour le tracé de traits avec Draw). Tout ce qu'il faut retenir de cette méthode de scrolling, que nous utiliserons tout le temps, c'est : - déplacement de l'écran - écriture des parties manquantes C'est tout ! Il ne vous reste plus qu'à compiler et à exécuter le programme. Maintenons, passons à la critique : Le scrolle n'est pas très fluide (surtout si l'écran est en quatre couleurs ou plus). La prochaine fois nous verrons une autre routine mieux adaptée avec une petite astuce. Quand on déplace beaucoup la souris, le scrolle continue quand la souris s'arrête, c'est très gênant ! Pour ça il existe plusieurs méthodes. On peut augmenter le 'pas' minimum de déplacement de la souris pour mettre en branle le scrolle, ainsi on arrive à synchroniser l'affichage avec le déplacement. On peut aussi, à l'aide de la fonction système ReportMouse, arrêter l'envoie des messages de déplacement de souris durant le scrolle. Ainsi le programme ne prend pas de retard sur les messages (les messages ne bouchent pas le Port). Donc pour le mois prochain vous allez me préparer un programme amélioré qui utilise au mieux l'une de ces méthodes. De plus je veux que lorsqu'on appuie sur le bouton gauche puis sur le bouton droit (tout en maintenant le gauche appuyé), le scrolle aille d'écran en écran. Pour vous aider, sachez qu'il faut utiliser la routine RedrawDisplay à la place de ScrollRaster + Text. Il n'y aura pas de corrigé. Mais le premier qui m'envoie son programme amélioré, aura droit à... toute mon estime (ça vaut le coup d'essayer !). Le mois prochain nous verrons donc une routine beaucoup plus fluide (quelque soit le nombre de couleurs) et qui utilise la req.library avec un gadget proportionnel (un ascenseur). Grâce à la req.library le problème de synchronisation souris-affichage sera réglé. A très bientôt et bonne programmation ! Christophe LARATTE 6 allée Marcel Pagnol 87410 Le Palais sur Vienne