Initiation au GFA Basic
(Partie 1)



Par Exyl/Sector One



Souvenez-vous de la grande époque où le Toxic Mag proposait diverses initiations entre autres aux langages de programmation. Et bien cette époque n'est pas révolue puisque débute aujourd'hui, en l'an de grace 1999, une initiation au GFA ! Les articles la composant seront fournis car la parution du Toxic Mag est irrégulière et assez rare, cela permettra donc aux nombreux :-) lecteurs de ne pas passer trop de temps à attendre la suite sans rien faire. Si, malgré tout, le rythme est trop lent pour vous, vous pourrez toujours écrire quelques articles pour le Toxic Mag qui manque un peu de volontaires ces temps-ci. Le sujet est toujours libre donc n'hésitez pas... Je voudrais aussi préciser que cette initiation vise à créer des programmes non-GEM donc les fonctions du GFA dédies au GEM ne seront pas abordées.


Bon, passons aux choses sérieuses.



I - Les instructions les plus utiles.


Certains d'entre vous n'auront, je pense, pas besoin de lire cette partie entièrement mais je vous conseille de la parcourir tout de même au cas où certains points vous auraient echappé ou si vous aviez pris de mauvaises habitudes. Cette partie est longue et peu attractive pour les débutants mais ne vous laissez pas rebuter si vous ne retenez pas tout, arrêtez et allez jeter un coup d'oeil aux programmes d'exemples joints. Ils vous permetront peut-être de mieux vous rendre compte de l'utilisation des instructions dont vous lirez la description et il est vrai que rien ne remplace un exemple concret.


Voyons d'abord les types de variables utilisées pas le GFA ou en tous cas les plus utiles.



1) Les types de variables.


Dans certains langages évolués (comme le C par exemple) il est indispensable de déclarer le type des variables avant de les utiliser. Ce n'est pas le cas en GFA mais cela n'a pas que des avantages : en effet il est beaucoup plus tentant d'utiliser un grand nombre de variables sans se soucier de ce qu'elles deviennent après utilisation. C'est l'une des erreurs que nous allons essayer d'éviter mais nous n'en sommes pas encore là : commençons par apprendre à ne pas gaspiller.


La plupart des variables sont des nombres et on peut leur donner le nom qu'on veut. Si j'écris "a=10" lors de l'exécution, la variable "a" vaudra 10 mais j'aurai tout aussi bien pu l'appeler "var" ou "tartenpion" si j'avais voulu, il faut juste utiliser le même nom quand on la réutilise plus tard.


On a donc donné la valeur 10 à "a" et là je dis "Stop" non pas que je décide de vous priver d'initiation au GFA pour protester contre l'élevage des concombre de mer en batterie mais la variable "a" est pour le GFA une variable à virgule et a une precision de 11 chiffres ce qui est un beau gachis pour une variable qui n'en utilise que 2 et qui est un entier. Il faut donc specifier au GFA que l'on veut que cette variable ait une autre type :


"a|" me semble parfait ici car le "|" indique que cette variable sera codée sur un octet et qu'elle pourra donc prendre des valeurs entieres comprises entre 0 et 255. Pour 10 c'est donc le type idéal.


Si l'on prévoit que la variable soumise aux additions, soustractions, multiplications et autres tortures que vous lui infligerez évoluera et deviendra negative ou depassera 255, le codage sur un octet qui correspond au suffixe "|" ne sera plus possible et vous vous trouverez face à un message d'erreur. On utilisera donc le suffixe "&" qui indique une variable codée sur deux octets (un mot comme on dit). La variable sera donc "a&" (ou tartenpion&) Attention cependant à ne pas changer de suffixe au cours du programme car pour le GFA "a", "a|" et "a&" sont trois variables bien distinctes. Vous pouvez donc utiliser des variables dont les noms ne diffèrent que par leur suffixe mais c'est un exercice périlleux car le bug est vite arrivé. On cherche alors pendant 1 heure pour finalement se rendre compte qu'on a mis "|" à la place de "&"... Les variables codées sur 2 octets peuvent prendre des valeurs entières comprises entre -32768 et +32767.


Enfin si votre valeur doit depasser ces valeurs utilisez le suffixe "%" qui indique une variable codée sur 4 octets (mot long) La valeur peut varier de -2147483648 à +2147483647... ça vous laisse de la marge. Un problème se pose pourtant lorsqu'on veut utiliser un nombre à virgule... dans ce cas on utilisera le type de variable vu au debut : sans suffixe.


On a donc vu 4 types de variables chiffrées correspondant aux suffixes suivants : pas de suffixe, "|", "&" et "%".


Il existe d'autres types de variables : les textes et un type un peu particulier de variable chiffrée : les Booléenes.


On utilise le suffixe "$" pour les variables textes. On peut par exemple ecrire : t$="j'aime le Toxic Mag" et la variable "t$" contiendra la chaine de caractères entrée entre guillemets. Les variables contenant des chaines de caractère sont particulières à manier mais nous verrons cela plus tard.


Enfin les variables booléenes ont pour suffixe "!" et sont codées sur un seul bit. Elles ne peuvent donc prendre que deux valeurs, 0 ou -1, en GFA on utilise TRUE ou FALSE (vrai ou faux, quoi). Leur intérêt peut vous parraitre limité mais elles sont parfois bien utiles dans certains cas.



2) Les bonnes habitudes.


Connaître les types de variables permet d'éviter d'encombrer la RAM : même si mettre "&" à la place de "%" ne fait gagner que 2 octets c'est toujours ça de pris et la vitesse d'exécution sera (pour la plupart des opérations) plus grande. Alors n'hésitez pas, quand cela est possible, à utiliser le suffixe.


Encore pour économiser de la RAM, n'oubliez pas d'effacer vos variables. Utilisez pour cela l'instruction CLR suivi du nom de la variable à effacer (sans oublier son suffixe). Cela n'a en fait pas une importance primordiale pour les variables numériques qui ne prennent pas énormément de place (mais il vaut mieux le faire quand même, c'est toujours ça de pris). Par contre il est très important d'effacer les variables texte car les chaines de caractères qu'elles contiennent peuvent aller jusqu'à 32768 octets ! A ce train là, quelques chaines oubliées et la RAM libre a diminué de moitié...



3) Les instuctions de base.


Cette partie peut également être passée par ceux qui ont déjà une certaine expérience du GFA mais vérifiez quand même que vous connaissez toutes les instructions qui y sont décrites.


- a - Les conditions.


Elles sont essentielles et servent sans arrêt. Voici la syntaxe:


IF condition
  machin
ENDIF


Si la condition est vérifiée, la partie du progamme "machin" située entre les instructions "IF condition" et "ENDIF" sera exécutée sinon on passera directement à l'instruction suivant le ENDIF sans passer par la case départ et gagner 20 000 F (ouh la je m'égare... :) On passera à cette instruction sans exécuter le bloc d'instructions machin.


Il existe une variante à cette syntaxe :


IF condition
  bidule
ELSE
  truc
ENDIF


Ici on a ajouté un "ELSE" (sinon en français). Bidule sera exécuté si la condition est verifiée sinon on exécutera truc.


Les conditions peuvent être très diverses mais elles sont souvent de la forme suivante : IF var=n. Var est une variable quelconque (qui aurait tres bien pu avoir un suffixe) et n un nombre. On aura donc des choses comme :


IF a=3.4
ou
IF v&=-16
ou
IF a%=76348
ou
IF key$="frqs"
ou enfin
IF test!=TRUE


On peut également combiner plusieurs conditions par des AND ou des OR.


IF condition1 AND condition2
  chose
ENDIF


Dans ce cas, chose ne sera executé que si les 2 conditions (la condition1 ET la condition2) sont verifiées.


IF condition1 OR condition2
  chose
ENDIF


Dans ce cas, chose sera exécuté si au moins l'une des 2 conditions est verifiée.


On peut ainsi mettre plusieurs conditions à la suite séparées par des AND ou des OR (on peut en mettre plus que 2 si c'est necessaire). Attention cependant à ne pas abuser des AND car l'exécution d'un IF comportant beaucoup de AND peut parfois être très longue, mieux vaut dans ce cas combiner les IF comme suit :


IF condition1
  IF condition2
    chose
  ENDIF
ENDIF


Ç fait exactement la même chose en plus rapide.


C'est toujours un peu difficile de s'y retrouver dans les conditions au debut mais ça vient tres vite.


- b - Les boucles.


Les boucles sont un élément essentiel du langage basic et il en existe plusieurs types :


DO
...
LOOP


Cette boucle est exécutée indéfiniment : lorsque l'ordinateur a effectué la dernière instruction avant LOOP, il revient à DO et exécute l'instruction qui suit. On peut sortir de cette boucle avec les instructions EXIT IF ou GOTO. EXIT IF n'est pas une condition comme celle qu'on a vu précédemment : on ne met pas de bloc d'instructions à exécuter si la condition est vérifiée ni de ENDIF pour marquer la fin, mais si la condition est satisfaite, l'exécution de la boucle s'arrêtera et l'instruction suivant le LOOP sera exécutée. On peut avoir par exemple :


DO
  truc
  EXIT IF t!=TRUE
LOOP


L'instruction GOTO indique que l'exécution ne doit pas se poursuivre linéairement mais que l'on doit directement passer à un endroit marqué par un label. Exemple :


DO
  truc
  IF t!= TRUE
    GOTO mk
  ENDIF
LOOP
machin
mk:
bidule


Ici on exécute la boucle jusqu'à ce que t!=TRUE puis on passe directement à bidule sans exécuter machin (car le label mk est situé juste avant bidule).


On peut encore sortir d'une boucle DO LOOP avec un UNTIL (jusqu'à en francais). La boucle est exécutée jusqu'à ce que la condition suivant le UNTIL soit satisfaite. Le premier exemple est équivalent à:


DO
  truc
LOOP UNTIL t!=TRUE


On boucle jusqu'à ce que t! soit vrai.



Passons maintenant à la boucle FOR NEXT qui est l'une des plus utiles et dont la syntaxe est la suivante :


FOR var=a TO b STEP c
  truc
NEXT var


C'est une boucle finie qui ne sera exécutée qu'un certain nombre de fois. Au depart de la boucle , "var" vaudra "a", à chaque fois qu'on arrivera au next on ajoutera "c" à "var" ("c" peut être positif ou negatif) puis on réexécutera la boucle et lorsque var dépasse ou égale la valeur "c" la boucle se termine.


La plupart du temps on utilise des boucles FOR NEXT sans le paramètre "STEP c" donc le "pas" est par defaut de 1 et on ajoutera 1 à "var" à chaque fois qu'on rencontrera NEXT var. La boucle suivante sera exécutée en tout 16 fois (attention de ne pas oublier qu'elle commence à 0)


FOR a&=0 TO 15
  truc
NEXT a&


Attention : Le GFA semble avoir quelques problèmes avec les boucles FOR NEXT lorsque la variable est codée sur un octet (sufixe "|") et les STEP negatifs même si la variable reste toujours positive.


L'utilisation de FOR NEXT est particulièrement avantageuse car cela permet d'une part de faire des boucles avec un nombre fini d'itérations et d'autres part d'avoir un paramètre qui peut être utilisé dans le bloc d'instructions contenu dans la boucle. En effet on peut utiliser la valeur de la variable (a& dans l'exemple precédent) pour faire des calculs. Attention toutefois de ne pas modifier la valeur de a& dans la boucle car sinon le compteur sera perturbé, le nombre d'étapes ne sera plus celui voulu, etc.


Il existe d'autres instructions permettant de faire des boucles mais elles n'apportent rien de plus que celles décrites cidessus donc je ne les aborderais pas ici. S'il m'arrive de les utiliser dans un programme d'exemple, j'annoterai alors pour vous expliquer leur effet.


- c - Les communications avec l'utilisateur.


Les péripheriques tels que le clavier et la souris permettent de communiquer avec l'utilisateur, certaines instructions en GFA permettent de récupérer les informations provenant de ces periphériques.


- Le clavier


L'instruction INKEY$ renvoie la touche appuyée au moment où elle est exécutée. Il faut stocker cette valeur dans une variable du même type qu'elle donc une variable avec le sufixe "$". Si l'on fait a$=INKEY$ on aura dans a$ la touche appuyée au clavier au moment ou l'instruction a été exécutée. Si aucune touche n'était enfoncée on aura a$="", soit une chaine vide. Donc si l'on veut attendre qu'une touche soit appuyée on fait :


DO
LOOP UNTIL INKEY$<>""


On boucle indefiniment jusqu'à ce que la valeur renvoyée par INKEY$ soit différente de la chaine de caractère vide. Il existe une autre instruction permettant de faire la même chose mais de manière plus "système" c'est ~INP(2). Le "~" est un équivalent de "VOID" dans les anciennes versions du GFA et indique que la valeur renvoyée ne doit pas être mémorisée. L'intérêt de ~INP(2) est d'être plus court à taper et encombre moins le code.


Il existe encore d'autres instructions permettant notamment de tester les touches spéciales telles que les flèches, les touches de fonction (F1 à F10) et les touches SHIFT, CONTROL, ALTERNATE et autres mais nous ne les verrons pas aujourd'hui.


- La souris


Il existe 3 instructions pour gérer la souris :

MOUSEX indique l'abscisse du plointeur à l'ecran
MOUSEY son ordonnée
MOUSEK l'état des boutons


Par exemple, en ST Basse, MOUSEX peut varier de 0 à 319 et MOUSEY de 0 à 199. Quelle que soit la résolution, MOUSEK peut prendre 4 valeurs :


0 : aucun bouton appuyé
1 : bouton gauche appuyé, droit relaché
2 : bouton gauche relaché, droit appuyé
3 : bouton gauche appuyé, droit appuyé


On peut demander de cacher le pointeur de la souris en utilisant l'instruction HIDEM et cela n'affecte pas les résulats des instructions de test de la souris : le pointeur est caché mais la souris fonctionne toujours.


- Le joystick


On peut tester l'état du bouton de tir avec l'instruction STRIG(1) qui renvoie TRUE (-1 en decimal) si il est enfoncé et FALSE (0) si ce n'est pas le cas. L'état du bouton est codé sur un seul bit donc les variables en "!" sont adaptées pour le stocker.


La direction du manche est donnée par STICK(1). On obtient :


Haut   : 1          Haut-gauche : 5          Centre : 0
Bas    : 2          Bas-gauche  : 6
Gauche : 4          Haut-droite : 9
Droite : 8          Bas-droite  : 10


Les valeurs de la direction du manche vont de 0 à 10 donc les variables codées sur un octet ("|") conviennent le mieux pour stocker le résultat du test.


L'utilisation de ces deux instructions ont un inconvénient : elles obligent à changer l'interruption clavier donc empêche la gestion de la souris. Une fois les tests du joy terminés il faut restaurer la gestion de la souris en demandant par exemple un test bouton avec MOUSEK.


- d - La gestion des variables.


Commençons par voir une instruction indispensable même pour les programmes qui n'utilisent ni le GEM, ni la fonte système : PRINT cette instruction permet d'afficher à l'ecran une chaine de caractère. Elle est indispensable car elle est très pratique pour contrôler si, par exemple, une de vos routine fonctionne bien. En effet, depuis le début de cette initiation nous avons vu comment utiliser des variable mais pas comment écrire le résulat à l'écran alors comment être sur que nous n'avons pas fait d'erreur si nous n'avons pas de moyen de contrôler le résultat ? L'instruction ne sera certainement plus présente dans les versions finales de vos programmes mais elle est utile lors de leur élaboration. Voyons donc comment fonctionne cette instruction.


PRINT var


Il suffit en fait d'écrire PRINT suivi du nom de la variable que l'on veut faire apparaître. Si c'est une chaine de caractères ("$") elle s'inscrira à l'écran. Si c'est un nombre codé sur un octet ("|"), un word ("&"), un long ("%") ou un nombre à virgule, il s'inscrira sous sa forme décimale ou en notation ingénieur s'il est trop grand ou trop proche de 0. Si c'est un booléen ("!") il s'inscrira -1 (ce qui correspond à TRUE) ou 0 (ce qui correspond à FALSE).


Si plusieurs PRINT sont demandés les données s'afficheront en passant à la ligne à chaque fois. On peut néanmoins demander qu'elles s'affichent à des endroits particuliers de l'écran grâce à AT :


PRINT AT(8,5);var


Cette instruction inscrira var en commençant au 8ème caractère de la 5eme ligne. En ST Basse on a 25 lignes et 40 caractères par ligne (on dit aussi 40 colonnes), en moyenne 25 lignes, 80 colonnes, etc.


On peut demander d'écrire plusieurs chaines sur la même ligne en faisant suivre PRINT de plusieurs variables ou chaines de caractères séparés par des virgules (pour passer un grand espace entre chacune) ou des points-virgules (pour ne pas passer d'espace).


Comme on l'a déjà vu précédemment, il n'est pas nécessaire de déclarer les variables pour les utiliser en GFA, par contre il est préférable de les effacer avec CLR une fois qu'on a fini de s'en servir. On ecrira donc :


CLR var


ou encore, pour en effacer plusieurs à la fois :


CLR var,a&,compteur%,mem&,test!,ch$


(évidemment les noms de variables utilisés ici ne sont que des exemples)


Il est particulièrement important d'effacer les chaînes de caractères car ce sont le type de variable qui prennent en général le plus de mémoire.


On peut également définir des "tableaux" qui permettent facilement de gérer un grand nombre de variables. Imaginons qu'on ait besoin de stocker 6 scores pour les hiscores d'un jeu, on pourrait utiliser score1&, score2&,... mais cela ne serait pas pratique à gérer, on utiliserait donc :


DIM score&(6)


On définit un tableau qui contiendra 6 valeurs. Les 6 variables ainsi définies seront : score&(0), score&(1),..., score&(6). Cela permet notamment de faciliter certaines opérations, essayons de les afficher :


FOR i|=0 to 5
  PRINT AT(1,i|+1);score&(i|)
NEXT i|


Ici on affiche chaque score en passant à la ligne à chaque fois. i| sert de paramètre dans le AT (on ajoute +1 car la ligne 0 n'existe pas) et pour la séléction de la variable à afficher. Cet exemple montre bien l'intérêt des boucles FOR NEXT et celui des tableaux.


On peut également définir des tableaux à 2 ou 3 dimensions qui dans certains cas peuvent s'averer utiles :


DIM a%(10,5)


Ici on a tableau à 2 dimensions avec 10*5, donc en tout 50 variables :


a%(0,0),...,a%(0,4)
a%(1,0),...,a%(1,4)
:
:
a%(9,0),...,a%(9,4)


On peut bien sur créer des tableaux avec n'importe quel types de variable, par contre la manière de les effacer de la mémoire est differente : on utilise ERASE par exemple :


ERASE score&()


Il est indispensable d'effacer les tableaux car si l'on veut dimensionner un tableau du même nom (avec une taille différente ou non) alors qu'il en existe déjà un le programme plantera.


- e - Opérations sur la RAM et chargement de fichiers.


Au début d'un programme on peut réserver de la mémoire pour les variables.


RESERVE taille%


La taille dépendra du type de programme : si vous ne comptez utiliser que quelques variables il est inutile de réserver beaucoup, par contre, si votre programme utilisera de nombreuses variables et de longues chaines de caractères, il vaut mieux réserver plus. Prennez garde quand même à ne pas stocker toutes les variables et chaines de caractères tel quel car cela vous obligera à augmenter le RESERVE exagérément et cela vous posera peut être problème car cette partie de la RAM n'est plus utilisable même si vous effacez une grande partie de vos variables en cours de programme. Vous serez donc géné par une zone presque vide mais qu'il vous sera impossible d'allouer pour un autre usage.


Dans des programmes évolués il arrive frequemment qu'on ait besoin de créer un "buffer" contenant certaines informations. Un buffer est une zone de mémoire qu'on alloue pour stocker des informations (résultats de précalculs, graphismes, etc). La syntaxe est la suivante.


buf%=MALLOC(taille%)


taille% est la taille du bloc de RAM que vous voulez allouer. Après l'exécution de cette instruction, buf% contiendra l'adresse de début de la zone mémoire allouée ou 0 si une erreur s'est produite. Il veut mieux toujours vérifier que l'allocation a bien fonctionnée car si on ecrit à l'adresse 0... Ça plante ! Il y a 2 principales causes d'erreurs lorsque l'on fait un MALLOC : soit vous demandez d'allouer plus de RAM qu'il n'en reste libre (ou il n'y a plus de buffer de la taille indiquée en un seul bloc : la RAM est fragmentée) ou bien il arrive que vous ayez demandé des MALLOC en trop grand nombre : en effet le nombre de zones mémoire allouées est limité donc il vaut mieux allouer quelques grands buffers que des dizaines de petits.


Lors de la création d'un programme on a souvent besoin de datas (musique, graphismes, etc.). Pour les stocker on a deux solutions en GFA : soit les charger en INLINE soit les charger avec des BLOAD. Les deux méthodes peuvent avoir leurs avantages mais voyons d'abord comment les utiliser.


INLINE truc%,taille


truc% sera l'adresse à laquelle sera situé en RAM le fichier en question et à la place de taille vous devez inscrire la taille du fichier. Lorsque vous tapez cette ligne en GFA il faut ensuite revenir dessus et appuyer sur la touche Help puis cliquer sur Load et sélectionner le fichier que vous voulez charger en INLINE. Par la suite le fichier sera inclu dans le source GFA et sauvegardé en même temps que lui en un seul fichier.


ATENTION : Si vous modifiez les zones de mémoires ou sont chargés les fichiers des inlines (ici à partir de l'adresse truc%) ils ne sont pas restaurés en retournant à l'éditeur donc si vous sauvegardez par la suite, la modification sera enregistrée aussi.


On peut aussi utiliser l'instruction BLOAD :


BLOAD "c:\fichier.ext",buf%


La syntaxe est donc le chemin et le nom du fichier entre guillemets puis l'adresse de la zone en RAM dans la quelle il faut le charger. Il est très important de faire attention que la zone dans laquelle vous chargez le fichier soit assez grande car sinon une partie de la RAM non allouée sera écrasée ce qui provoque le plus souvent un plantage. Donc pour charger un fichier on peut utiliser un MALLOC et un BLOAD :


buf%=MALLOC(taille)
BLOAD "c:\fichier.ext",buf%


Comme je l'ai dit les methodes MALLOC+BLOAD et les INLINES ont toutes les deux leurs avantages et leurs inconvénients pour utiliser un fichier de data.


Le principal avantage de l'INLINE est que le fichier est inséré dans le source et lorsqu'on le compile, l'exécutable obtenu ne devra pas charger de fichiers sans arrêt. Son principal inconvénient est que la taille des fichiers chargés ne doit pas dépasser 32768 octets, ce qui n'est parfois pas suffisant. De plus la mémoire utilisée pour l'INLINE ne peut pas être restituée au système en cours de programme donc même si un INLINE nous a déjà servi et qu'on n'a plus besoin de son contennu il est impossible d'allouer à nouveau cette partie de la RAM.


Le MALLOC+BLOAD est tout de même plus avantageux, même s'il il oblige à charger le fichier à partir d'un lecteur (disquette, disque dur, etc.).


On peut aussi modifier les informations stockées en RAM grâce aux BYTE, WORD et LONG :


BYTE{adr%}=48


Ici on place la valeur 48 dans l'octet dont l'adresse est adr%. On peut faire la même chose avec sur des mots ou des longs en utilisant respectivement WORD et LONG. L'adresse doit être entre accolades et pas entre parenthèses. Si vous essayez de stocker une valeur qui ne peut être codée sur la taille choisie (par exemple 15740 sur un octet) votre programme ne fonctionnera pas. Enfin sur ST les adresses de WORD doivent être des multiples de 2 et celles de LONG des muliples de 4 alors si ces conditions ne sont pas remplies vous aurez le droit à 2 jolies bombes... ;)


Il est aussi possible d'extraire des informations de la RAM grace à ces instructions et de les stocker dans des variables :


a&=WORD{adr%}.


Enfin il existe une instruction permettant de copier une partie de la RAM d'un endroit à un autre.


BMOVE source%,destination%,taille


source% est l'adresse à partir de laquelle on va copier, destination% est l'adresse de début de la zone où on va copier, et taille est la taille que l'on va copier. Ici encore attention de ne pas copier à un endroit qui n'a pas été alloué avant. Cette instruction est particulièrement utile dès que l'on fait des programmes evolués.


- f - Le calcul.


Il existe de nombreuses instructions en GFA pour effectuer des calculs sur les variables et leur utilisation est assez intuitive mais je vais tout de même vous en donner un rapide aperçu.


Pour effectuer des addition, soustractions, multiplications et divisions on utilise les signes usuels ("+" pour l'addition, "-" pour la multiplication, "*" pour la multiplication, "/" pour la division et "^" pour la puissance ainsi que des parenthèse pour indiquer l'ordre de priorité des operation lorsqu'elle n'est pas évidente)


v=10+3*(148/2)


Si on travaille exclusivement sur des entiers il est préférable d'utiliser ADD (pour l'addition), SUB (pour la soustracion), MUL (pour la multiplication) et DIV (bon je crois que vous avez compris ;). La syntaxe pour le calcul précédent devient alors :


v& = MUL(ADD(10,3),(148 DIV 2))


SUB fonctionne avec la même syntaxe que ADD, la seule instruction avec une syntaxe particulière est DIV.


Pour modifier une variable déjà existante on peut faire :


v=v+5


(ou utiliser toutes les autres opérations décrites ci-dessus)


Il existe aussi une autre syntaxe pour modifier une variable mais qui ne permet de faire qu'une seule operation :


ADD a&,4
SUB a&,4


Ici on ajoute ou soustrait 4 à a& (on aurait pu mettre une variable à la place du 4).


Enfin INC et DEC permettent respectivement d'ajouter ou de retrancher 1 à la variable :


INC a&(ou DEC a&)


Le seul intérêt des instructions ADD, SUB, MUL, DIV, INC et DEC est qu'elles sont plus rapides que les opérations classiques mais la restriction est qu'elles traitent uniquement des nombres entiers.


Les fonctions usuelles existent également en GFA :


SIN(a)  (où a est un angle en radian)
SINQ(a) (où a est un angle en degre)
COS(a)
COSQ(a)
TAN(a)
TANQ(a)
LOG(a)
etc.


Je crois que nous avons vu suffisamment d'instruction pour pouvoir commencer à coder. Nous en verrons d'autres au fur et à mesure des programmes.



II - Les premiers programmes.


Tous les programmes décris ici sont présents dans une archive. Ils ne sont pas présents ici car ils encombreraient le TOXIC pour rien, il valait mieux les mettre sous une forme exécutable. De plus ils sont suffisamment annotés pour que vous puissiez les comprendre sans difficultés.


FIRST.GFA : un exemple qui vous montre comment utiliser quelques unes des instructions décrites dans l'initiation et qui en contient aussi quelques unes en plus (avec les explications évidemment).


CALC.GFA : un exemple de boucle FOR NEXT et les différentes méthodes d'incrémentation avec un comparatif de rapidité des instructions.


On passe ensuite à des choses plus sérieuses : AFF_ECR1.GFA affiche une image PI1.


AFF_ECR2.GFA affiche une image en faisant un scrolling vertical.


Dans ces deux derniers programmes vous pouvez facilement changer l'image affichée en chargeant une autre image PI1 dans l'inline (l'inline ne fait que 32034 octets mais les 32 octets en plus ne seront tout simplement pas chargé et n'ecraseront rien. Ici, ces derniers octets ne servent pas donc pourquoi les charger ?


Voci quelques explication au sujet des deux derniers exemples :



III - La mémoire vidéo et les fonction systèmes associées.


La structure de la mémoire vidéo sur ATARI est sous forme de plans mais je n'expliquerai pas comment cela fonctionne cette fois ci car cela ne sert pas dans les exemples.


En ST basse (320*200, 16 couleurs) un écran prend 32000 octets (il faut 4 bits pour coder les valeur de la couleurs de chaque pixel (car 4 bits permettent de coder des valeurs de 0 à 15) et il y a 320*200=64000 pixels donc en tout 64000*4=256000 bits soit 32000 octets). Une ligne fait 320 pixels donc représente en mémoire 320*4=1280 bits soit 160 octets. Ces informations nous serviront sans arrêt et notamment dans les deux fichiers d'exemple d'affichage d'image.


L'écran est caractérisé par sa résolution et son nombre de couleurs. Le ST est prévu pour avoir un double écran, c'est à dire qu'en plus de la partie de la RAM correspondant à l'ecran affiché (l'écran physique), il existe une autre partie de la RAM qui contient l'écran logique. L'intérêt est que pendant que la machine modifie l'écran logique (qu'elle "dessine" si on peut dire) cela ne se voit pas et on n'affiche que lorsqu'elle a terminé. De cette manière, on ne voit pas des choses se dessiner "petit à petit" à l'ecran, mais elle apparaissent d'un seul coup. Même lorsqu'il est très rapide, le redessin se manifeste parfois par un scintillement désagréable alors cette technique dite du "swapping d'ecran" est indispensable.


Une fonction système permet de choisir les adresses des écrans physique et logique (ainsi que la résolution) : XBIOS 5. En GFA la syntaxe est la suivante :


~XBIOS(5,L:adr_log%,L:adr_phys%,W:res&)


Le premier chiffre indique que c'est un XBIOS 5, les "L:" indiquent que la variable qui suit doit être transmise sous forme d'un LONG (et sous forme d'un WORD pour les "W:"). Le premier paramètre est l'adresse de début de la partie de RAM que l'on veut definir comme nouvel écran logique, le second est identique mais pour l'écran physique et le troisième permet de modifier la réso- lution (0 pour ST BASSE, 1 pour ST MOYENNE, etc.). Le tilde ("~") avant la fonction indique que la valeur renvoyée par la machine ne sera pas conservée : dans la plupart des cas c'est inutile pour un XBIOS 5 car la machine se contente de renvoyer -1 en cas d'echec et 0 sinon (il me semble). Si un paramètre est mis à -1 cela signifie que celui-ci ne sera pas modifié.


Il y a une chose importante à savoir sur ST : les adresses des écrans doivent être des multiples de 256 donc il faut transformer l'adresse losqu'on fait un MALLOC car on n'obtient pas forcément un multiple de 256. La méthode pour modifier l'adresse est expliquée dans les exemples.


Il est important qu'un programme laisse le système comme il était avant l'exécution lorsqu'on le quitte donc il faut pourvoir restaurer les anciennes adresses écran, la résolution et la palette par exemple.


Il est facile de restaurer les adresses ecran : en effet il existe deux fonctions XBIOS (XBIOS(2) et XBIOS(3)) qui renvoient ces adresses donc il faut, en début de programme, les stocker dans des variables et les restaurer en fin de programme avec un XBIOS 5. On procède ainsi:


old_phys%=XBIOS(2)    (XBIOS(2) renvoie l'adresse de l'ecran phys)
old_log%=XBIOS(3)     (XBIOS(3) celle de l'ecran logique).
.
... Ici on a le corps du pogramme...
.
~XBIOS(5,L:old_log%,L:old_phys%,W:-1)


Pour la résolution on utilise la fonction XBIOS(4) mais le problème est que d'autres résolutions ont été ajoutées sur le Falcon et le TT, donc sur ces machines cette fonction ne suffit pas pour restaurer la résolution. Nous verrons comment faire sur ces machines une autre fois, contentons-nous de restaurer la résolution sur ST.


rez&=XBIOS(4)
.
... Ici on a le corps du pogramme...
.
~XIOBS(5,L:-1,L:-1,W:rez&)


Evidemment on peut restaurer les écrans et la résolution en un seul XBIOS 5.


La palette est plus compliquée à restaurer car il faut aller directement chercher en RAM (en fait il existe une fonction XBIOS mais je ne l'apprécie pas car elle foire une fois sur 2 s'il y a un petit truc qui ne lui plaît pas). En ST basse, la palette est située à l'adresse $FF8240 mais il faut passer en mode superviseur pour y accéder (voyez les exemples pour savoir comment passer du mode utilisateur au mode superviseur et reciproquement). Une fois qu'on est en mode superviseur, il suffit de copier les 32 octets (une palette 16 couleurs ST fait 32 octets) de la palette dans un buffer alloué au préalable puis en fin de programme de passer en superviseur à nouveau et copier le contenu du buffer en $FF8240 (on remet ce qu'on avait vu en début de programme en fait).


A part pour restaurer cette palette, la fonction XBIOS 6 est plus appropriée (sauf si vous avez vraiment besoin de vitesse). Lorsqu'au cours d'un programme vous voulez changer la palette, utilisez la syntaxe suivante :


~XBIOS(6,L:pal%)


pal% est l'adresse ou se trouvent les 32 octets de la palette que vous voulez installer.


Enfin comme on l'a dit tout à l'heure, il faut redessiner sur l'écran logique puis l'afficher (en fait l'écran physique devient logique et vice-versa) mais pour pouvoir l'afficher il faut attendre que le spot soit revenu en haut à gauche de l'écran (les phosphores de l'ecran sont frappés par un faisceau d'électrons qui parcourt chaque ligne puis passe à la suivante, l'endroit où il se trouve en un instant donné est le spot). C'est ce qu'on appelle la VBL. Si l'on n'attend pas la VBL, le nouvel écran s'affichera seulement en partie : pendant une VBL, la partie supérieure de l'écran affichera l'ancien écran et la partie inférieure le nouveau... Donc il faut attendre la VBL. La fonction VSYNC permet de le faire en GFA mais, vu son efficacité, il vaut mieux utiliser l'adresse mémoire du compteur VBL ($466). Il faut être en mode superviseur pour utiliser cette adresse. Pour voir si on passe la VBL, il faut avoir sa valeur et attendre qu'elle change, lorsqu'elle a changé, c'est qu'on est passé à la VBL suivante. La syntaxe est la suivante :


l%=LONG{&H466}
REPEAT
UNTIL l%<>LONG{&H466}


En fait on stocke une valeur dans l% (ou une autres variable) puis on boucle jusqu'à ce que le compteur soit différent de la valeur stockée.


Pour faire un swapping d'écran on utilise donc la méthode suivante :


DO       (debut de la boucle)
  .
  ... on redessine l'ecran comme on veut ...
  .
  l%=LONG{&H466}
  REPEAT
  UNTIL l%<>LONG{&H466}
  SWAP ecran1%,ecran2%
  ~XBIOS(5,L:ecran1%,L:ecran2%,W:-1)
LOOP


A chaque image on attend la VBL, on fait un SWAP (on échange entre elles les valeurs des adresses des deux ecrans) puis on fait un XBIOS 5 pour que le changement soit pris en compte.



Voila, c'est tout pour ce numéro. J'espère que j'ai été assez clair mais si ce n'est pas le cas n'hesitez pas à me contacter par mail. Vous pouvez aussi m'envoyer vos programmes pour me demander mon avis ou éventuellement pourquoi ils ne marchent pas...


Certains d'entre vous auront peut-être un peu de mal à comprendre pourquoi l'exemple 2 d'affichage fait un scrolling vertical mais ce n'est qu'une question de logique, si vraiment vous ne comprennez pas, dites-le-moi et j'expliquerai plus clairement comment ça marche.


A la prochaine fois pour un nouveau volet de cette initiation dans le prochain Toxic Mag, en attendant codez bien !



Exyl / SECTOR ONE --- sectorone.atari.org --- gdecooman@nordnet.fr




Edito Rubriques habituelles Planè Atari Internet Techniques et programmation Interviews Jeux Vidéo Humour Musique Chroniques, etc. Divers