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.4ou
IF v&=-16ou
IF a%=76348ou
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 !