Sponsors

FacebookTwitterGoogle Bookmarks

Voilà, nous avons maintenant tout ce qu’il nous faut pour réaliser un analyseur syntaxique. Le mois prochain, nous verrons comment compiler ce programme Yacc sur Amiga et nous continuerons notre apprentissage de ce langage en améliorant notre exemple. En attendant, pour ceux qui connaîtraient déjà ce langage, et également pour vous permettre de vous le procurer pour le mois prochain, je vous signale que Yacc version Amiga est disponible dans la collection Fred Fish sur la disquette numéro 419. Je me permets de vous souhaiter une bonne et heureuse année 1992, avec TANT, bien entendu. A bientôt. Par Herr Doktor von GlutenStimmellmDorf *tj Votre pantagruélique serviteur revient faire des siennes dans les pages de votre cher ANT, suite à une absence prolongée pour cause de programmation intense d’utilitaires déments. GREFFEUR 00000000, 00000001 et 00000002, puis trois mots longs donnant leur longueur respective, et ainsi de suite. ÜSiii Vous avez compris ? Je continue. Pour parvenir à nos fins, il suffit de connaître tous les identificateurs existants (ou plutôt ceux utilisés dans un fichier exécutable) et de savoir comment chaque hunk est structuré. Et puisque l’on parle d’utilitaires, celui que je vous propose aujourd’hui, n’est pas dément, mais presque. Il s’agit d’un programme capable de greffer des programmes sur des programmes. Autrement dit, vous disposez d’un excellent programme, mais vous regrettez que lors de son lancement, telle ou telle fonction ne soit pas accomplie : il vous suffit de programmer vous-même ladite fonction et de la greffer sur le programme fautif grâce à l’utilitaire du mois. RESTEZ SAGES ! Je vois venir certains petits malins, qui ont immédiatement compris qu’ils leur sera possible de se servir de cet utilitaire (au demeurant génial) afin de répandre d’odieux virus coquilles tout partout. Oui cela est possible, mais je maudis les coupables jusqu’à la 52ième incarnation. Voilà ! COMMENT CELA SE PEUSSE ? Puisque l’Amiga est multitâche, n’en déplaise aux bidouilleurs de démos (que je maudis cette fois jusqu’à la 104ième incarnation), il n’est pas possible de savoir à l’avance à quelle adresse mémoire sera chargé tel ou tel programme. Pour résoudre cette difficulté, les développeurs de l’Amiga ont employé la solution suivante : un programme exécutable n’est pas sauvé tel quel sur disquette. Ainsi, toutes les addresses de saut ne sont pas réelles mais comptées à partir de l’adresse 0. Lors du chargement du programme en mémoire, AmigaDOS se charge d’ajouter à chaque adresse répertoriée, l’adresse absolue de chargement et le programme se trouve relogé et donc en état de fonctionner.

Click image to download PDF

AMIGA NEWS TECH numero 30 (02-1992)

Document sans nom G A
r. 1
r
EN VENTE UNIQUEMENT PAR ABONNEMENT
ExecBase : le C ANSI (II) 2 AMOS : Trucs en vrac (III) 4 Daisy : Multi Rainbows 5 Arexx : Le déverminage (II) 6 Pascal : Présentations (II) 8 GFA : Le trackdisk.device 10 Algos : Les piles 12 Transactor : Test-Disks 15 Hardware : La voix de Paula (II) 16
ToolBox : YACC 18 Utilitaire : Le Greffeur 20 Demos : Les ellipses 24 Demos 2 : Etoiles en 3D 26 Routinettes : Exist() 29 Requester 31
Denis Jarril
François Lionet
Daisy Lionet
François Gueugnon
Olivier Garrigues
Pierre Philippe Launay
Bruno Bailluet et Lucien Turski
François Braün
Philippe Rivaillon
Herr Doktor Von Machin
Frédéric Mazué
Thomas Landspurg
Jérôme Etienne
Tonton François Lionet et Max
Ok & Cancel
Cela fait un bon bout de temps maintenant que nous n’avons pas effectué de sondage pour savoir si l’Amiga NewsTech, dans sa formule actuelle, répondait à vos attentes.
Rassurez-vous, nous allons y remédier dès maintenant. Non pas sous la forme d’un véritable sondage, comme dans le numéro 20, mais plutôt en vous incitant à nous donner encore et toujours votre avis : n’hésitez pas à nous écrire ce que vous pensez de telle ou telle rubrique, ce que vous aimeriez trouver en plus dans l’ANT ou, au contraire, ce qui sous semble inutile.
Ceci est plus particulièrement important pour les rubriques génériques (langages, Algos, Hardware...). Certains d’entre vous nous ont déjà fait part de leur manque d’intérêt pour Pascal et GFA et de leur soutien pour Algos (cf. Requester). Si cela devait s’avérer être la tendance générale, nous supprimerions sans hésiter les deux premières.
La balle est donc dans votre camp. Vous pouvez nous écrire à l’adresse habituelle, ou par Minitel, 3615 ANT, BAL Redac : elle est lue au minimum une fois par semaine.
A vous de jouer.
Stéphane Schreiber
OURS
Amiga NewsTech (ANT pour tes intimes) est une publication mensuelle et vendue uniquement par abonnement, de Commodore Revue SARI., 5. Rue de la Fidélité, 75010 Pan-:. Téléphone de la rédaction : (h 42.46.02.90. ISSN 1157-8912. Commission Paritaire1 : et puis quoi encore ?
Bon. J’vous explique comment ça fonctionne : tout en haut de l’échelle, y'a un mec oui allonge les thunes pour fabriquer le canard. C’est Jean-Yves Primas, le Directeur fie la Publication. On rappelle "jcan-Yves* en temps normal. "Monsieur le Directeur" quand on veut lui demander une augmentation, et "cnlbirc" quand il la refuse.
En dessous, y’a le type qui décide du contenu du magazine. C’est Stéphane Schreiber,
le Rédacteur en Chef, un type super. J'ai beau chercher, j’trouve aucune saloperie à
dire sur lui.
Encore en dessous, y’a les pigistes : ce sont eux qui écrivent tous ces bô zarticles qui vous enchantent chaque mois un peu plus. Par ordre alphabétique, ça nous donne : Pascal Amiable, Bruno Bailluet, François Braiin, Jérôme Etienne, Olivier Garrigues,
François Lionel, Denis Jarrii, Thomas Landspurg. Pierre Philippe Launay, François
Jouet, Max. Frédéric Mazué. Philippe Rivaillon, Stéphane Schreiber et Lucien Turski- Marcon.
Une fois que les textes sont rendus. Stéphane Schreiber les relit, les censure et corrige tes fautes d’orthographe, avec l’aide et le soutien inestimables de Christine Robert, qui a pourtant déjà suffisamment de boulot comme ça avec Amiga Revue.
Après, il faut faire la mise en page. C’est encore Stéphane Schreiber qui s’y colle, on se demande vraiment où il trouve le temps. Quand je vous disais qu’il était super, je: n’exagérais pas.
Ensuite, le flashage : à partir des fichiers PostScript générés par PPM, ColorWax (5, rue de la Fidélité. 75010 Paris. Tel : (J)
47. 70.S7.S7) produit des films pour l’imprimeur, lequel se trouve être INC) Mont rouge (9, rue Auber, 92120 Mont rouge. Tel : il) 46.56.27.69).
Parallèlement à la fabrication, faut gérer tout ça. Par exemple, c’est MCM ANT (16, Quai Jean- Baptiste Clément, 94140 Alfortville. Tel : il)
45. 78.92.10) qui s’occupe des abonnements, de la duplication des disquettes et de l'expédition. Par autre exemple, la voix sensuelle au téléphone,
;c'est généralement celle de Valérie, cl le bruit de Vaspirateur dans le couloir, c’est toujours celui de Madame Maria.
Voilà, vous savez tout, vous pouvez retourner vous coucher. Atchao bonsoir.
?
LU
_l
...... . i-
• m i 11
Nous Pavons vu, toutes les | fonctions et macros que la norme 1 ANSI impose sont regroupées 1 dans une bibliothèque standard et 1 déclarées dans des fichiers 1 d’en-tête, standards eux aussi. 1
La liste de ces fichiers d’en-tête a été donnée le mois
dernier, nous n’y reviendrons donc pas. Rappelons
simplement que tout compilateur C se réclamant de la
norme ANSI se doit de fournir ces fichiers, ainsi que la bibliothèque elle-même, bien entendu. La suite de cette série sur le C ANSI est donc claire : nous allons détailler chacun de ces fichiers, en expliquant les fonctions, macros et variables qu’il contient.
PETITE PRECISION
Tous les objets que nous allons voir dans ce qui suit, travaillent avec certains types de données, notamment les inî ou les float. D’autres travaillent avec des structures, composées d’objets de plusieurs types.
Le programmeur n’a normalement pas à connaître la taille de ces objets. En d’autres termes, il n’a pas à se soucier de savoir si, sur telle ou telle machine, dans tel ou tel environnement, un int est codé sur deux, quatre ou même huit octets.
Si une telle connaissance est souvent primordiale pour accéder aux ressources d’un système donné (par exemple pour l’Amiga, les structures définies par Intuition ou la graphies.library), il suffit de savoir que, dans le cas de la bibliothèque standard, telle ou telle fonction renvoie un int, un char, ou un pointeur quelconque. Si l’on a absolument besoin de connaître la taille d’un int (par exemple, pour une allocation mémoire), l’opérateur sizeofo doit être utilisé.
Enfin, certains types définis particuliers seront rencontrés au cours de notre exploration de la bibliothèque. Nous les détaillerons à ce moment là.
LES ENTREES-SORTIES : STDIO.H
Nous attaquons par le plus gros morceau... Les fonctions, macros et types d’entrées-sorties (Input-Output, ou plus simplement 10), représentent presque un tiers de la bibliothèque.
On y trouve notamment la définition des flux : il ne s’agit ni plus ni moins que d’un flot de données associées à un périphérique quelconque (disque bien sûr, mais également clavier, écran, imprimante...). Les flux peuvent être gérés soit en mode texte, soit en mode binaire. Dans le premier mode, les données sont organisées en lignes, conclues par le caractère de fin de ligne, ' n'. Ce caractère peut être, de manière interne, représenté soit par CR (ASCII 13), soit par LF (ASCII 10), soit par une combinaison des deux. C’est le système qui gère cela. Un flux binaire est constitué d’une suite d’octets non traités par le système, même s’il devait contenir un ou plusieurs caractères de fin de ligne. C’est le programmeur qui décide d’ouvrir un flux en mode texte ou au contraire en mode binaire.
Au début de l’exécution du programme, le code de démarrage ouvre toujours les trois flux standards stdin (entrée), stdout (sortie) et stderr (sortie d’erreur, peut être différent de stdout).
Les fichiers
L’ouverture d’un fichier renvoie un objet de type FILE, contenant toutes les informations nécessaires au contrôle du flux. Le type LILE est défini ainsi :
struct _iobuf
struct _iobuf *_next; * flux suivant *
unsigned char *_ptr; * pointe le buffer 10 *
int _rcnt;
*
compteur d'octets en lecture *
int _wcnt;
*
compteur d'octets en écriture *
unsigned char *_base;
*
adresse de base du buffer 10 *
int _size;
*
taille du buffer 10 *
int _flags;
*
flags de contrôle du flux *
int _file;
*
descripteur de fichier *
unsigned char _cbuf;
*
buffer pour 1 char. *
typedef struct _iobuf FILE;
Les fonctions suivantes concernent le traitement des fichiers.
FILE *fopen const char *filename const char *mode)
fopen() tente d’ouvrir le fichier décrit par filename, dans le mode défini par mode, et retourne un flux en cas de succès, et NULL sinon, mode peut être :
"r" fichier texte en lecture
"w" fichier texte en écriture. Si le fichier existait déjà, il est écrasé.
"a" fichier texte en écriture. Si le fichier existait déjà, les nouvelles données lui seront ajoutées.
"r+" fichier texte en lecture écriture.
"w+" fichier texte en lecture écriture. Si le fichier existait déjà, il est écrasé.
"a+" fichier texte en lecture écriture. Si le fichier existait déjà, les nouvelles données lui seront ajoutées.
Si l’on ajoute un b à l’un quelconque de ces modes (par exemple, "rb", ou "w+b"), le flux est ouvert en mode binaire au lieu du mode texte. La longueur des noms de fichiers est limitée à FILENAME_MAX caractères et FOPEN_MAX fichiers peuvent être ouverts simultanément.
Note : FILENAME_MAX n’est pas défini en Lattice SAS C 5.10, et FOPEN_MAX est défini sous le nom de _NFILES.
FILE *freopen(const char ?filename, const char *mode,
FILE *stream)
freopen() ouvre le fichier décrit par filename dans le mode défini par mode, et lui associe le flux stream. On utilise en général freopen() pour changer les fichiers associés à stdin, stdout et stderr.
Int fflush(FILE *stream)
fflush() provoque l’écriture anticipée des données encore présentes dans la mémoire tampon du flux stream, qui doit avoir été ouvert en écriture. Son effet est indéfini pour un flux ouvert en lecture. Elle retourne EOF pour une erreur d’écriture, 0 si tout va bien.
Int fclose(FlLE *stream)
fclose() force l’écriture des données encore présentes dans la mémoire tampon du flux stream, libère tout tampon alloué automatiquement et ferme le flux, qui devient inutilisable. Elle retourne EOF en cas d’erreur, 0 sinon.
Int remove(cont char *filename)
remove() détruit le fichier décrit par filename. Elle retourne une valeur différente de 0 en cas d’échec (fichier absent, erreur disque...).
int rename(const char *oldname, const char *newname)
renameO change le nom du fichier oldname en newname. Elle retourne une valeur différente de 0 en cas d’échec.
FILE *tmpfile (voici)
tmpfile() crée un fichier temportaire dans le mode "wb+", qui sera automatiquement détruit lors de sa fermeture ou lors de la fin normale du programme.
Note : cette fonction n'est pas définie dans le stdio.h du Lattice SAS C 5.10.
int setvbuf(FILE *stream, char *buf, int mode, size_t size)
setvbuf() permet de contrôler la mise en mémoire tampon du flux stream. Il faut l’appeler avant la première lecture ou écriture sur le flux. Le mode _IOFBF provoque un tamponnage complet, _IOLBF un tamponnage par ligne de texte et _IONBF ne provoque pas de tamponnage. Si buf est différent de NULL, il sera utilisé comme tampon, sinon il sera alloué, size détermine la taille du tampon. Setvbuf() retourne une valeur différente de 0 en cas d’erreur.
Note : le type sizejt est défini comme typedef unsigned int size_t. Size_t fread(void *ptr, size_t size, size_t nobj, FILE *stream)
fread() lit sur le flux stream, nobj objets de taille size et les place dans le tampon ptr. Elle retourne le nombre d’objets effectivement lus, qui peut être inférieur au nombre demandé.
Size_t fwrite(void *ptr, size_t size, size_t nobj, FILE *stream)
fwrite() écrit dans le flux stream, nobj objets de taille size depuis le tampon ptr. Elle retourne le nombre d’objets effectivement écrits, qui peut être inférieur au nombre demandé.
Int fseek(FILE *stream, long offset, int origin)
fseek() positionne le pointeur de fichier pour le flux stream. Pour un fichier binaire, la nouvelle position est fixée à offset octets du début si origin vaut SEEK_SET, de la fin si origin vaut SEEK_END, ou de la position courante si origin vaut SEEKjCUR. Pour un fichier texte, offset doit valoir 0 (ou une valeur retournée par ftellQ) et origin doit valoir SEEK_SET. Fseek() retourne une valeur non nulle en cas d’erreur.
Int ftell(FILE *stream)
ftell() retourne la position courante dans le fichier pour le flux stream, ou -IL en cas d’erreur.
Void rewind(FILE *stream)
rewind() replace le pointeur de fichier au début du flux stream et ré-initialise toute erreur détectée, rewind(fp) est l’équivalent de
fseek(fp, 0, SEEK_SET); clearerr(fp); int fgetpos(FILE *stream, fpos_t *ptr)
fgetpos() place dans *ptr la position courante dans stream. Elle retourne une valeur non nulle en cas d'erreur.
Note : le type fpOS_t est défini Comme typedef unsigned long fpos_t.
Int fsetpos(FILE *stream, const fpos_t *ptr)
fsetpos() déplace le pointeur de fichier de stream à la position mémorisée dans *ptr par fgetpos(). Fsetpos() retourne une valeur non nulle en cas d’erreur.
Void clearerr(FILE *stream)
clearerr() remet à 0 les indicateurs de fin de fichier et d’erreur du flux stream.
Int feof(FILE *stream)
feof() retourne une valeur non nulle si l’indicateur de fin de fichier (EOF) est positionné pour le flux stream.
A suivre...
Par Denis Jarril t
TRUCS EN VRAC (III)
Chers amos, bienvenue dans la rubrique Amis... Une page, deux petits programmes très intéressants, une Daisy toujours aussi imbue d’elle-même : la routine, quoi.
(E) nregistrement. Faites défiler une page sur le minitel, puis pre une touche sur J’Amiga et sauvez votre fichier sur disque. V pourrez relire la page sauvée avec l’option (L)ecture.
Vous constaterez en regardant le listing, que ce programme 1 vraiment rien de sorcier : il ne fait que bêtement poker les caratèi reçus dans une banque, et la sauver sous forme binaire.
Le minitel c’est tout simple, et c’est rigolo. Rendez-vous le mo: prochain, où nous allons émuler comme des fous !
Lancer des programmes externes en AMOS... Encore ? Vous exclamez-vous. Oui, je vous propose encore une procédure pour faire démarrer un programme externe à partir d’AMOS. Celle-ci marche à tous les coups : même lorsque le Workbench est fermé, même lorsque qu'aucun CLI n’est ouvert, même lorsque l’Amiga est éteint... Non, quand même, il ne faut pas exagérer !
Mini Enregistreur Lecteur de page minitel
F. Lionet ANT 1992
Ouvre un écran "sérieux" Screen Open 0,640,200,4,Hires Colour 1,0
7 Banque servant de buffer Reserve As Work 10,100000
Avant de lancer le programme, la procédure ouvre le device nul (NIL:) en entrée sortie, grâce à l’instruction AmigaDOS "Open". Ce device est une véritable poubelle faisant disparaître toutes les données qui lui sont envoyées. Son fonctionnement en tant qu’entrée est très simple : il ne renvoie rien du tout.
' Ouvre l'interface série. Les paramétrés par ' par défaut sont ceux du minitel Serial Open 0,1
Boucle de prise d'ordre
Do
Input "(E)nregistrement, (L)ecture, (F)in ?";A$ A$ =Upper$ (A$ )
If A$ ="F" : Exit : End If If A$ ="E" : ENREGISTREMENT : End If If A$ ="L" : LECTURE : End If Loop
En communiquant l’adresse de ce device lors du lancement du programme, on évite les plantages : AmigaDOS a quelque chose où envoyer ses données, le programme est lancé correctement.
Serial Close Edit
Une fois la commande partie, on peut fermer le canal.
Procédure LECTURE
' Lecture du fichier .Nitel
AMOS Exec: lancer un programme externe en AMOS
ca marche à tous les coups!
F. Lionet A.N.T.
N$ =Fsel$ ("*.Nitel","","Entrez le nom du fichier","") If N$ >""
' Trouve la longueur Open In 1,N$ : LF=Lof(l) : Close 1 ' Charge dans la banque Bload N$ ,Start(10)
' Envoie le fichier Serial Out 0,Start(10),LF Print "Envoi en cours !"
End If
On lance l'AMOS à partir de l'AMOS. Haha. EXEC["AMOS1.3"]
Print Param
Procédure EXEC[C$ ]
' Ouvre un canal d'entree et de sortie NUL A$ ="NIL:"+Chr$ (0)
Dreg(l)=Varptr(A$ ) : Dreg(2)=1004 HAND=Doscall(-30)
End Proc
Procédure ENREGISTREMENT
' Enregistrement d'un fichier
' Si ouverture réussie If HAND
' On lance la commande C$ =C$ +Chr$ (0)
Dreg(l)=Varptr(C$ ) : Dreg(2)=HAND
F=Doscall(-222)
Print "Enregistrement en cours!"
ABANK=0
' Boucle d'enregistrement Repeat
' Demande le caractère, sans attendre A=Serial Get(0)
' Si un caratere est disponible If A>0
7 Si l'on est pas au bout de la banque If ABANK Length(10)
7 On le sauve
Poke Start(10)+ABANK,A : Inc ABANK Print At(0,);"Caractères reçus:";ABANK; Else
7 Au bout de la banque!
Bell End If End If
7 Jusqu7à ce que l'on appuie sur une touche A$ =Inkey$
Until A$ >""
Print
Dreg(3)=HAND
7 On peut fermer le canal maintenant Dreg(1)=HAND A=Doscall(-36)
End If
¦1 si réussi
' Retourne End Proc[F]
UN ENREGISTREUR MINITEL
Grande nouvelle : Daisy et moi-même allons nous réconcilier et travailler ensemble sur une série d’articles portant sur la réalisation d’un émulateur minitel, en AMOS bien sûr. Il va falloir que je fasse des concessions à cet animal débile.
7 Quelque chose à sauver?
If ABANK-Start(10)
7 Demande le nom
N$ =Fsel$ ("*.Nitel","Entrez le nom du fichier","Taille:"+Str$ (ABANK))
' Sauve un simple fichier binaire.
If N$ >""
Bsave N$ ,Start(10) To Start(10)+ABANK End If End If
Nous allons vous proposer une complète émulation, avec possibilité d’enregistrement de pages, macros, et téléchargement. Avec des graphiques et des couleurs en plus. Ce qui nous permettra de voir comment il fonctionne, ce minitel.
Pour vous donner un avant-goût de ces articles, voici un tout petit programme enregistrant et restituant des pages minitel. Il fonctionne avec l’extension série de l’AMOS 1.3.
End Proc
Connectez-vous sur votre service favori (au hasard, 3615 ANT ou 3615 ComRev), faites fonctionner le programme et choisissez
Par François Lionet
C= Amiga NewsTech mm Numéro 30 - FEV 92
I
ARCS MULTICOLORES
?
Doke ACOP+2,C2 : Add ACOP,4
Add ACOP,4
Il est possible, vous le savez, d’afficher en AMOS des dégradés de couleurs, les Rainbows, ou arcs-en-ciel. Le problème est que l’on ne peut pas avoir plus d’une couleur par ligne. Pourquoi cette limitation ? Je vais vous le dire : c’est l’autre idiot qui en a fait la programmation. Alors aujourd’hui, je vais essayer de le rattraper un peu, avec le magnifique programme qui suit.
Multi_Rainbow recrée une liste Copper à partir de la liste originale de l’AMOS, en mode Copper Off. Il recopie la liste en insérant les nouvelles couleurs.
Vous pouvez ainsi fabriquer votre affichage, en chargeant ou en ouvrant des écrans. Lorsque tout est prêt, vous lancez la fabrication de la liste.
Le nombre de Rainbows est défini au début du programme, il n’est limité que par la taille de la liste Copper (11 Ko) : on peut avoir environ 6 arcs-en-ciel en même temps, et plus en augmentant la taille mémoire affectée au Copper à l’aide du programme de configuration.
Les couleurs se trouvent dans des datas à la fin du programme. Chaque groupe de données contient les valeurs de couleurs pour chaque ligne. Un label de la forme Rnx, ou "x" est le numéro du Rainbow, se trouve au début de chaque liste.
Vous pouvez taper les datas à la main, bon courage. Le mieux est d’utiliser le programme domaine public Rainbow-Warrior : il sauve des fichiers de datas immédiatement utilisables dans ce programme. Comme je suis bonne patte, j’ai envoyé Rainbow-Warrior à Stéphane. Il sera mis très rapidement en téléchargement sur le 3615 ANT (le programme, pas Stéphane).
Allez, une lèchouille et à bientôt. Sluuuuurp !
Daisy au micro, pour encore une fois relever cette rubrique. Comment ça, nous réconcilier ? Pas question ! Ou alors, double ration de Royal-Canin. Ha ! Je ne vais pas me laisser faire...
L=0
Repeat
C=Leek(Cop Logic+L)
Loke Start(10)+L,C
L=L+4
Until C=$ FFFFFFFE
' A pus copper
Copper Off : Wait Vbl
7 Addresses ou poker
ACOP=Cop Logic : ACH=Start(10)

' Met les définitions des sprites
For N=0 To 15
Cop Move 0,0
Loke ACOP,Leek(ACH)
Add ACOP,4 : Add ACH, 4
Next

' Boucle de création de la liste Y=3 0 : YRAIN=0 Do
7 Attente du début de ligne Cop Wait 7,Y : ACOP=ACOP+4
7 Adresse dans la liste
CY=Deek(ACH) 2 5 6 +YBASE
If Y=CY
' C'est le début d'un écran
ACH=ACH+4
Cl=Deek(ACH)
If(Cl and 1)=0
' On copie betement la liste originale Do
Cl=Deek(ACH) : C2=Deek(ACH+2)
Exit If(Cl and 1) >0
' Sauf si on change l'une des couleurs ' du rainbow For C=0 To NRAIN-1 If C1=C(C)*2+$ 180 C2=CC(C(C),YRAIN)
End If Next
Add ACH,4 Cop Move 0,0 Doke ACOP,Cl Loop End If Else
' Ce n'est pas un écran, on met ' simplement les couleurs For C=0 To NRAIN-1
Cop Move $ 180+C(C)*2,CC(C(C),YRAIN) Next End If
P
' Un nouveau miracle signé Daisy:
' Multi-Rainbows. Plein d'arc-en-ciel
' sur un meme écran!
' (c) Daisy Lionet ANT 1992 _______
' Nombre de Rainbow à traiter NRAIN=3
' Dimensionne les tableaux de couleurs Dim C(NRAIN),CC(NRAIN,279)
7 Affectation des Rainbows aux couleurs C(0)=0 : C(1)=1 : C(2)=2
7 On lis les datas produits par Rainbow-Warrior For N=0 To NRAIN-1
Restore "Rn"+Mid$ (Str$ (N),2)
For C=0 To 279
Read CC(C(N),C)
Next
Next
7 Ici, on fait son propre affichage. Autant d1écrans 7 que l7on veut.
Screen Open 0,320,256,16,Lowres Cls 1
Paper 1 : Pen 5 : For X=0 To 205 : Print "Daisy!"; : Next Cls 2,64,70 To 320-64,170 Screen Open 1,640,32,4,Hires Screen Display 1,,70,,
Cls 0 : Paper 0 : Pen 3 : For X=0 To 25 : Print "Ecran HIRES!";
Next

' Penser à enlever la souris!
Hide On
____
7 On s'assure que les écrans sont affiches.
Wait Vbl : View : Wait Vbl : View : Wait Vbl
Inc Y : Inc YRAIN
If Y=256 : YBASE=256 : End If
Exit If Y>311 Exit If YRAIN>279 Exit If ACOP-Cop Logic>10*1024 Loop
7 La fin de la liste Cop Wait 255,255
7 On affiche!
Cop Swap : Wait Vbl : Wait Key Copper On : Wait Vbl
f
7 Et voila Edit
' Définition des Rainbows
7 Intégrer les liste de Rainbow-Warrior, précédées de 7 labels Rnx, ou X est le numéro du Rainbow...
RN0 :
Data $ 100,$ 210,$ 320,$ 430,$ 540,$ 650,$ 761,$ 872
... Pleins de Datas dont je vous fait grâce
7 Deuxième rainbow RN1:
Data $ BEE,$ CC,$ CC,$ CC,$ BEE,$ CC,$ CC,$ CC . . . Meme remarque !
7 Troisième rainbow RN2 :
Data $ AF,$ AF,$ AF,$ AF,$ AF,$ AF,$ AF,$ AF ... Commandez la disquette ANT!
Par Daisy Lionet *b
C= Amiga NewsTech
5
Numéro 30 - FEV 92
Nous avons vu comment on pouvait suivre, et éventuellement mémoriser, le déroulement d’un programme afin d’en détecter les défauts divers. Il reste encore quelques possibilités intéressantes, que nous allons analyser.
Nous avions précédemment laissé de côté le traçage interactif, non pour le négliger, bien au contraire : il permet de modifier momentanément le programme en cours Liste des interruptions reconnues (valeur ou instruction). L’intérêt en est considérable et la mise en oeuvre très simple. Il y a deux façons d’entrer dans le mode interactif :
• à partir de l’intérieur du programme, en ajoutant un ? à F instruction trace en complément avec n’importe quelle option. Exemple trace ? Ou encore trace ? Intermediates. Attention à la place relative de l’option et du ?. La commande du mode interactif agit en mode alterné (toggle) : TRACE ? Lance le mode interactif, puis TRACE ? à nouveau l’arrête, et ainsi de suite.
• à partir de l’extérieur, en lançant le programme TS (fourni avec Arexx). Dans ce cas, tous les programmes Arexx en cours entrent en mode interactif. TS (Trace Start) a son opposé TE (Trace End), qui permet de sortir du mode interactif.
Le mode interactif arrête le programme après chaque instruction Arexx sauf pour call, do, else, if, then et otherwise qui pourraient poser des problèmes à l’interpréteur. Pour ces instructions, l’arrêt est effectué à la fin de l’exécution totale de l’instruction. En outre l’interpréteur ne s’arrêtera pas après une instruction qui aurait déclenché une erreur, son traitement étant prioritaire.
Lorsque l’interpréteur est arrêté par le mode interactif, quel qu’en ait été le mode de lancement, le symbole +++ apparaît dans la fenêtre de traçage. A partir de cet instant, trois actions peuvent être réalisées :
• une ligne vide, sanctionnée par un simple appui sur la touche retour, permet au programme de continuer son exécution jusqu’au point d’arrêt suivant.
• un signe = suivi de l’appui sur la touche retour permet à l’instruction précédant l’arrêt d’être exécutée une nouvelle fois.
• l’entrée d’une instruction valide quelconque, qui est exécutée tout de suite après l’appui sur la touche retour. Comme il a été vu précédemment, les instructions de type DO ou SELECT doivent être complétées avant l’appui sur la touche retour pour ne pas plonger l’interpréteur dans une profonde angoisse qui l’inciterait à faire méditer le GURU.
TRAITEMENT DES ERREURS
Heureusement, Arexx procure un traitement d’erreurs spécial pendant qu’il fonctionne en mode interactif, qui fournit un rapport pour chaque erreur mais ne provoque pas l’arrêt du programme (ce qui en serait normalement la conséquence). Cette particularité ne s’applique qu’aux instructions qui ont été entrées en mode interactif (ouf!). Les erreurs dues aux défauts du programme sont traitées par la méthode normale.
En plus de ce traitement spécial et en mode interactif uniquement, l’interpréteur désactive les indicateurs internes d’interruption. C’est indispensable pour éviter le transfert vers le traitement d’erreur éventuellement intégré au programme (voir chapitre suivant). Une exception cependant pour les instructions de type signal qui continuent non seulement à autoriser le transfert mais qui font abandonner ce qui pouvait rester comme entrée interactive en suspens.
RMINAui. V»
INTERRUPTIONS
Arexx entretient un système interne d’interruption qui peut être utilisé pour détecter et piéger certaines conditions d’erreur. Lorsqu’une interruption est activée (mise en état de fonctionner) et que les conditions de déclenchement correspondantes se présentent, le programme est dévié vers un sous-programme dont le nom est spécifique de l’interruption, pour peu qu’on ait pris soin d’en écrire un.
Les conditions d’interruption peuvent être synchrones, telles que les erreurs de syntaxe, ou asynchrones, telles que les demandes au clavier, CONTROL-C par exemple. Il faut noter que les interruptions extérieures sont complètement séparées des interruptions "hardware" gérées par Exec.
Certaines des interruptions ont des comportements différents selon qu’elles sont activées ou non, d’autres n’ont qu'un type de
comportement efficace seulement après activation.
• BREAK_C : réagit à CONTROL-C au clavier. Si cette interruption n’est pas activée, le programme se termine immédiatement et le message Execution halted’ est présenté à l’opérateur, agrémenté d’un ’error code n °2 ’.
• BREAK_D : réagit à CONTROL-D au clavier. Si cette cette
interruption n’est pas activée, elle est ignorée.
• BREAK_E : réagit à CONTROL-E au clavier. Si cette cette
interruption n’est pas activée, elle est ignorée.
• BREAK_E : réagit à CONTROL-F au clavier. Si cette cette
interruption n’est pas activée, elle est ignorée.
• ERROR : cette interruption est générée par les commandes de l’hôte (ici, le système) qui retournent un code non zéro traduisant une impossibilité quelconque (voir ci-après).
• H ALT : si elle est activée, cette interruption piège une requête d’arrêt extérieure sinon, le programme se termine immédiatement et le message Execution haltecV est présenté à l’opérateur, agrémenté d’un
’error code n °2
• IOERR : si elle est activée, cette interruption piège les erreurs détectées par le système d’Entrées S orties.
• NOVALUE : si elle est activée, cette interruption piège les variables non initialisées. On rappelle à cette occasion qu’il n’est pas besoin de déclarer une variable : DO a=l to 14 ne demande pas que a soit déclaré d’une façon quelconque auparavant mais a=a+3 n’est légal que si a a déjà une valeur, bien évidemment.
• SYNTAX : cette interruption piège les erreurs de syntaxe ou d’exécution. Pas toutes cependant, en particulier les erreurs arrivant avant l’exécution réelle du programme ou celles détectées par l’interface externe de Arexx ne sont pas traitées.
Qu’on se rassure (mais est-ce bien nécessaire ?) Lorsque qu’une interruption est prise en compte, tous les contrôles en cours actifs et aussi l’interruption qui vient de se déclencher sont désactivés. Ceci pour prévenir d’éventuelles boucles inextricables. Cependant, cette disposion n’affecte que l’environnement courant, et pas l’environnement général.
UTILISATION DES INTERRUPTIONS
Nous venons de voir que les interruptions ont un comportement différent suivant qu’elles sont activées ou non. Le comportement non activé ayant été décrit, voyons comment les utiliser en mode activé.
Il faut réaliser deux opérations : activer l’interruption souhaitée et écrire le sous-programme de traitement.
La forme générale d’activation est signal on label, où label est le nom d’une des interruptions décrites plus haut. Par exemple, signal on novalue s’inquiétera dès que le programme tentera d’utiliser une variable non initialisée. A l’arrivée de la condition, le déroulement du
?
P
programme sera dévié vers un sous-programme de traitement de cette interruption. L’instruction SIGNAL ON est semblable au GOTO prohibé de tous les programmes respectables. Dans le cas présent, et ailleurs aussi, parce que c’est une nécessité, il faut réaliser un transfert impératif, d’où cette forme. Le sous-programme intéressé se nommera donc label: (dans le cas de l’exemple, novalue:). A partir de l’entrée dans le sous-programme on revient à de la programmation classique. On peut ainsi donner une valeur arbitraire à la variable non initialisée et retourner à la suite du programme, ou encore solliciter l’opérateur avec un texte lui demandant une valeur de son choix et utiliser la capacité du parse (PARSE PULL ou tout simplement la forme abrégée PULL) et retourner au programme pour continuer. Le choix du retour et à la continuation du programme dépend de la volonté et du besoin du programmeur.
Enfin, pour désactiver signal on label on utilisera son contraire, signal off label, ce qui permet une jolie série de combinaisons ON OFF de toutes les formes possibles d’interruption, sélective en forme, en activation, et donc en position et en répétition.
Signalons (sans jeu de mots...) une forme un peu plus complexe de SIGNAL : signal value expression, qui agit comme un GOTO càlculé c’est-à-dire qu’il y a transfert vers un sous-programme de traitement d’erreur qui dépend de l’évaluation de l’expression. Le type de l’interruption est défini par une variable spécifique dont nous allons nous occuper maintenant.
VARIABLES 'SIGL11 et "RC"
Ces deux variables ont des noms réservés, au même titre que les mots spécifiques du langage : do, parse et tous les autres. Elles sont constamment mises à jour pendant l’exécution du programme et peuvent être consultées à tout moment.
La variable sigl (pour SIGnaL) contient le numéro de la ligne qui précède celle qui a déclenché le transfert vers le traitement d’erreur. Attention, si vous avez utilisé la faculté légale d’écrire plusieurs instructions sur la même ligne, séparées par un point-virgule, vous risquez de ne pas retrouver aisément celle qui est la cause de l’interruption.
La variable rc (pour Return Code), contient, elle, le résultat des interruptions ERROR et SYNTAX en cas d’erreur et une autre valeur en cas de bon fonctionnement. Pour ERROR, le résultat usuel est le niveau de sévérité de l’erreur et il peut venir de l’extérieur. Pour SYNTAX, le résultat est toujours un code erreur Arexx. Par exemple, si l’erreur est la numéro 3 et la sévérité 20, on en déduit qu’il y a une quantité de mémoire insuffisante en se référant à la liste des codes d’erreurs fournie avec la documentation d’ARexx.
La variable RC n’est pas toujours d’un maniement aussi limpide, surtout lorsqu’elle est affectée par un élément extérieur à Arexx. En principe, tout appel à une fonction extérieure, de l’OS par exemple, devrait donner une valeur au RC et, si cela est réputé vrai avec F OS 2 de l’AMIGA 3000 (ARexx fait partie de son environnement) cela ne l’est sûrement pas pour les OS 1.1 à 1.4 des "petits" Amigas. L’emploi du RC doit donc être soumis à l’essai pour avoir une idée de la vérité. Enfin, avant de donner des exemples d’emploi, il faut avoir en tête que le RC est affecté par le retour de l’appel à une fonction et qu’il a de ce fait, une valeur très rapidement caduque.
EXEMPLES D’UTILISATION DE RC
La valeur de retour de RC est dite booléenne, c’est-à-dire qu’elle vaut 1 en cas de mauvaise exécution et 0 sinon. C’est en tout cas le fonctionnement qu’elle devrait avoir d’après les livres... Pour se faire une idée, on pourra avec profit utiliser le petit programme suivant qui permet d’effectuer divers essais. Ce programme permet l’entrée au clavier de diverses instructions Arexx et l’on pourra regarder avec intérêt ce qui se passe en cas de défaut dans l’orthographe, la syntaxe ou encore dans le système. Il est prévu trois cas de RC : 0, 1, autre.
1 **
2 PRAGMA('W','NULL7)
3 SIGNAL ON BREAK_F
4 DO FOREVER
5 PULL instruction
6 INTERPRET instruction
7 SAY"
8 SAY'Le résultat RC est:'
9 SELECT
10 WHEN rc=0 THEN SAY'Ok...RC=' rc
11 WHEN rc=l THEN SAY'pas Ok...RC=' rc
12 OTHERWISE SAY'Autre valeur...RC=' rc
13 END
14 END
15
16 BREAK_F:
17 PRAGMA('w' ,'WorkBench')
18 EXIT
En ligne 1, on interdit à l’OS d’envoyer des Requesters selon son habitude en cas de mécontentement (on remet tout en place en ligne
17) . En ligne 3, on active l’arrêt sur CONTROL-F. De la ligne 4 à la
ligne 14, c’est le programme en boucle infinie à cause de la ligne 4. En 5, attente de l’instruction à entrer au clavier et en ligne 6, exécution de ladite instruction avec interpret. De 9 à 11, on analyse le résultat dans RC. En ligne 16, on trouve le label de branchement de CONTROL-F, qui nous sort de la boucle infinie et où l’on fait le ménage.
INSTALLATION
Nous allons maintenant présenter une organisation qui permet d’écrire les lignes de code, de voir le résultat à l’exécution et de surveiller le déroulement dans la fenêtre spécialisée.
On doit trouver les programmes suivants dans le répertoire C: courant: HI, TCO, TCC, TE, TS, en plus deRX, RXC et RXSET.
On ouvre une console CONMAN par exemple (recommandé).
On ouvre l’éditeur de texte de son choix.
On ouvre enfin la fenêtre de messages d’erreur par TCO.
Les fenêtres sont réparties de la façon suivante : l’éditeur occupe les 2 3 supérieurs de l’écran, la console le 1 3 inférieur et la fenêtre de surveillance est en haut à droite.
Tout ceci doit se trouver dans la Startup-Sequence, comme il est indiqué dans la documentation. De plus, pour éviter de cliquer à chaque fois dans les fenêtres, il est agréable d’installer Dmouse de Matt Dillon. Enfin, on peut fermer la fenêtre de début qui ne sert plus à rien.
On peut alors taper un programme dans la fenêtre de l’éditeur, le sauvegarder, le faire s’exécuter dans la console... A ce propos, il est recommandé de lancer l’exécution par RUN RX plutôt que par RX tout court, car en cas de boucle infinie imprévue dans le programme, l’envoi de HI permet de tout arrêter... sans devoir re-booter ! On se méfiera toutefois des programmes qui utilisent PARSE PULL pour demander quelque chose à l’opérateur, car ils lisent d’abord la console et on obtiendrait une erreur car ils liront le retour de RUN. Pour ceux- là, si l’on ne veut pas changer de méthode d’appel, on placera une condition d’arrêt interne et on ne les lancera pas avec RUN. On lancera de la même façon le traçage interactif et toutes les options que nous avons analysées.
Par Français Gueugnon
PRESENTATIONS (II)
wëm M
Jp
SM-
la
sur
>> début du bloc programme) >> début du bloc
L’article d’aujourd’hui se décompose en deux parties première sera essentiellement théorique et portera l’architecture des blocs. La seconde continuera l'exploration du compilateur PCQ et sera beaucoup plus pratique (NDLR : rappelions simplement que le compilateur PCQ peut être trouvé sur la disquette ANT numéro 22 et que vous pouvez toujours la commander en utilisant le bon page 32. Fin de la pub).
LES BLOCS
Le Pascal est un langage structuré : tout ce qui le compose obéit à une forme syntaxique. L’architecture principale d’un programme est basée sur le bloc (nous en avons vu un le mois dernier). Il existe différentes sortes de blocs qui disposent chacune de propriétés propres.
La forme générale d’un bloc est un en-tête, suivi d’une zone de déclarations locales et d’un segment d’instructions. L’en-tête se divise lui-même en un mot introductif qui indique le type du bloc (à ne pas confondre avec les types de variables), suivi d’un identificateur qui sera le nom donné au bloc pour de futures utilisations et, éventuellement, d’autres informations comme les passages de paramètres (ordre et sens). Il se termine par un point-virgule.
La déclaration locale est la zone qui permet de définir de nouveaux objets (par l’intermédiaire d’autre blocs) connus exclusivement du bloc courant. Le segment d’instructions, lui, est toujours le couple begin...
END.
Tous les blocs ont nécessairement un en-tête. Le premier mot d’un bloc (et de son en-tête) est celui qui définira son type.
• program indique, je l’ai déjà expliqué, le bloc programme. Il ne doit apparaître qu’une seule fois dans tout le listing. Dans le fichier qui le contient, il doit être le plus grand bloc, c’est-à-dire que tous les autres blocs doivent se trouver dans sa zone de déclarations locales.
• external définit un bloc externe qui permet de ne pas associer des procédures et des fonctions à un programme sans créer d’erreur de compilation. Le fichier objet obtenu est nommé une "bibliothèque", qu’il ne faut pas confondre avec les ".library" d’Exec qui, elles, doivent être ouvertes pour pouvoir être utilisées. Celles construites à l’aide d’un bloc externe sont utilisées avec le linker évitant la compilation répétée d’une partie d’un programme. Je ne cite ici ce bloc que pour des raisons d’exhaustivité, nous l’étudierons en détails dans un autre article.
Les points communs de ces deux blocs, sont qu’il sont uniques dans un fichier, qu’ils ne peuvent se contenir mutuellement, et qu’ils contiennent tous les autres blocs dans leur zone de déclarations locales (j’insite car ceci est très important). A l’opposé d’un bloc programme, le bloc externe ne possède pas de nom, et n’a pas de segment d’instructions.
• var (que nous avons déjà vu), permet de déclarer un bloc (ou plusieurs blocs) de déclarations de variables.
• const déclare des constantes (qui ne changent pas de valeur tout le long du programme) mais aussi, des variables qui possèdent une valeur par défaut. Pourquoi avoir mis ce type de variables dans "constantes" ? Afin de respecter une décision de Borland dans le Turbo Pascal (il en sont à la version 6) tenant de la syntaxe de la déclaration. Nous y reviendrons, alors patience.
• type permet de construire des types nouveaux avec les quelques types de bases et des constructeurs de types tels que array, record
De nouveau du Pascal ! Ceux qui ont cru que le simple article du mois dernier expliquait tout du Pascal se trompaient lourdement
?
PROGRAM Vue_Des_Blocs;
VAR déclaration)
cnoms de variable>:ctype défini>;
autres des déclarations>
>> fin du bloc déclaration )
PROCEDURE nom>( déclarations des paramètres> );
>> début du bloc procédural )
TYPE >> début du bloc déclaration interne )
nom de type> = ctypes construits>
>> fin du bloc déclaration interne )
VAR >> début d'un autre bloc déclaration interne )
autres des déclarations>
>> fin du bloc déclaration interne )
BEGIN début du segment d'instructions du bloc procédural ) instructions>
END; >> fin du bloc procédural (et du segment) }
>> début d'un nouveau bloc procédural )
FUNCTION nom> ( déclarations des paramêtres> ): type>;
TYPE >> début du bloc déclaration interne )
nom de type> = types construits>
>> fin du bloc déclaration interne )
VAR >> début d'un autre bloc déclaration interne }
autres des déclarations>
>> fin du bloc déclaration interne }
BEGIN début du segment d'instructions du bloc procédural ) instructions>
END; >> fin du bloc procédural (et du segment)}
BEGIN >> début du segment d'instructions du bloc principal }
instructions>
END. >> fin du segement et bloc programme }
Remarquez que dans cet exemple, j’utilise le terme interne pour bien différencier la zone de déclarations locales du programme et les zones de déclarations locales de chaque bloc procédural.
LA PORTEE
Tout objet qui a un nom possède une portée qui lui est associée, c’est- à-dire qu’à un certain moment, on ne pourra plus l’utiliser. Pour qu’un objet (variable, type, procédure ou fonction) soit défini dans la zone où l’on veut le référencer (en général, dans un segment d’instruction), il faut non seulement que sa déclaration soit située plus haut dans le texte, mais aussi dans un bloc qui contient (par emboîtements successifs) le bloc où l’on travaille. Un petit exemple, commenté au delà du raisonnable, ne fera pas de mal.
PROGRAM Portée; en-tête du bloc programme et début du fichier } ce programme n'a pas de signification réelle mais il est là pour aider à la compréhension du problème de la portée }
var i,j:integer;
i et j sont des entiers }
PROCEDURE EcritUnEntier();
déclaration d'une procédure. Le bloc procédural)
a comme nom ECRITUNENTIER }
(see later, pour ne pas se répéter). Le point commun de ces trois blocs est qu’ils ne sont composés que d’un en-tête ; ils n’ont ni zone de déclarations locales, ni segment d’instructions.
• procédure et function sont les déclarateurs de blocs procéduraux. Le premier déclare une procédure et l’autre une fonction (avouez que vous ne l’aviez pas trouvé). Une fonction est simplement une procédure qui renvoie un objet. Il faut donc préciser son type. Nous verrons comment il faut s’y prendre. Ce type de blocs possède les trois zones : un entête (pour définir son nom, l’ordre des arguments et leur type pour l’appel), une zone de déclarations locales, et un segment d’instructions.
En fait, nous avons six en-têtes différents pour cinq types de blocs. Ce qu’il faut bien comprendre est que cette structuration permet de créer des blocs dans d’autres blocs, comme des boîtes qui contiendraient d’autres boîtes plus petites. Ainsi, certaines de ces boîtes nous seraient cachées ou accessibles suivant l'endroit où l'on travaille (effet de localité).
Un exemple, toujours très formel (ce n'est pas la partie théorique pour rien), pour ceux qui seraient perdu :
?
Var n,l:integer; on ne peut les appeler i et j car i et j sont >
accessible : c'est un effet de bord... >
nous avons un bloc de déclaration interne au > bloc EcritUnEntier >
procédure InterneAEcrit(); ce bloc connait i,j,n et 1 comme variables)
il est connu dans tout EcritUnEntier mais pas dans }
le bloc programme }
Begin
n:=n*12; segment d'instructions de InterneAEcrit }
n prend douze fois son ancienne valeur )
1:=1+1;
Je fais une référence aux variables n et 1, gui ne sont ni des paramètre ni des variables locale de InterneAEcrit.
Je me sers d'un effet de bord qui ne faudra pas utiliser sauf pour des constantes )
end; fin du segment d'instructions de InterneAEcrit }
begin segment d'instructions de EcritUnEntier }
1:=j; même remarque que ci-dessus: un effet de bord qui
mène a }
n:=i*j; l'erreur assez facilement }
InterneAEcrit; appel de la procédure définie plus haut ) writeln ('vive le Pascal, n un chiffre ',n);
je suis sûre que vous ne vous y attendiez pas ! >
ligne écriture du nombre n et repassage a la ligne.)
End; fin du segment d'instructions de EcritUnEntier )
BEGIN début du segment d'instructions du bloc progamme )
i : =1; j:=i+2;
seules ces deux variables sont accécible tout les autres ne le sont plus. Seule, aussi la procédure ECRIUNENTIER est accéssible; 1'InterneAEcrit ne l'est plus du fait que l'on ne travail plus dans le bloc ECRIUNENTIER (comme si on avait fermer le couvercle de la boîte. }
EcritUnEntier appel de la procédure ECRIUNENTIER )
END. fin du bloc programme et fin du fichier )
NOUVELLES PROCEDURES
Bon, finie la théorie, passons à la pratique. Je vous avais laissé un petit devoir (tiens, plus personne ne me regarde...). La spécification était : "saisie de l’âge d’une personne, ainsi que l’année de sa naissance sur l’entrée standard pour calculer approximativement l’année actuelle, le tout très familièrement". Le but de ce programme était de vous présenter mister readln, dont voici la fiche signalétique :
READLN()
Syntaxe ! READLN (varl, var2, ..., varN)
type : procédure multiparamètres
spécification : identique à celle de READ, mais avec un retour-
chariot après saisie de toutes les variables paramètres.
Pour comprendre READLN, il faut connaître read.
READ()
syntaxe ; READ (varl, var2, ..., varN)
type : procédure multiparamètres
paramètres : variables du type integer, real, string ou array of char.
Spécification : lit sur l’entrée standard (généralement le clavier) ou, si varl est du type file, dans le fichier désigné par varl, une séquence de caractères, puis fait la conversion suivant le type de la variable en cours de saisie. L’ordre de saisie est de la gauche vers la droite, et il faut séparer les entrées par des espaces. Enfin, un écho à l’écran est fait si la saisie passe par le clavier.
Vous pouvez observer que d’autres types de variables peuvent être utilisés avec READQ ou READLN(). Ceux-ci seront expliqués plus tard.
Par Olivier Garrigues ïfc|
L’implémentation fournie par PCQ pour READ() n’est pas tout-à-fait standard. READ() demande toujours que tous ses arguments soient séparés par un blanc, d’où certains effets indésirables, que montre l’exemple suivant.
Program Read_Essai;
saisie sur l'entrée standard de deux nombres entiers )
sur la même ligne )
var i,j:integer ;
BEGIN
writeln('Veuillez taper deux nombres entier :'); write('premier nombre :'); read(i);
write('second nombre :'); read(j);
writeln('merci. Les nombres sont :',i,',',j);
END.
Pour pouvoir saisir les deux nombres, il faut les taper l’un derrière l’autre, séparés par un blanc. Par contre, l’implémentation de READLN() est respectée, comme le montre ce petit programme :
program Readln_Essai;
saisie sur l'entrée standard de deux nombres entiers }
sur deux lignes )
var i,j:integer ;
BEGIN
writeln('Veuillez taper deux nombres entier :'); write('premier nombre :'); readln( i ) ;
write('second nombre :'); readln(j);
writeln('merci. Les nombres sont :',i,',',j);
END.
Alors, READQ ou READLNQ ? Pour ne pas avoir de supprises, je vous conseille READLN() quand vous utilisez PCQ. Pour ceux qui utilisent d’autres compilateurs Pascal, testez-les avec le programme ci- dessus et faites-vous une idée.
Pour mémoire, voici la fiche signalétique des procédures write o et
WRITELN() :
WRITEO :
syntaxe : write (expi, exp2, ..., expN)
type : procédure multiparamètres
paramètre : expX peut être une expression du type array of
char, string, integer, booléen, réel mais aussi des constantes string (comme par exemple "Hello boy") ou array of char (comme ’hello boy’) spécification : envoie sur la sortie standard (ou si varl est du type "file of" dans le fichier désigné par varl), les valeurs sous forme de chaînes de caractères, des paramètres restants.
WRITELN() :
syntaxe : writeln (expi, exp2, ..., expN)
type : procédure multiparamètres
paramètres : identiques à ceux de WRITE()
spécification : identique à WRITE(), mais avec ajout d’un retour- chariot après tous les paramètres.
Vous vous demandez peut-être à quoi correspond une variable de type file of... En Pascal, on désigne un fichier par un nombre qui lui est propre (au même moment, un nombre ne désigne qu’un et un seul fichier). L’entrée et la sortie standard sont aussi considérés comme des fichiers par Pascal (et par AmigaDOS). Une variable de type "file of" peut recevoir ce nombre. Les procédures WRITE() et READQ sont des procédures d’entrées-sorties générales qui ne se bornent pas au clavier et aux fenêtres (CLI ou Shell) ; elles permettent des "accès disques" à travers les fichiers que l’on aura préalablement ouverts. Mais ceci est une autre histoire.
Le mois prochain, si tout va bien, nous aborderons, pour la partie théorique, les mystérieux types prédéfinis ainsi que les effets de bord et, dans la partie pratique, l’implémentation des chaînes de caractères (y a du boulot !).
Il existe plusieurs fonctions très agréables pour lire les fichiers, mais elles sont insuffisantes pour une véritable gestion des secteurs. Il faut alors utiliser les structures et les routines propres au système.
TRACKDISK.DEVICE
Vous savez que toutes les routines C ont été ré-écrites en GFA 3.52F, aussi nous ne serons pas vraiment dépaysés. La démarche est tout-à-fait identique, voire un peu plus simple en GFA : le trackdisk.device utilise bien sûr une structure d’entrée-sortie étendue (IOExtTD). Pour plus de détails, je vous conseille de lire le message des thaïlandaises de Max dans TANT 24, ainsi que la Bible pages 357. 543 et 687. Voici ce que cela donne pour notre programme :
structure IOExtTD 0
48
52
structure IOStdReq
0
Structure Message
0
Structure Node
0 ln_succ
Pointeur Noeud suivant
LONG%
4 ln_pred
Pointeur Noeud précédant
LONG%
8 ln_type
Type NT_MSGPORT
BYTE i
9 ln_pri
Priorité dans la liste
BYTE!
10 ln_name
Nom de la structure
LONG%
14
mn_ReplyPort
Pointeur port réponse
LONG%
18
mn_Lenght
Longueur
WORD&
20
io_
_device
Pointeur structure Device
LONG%
24
io.
_unit
Pointeur structure Unité
LONG%
28
io_
_command
Commande
WORD&
30
io.
_flags
Direction de la commande
BYTE |
31
io.
_error
Erreur du device
BYTE |
32
io.
_actual
Nb données transmises
LONG%
36
io.
_lenght
Nb données
LONG%
40
io.
_data
pointeur de données
LONG%
44
io.
_of f set
N° du secteur en octet
LONG%
iotd_count
Nb changements de disk
LONG%
iotd_seclabel
16 octets (copyright)
LONG%
Ainsi, une structure Message est associée à un message ne devant pas dépasser 64 ko. Ce nombre est une limite qui peut avoir ses contraintes.
GFA ET C, MON GENERAL !
Les bibliothèques et les devices ont une logique de base assez semblable, ou tout au moins assez similaire (gasp, j’ai du dire la même chose). Les structures d’unité sont également assez classiques et nous devons seulement savoir que nous disposons ici des unités DFO: à DF3: et que nous écrirons pour cela 0, 1, 2 ou 3 dans l’IO.
- io_data contiendra l’adresse où débutent les données à lire. Attention, le système s’avère ici particulièrement ségrégationniste : tout d’abord, celles-ci doivent avoir une taille au minimum égale à 512 octets, ce qui élimine Super OctetoPhage. Le pauvre. Et ensuite on ne pourra les trouver qu’en mémoire Chip (MALLOC).
- io_offset doit également toujours être un multiple de 512 octets. Il n’est donc pas possible de se positionner à cheval sur deux secteurs en écrivant par exemple 300.
- ioîd_counl indique le nombre d’insertions et de retraits de disquettes dans le lecteur. Cette valeur augmente de 2 pour un simple échange de disquettes. Or, comme la valeur de référence correspond au dernier réamorçage du système, un nombre impair signalera immanquablement un lecteur vide et un nombre pair, la présence d’une disquette.
Pierre Philippe Launay * Artiodactyl Bloc Editor * 30 AVRIL 1991 *
* * *******************************************************
' * CONCEPTION DU PROGRAMME ' * EXTRAIT DE ' * DATE DE CREATION
- iotd_seclabel doit avoir 16 octets d’un seul tenant et est soit réservé aux lettres du copyright, soit rempli avec des 0. SuperOctetoPhage y écrit d’ailleurs un 0 par lettre de son nom et comme il dit, personne ne pensera à les modifier.
! Lecture des blocs
! En C c' est 1' instruction Main() ! Oui, certe, que diantre ! Les constantes du programme
programme

PROCEDURE programme ouvre.Ecran variable
TOUJOURS DU C, MON GENERAL
Il reste à parler de la structure ReplyPort :
0 structure MsgPort
0
Structure Node
0
ln_succ
Pointeur Noeud suivant
LONG%
4
ln_pred
Pointeur Noeud précédant
LONG%
8
ln_type
Type NT_MSGPORT
BYTE |
9
ln_pri
Priorité dans la liste
BYTE |
10
ln_name
Nom de la structure
LONG%
14
mp_
_f lags
BYTE |
15
mp_
.sigbit
N° du bit signal envoyé
BYTE |
16
mp_
.sigtask
Pointeur où va le signal
LONG%
ET ENFIN DU GFA, MON GENERAL !
Tout cela nous permet de savoir où nous allons et grâce aux remarques, le codage se laissera ainsi déchiffrer très facilement.
On trouve d’abord l’adresse de notre propre tâche par la méthode "longue" : FindTask(O). Il existe en effet la possibilité de remplacer cette ligne par un appel direct plus rapide à la structure Exec à l’offset 276, appelé justement ThisTask, mais attention, ce procédé ne peut fonctionner qu’avec la recherche de notre propre tâche car ce pointeur n’indique que la tâche actuellement traitée et ne peut donc indiquer les autres tâches. Ouf, Super OctetoPhage peut reprendre sa respiration après cette longue tirade. Nous modifions ensuite le pointeur mp_sigtask et nous pouvons alors nous ajouter au système.
ET QUE LE DEVICE SOIT
L’ouverture du dispositif se fait bien entendu avec la fonction OpenDeviceQ. Voici les erreurs possibles :
0 IOERR_
- 1 IOERR.
- 2 IOERR_
- 3 IOERR_
- 4 IOERR_
20 TDERR,
21 TDERR_
22 TDERR.
23 TDERR_
24 TDERR_
25 TDERR_
26 TDERR_
27 TDERR_ 2 8 TDERR_
29 TDERR_
30 TDERR_
31 TDERR,
32 TDERR,
33 TDERR,
34 TDERR,
35 TDERR
NOERROR
.OPENFAIL
ABORTED
NOCMD
JBADLENGTH
NOTSPECIFIED
NOTSECHDR
BADSECPREAMBLE
BASECID
BADHDRSUM
.BADSECSUM
TOOFEWSECS
BASECHDR
WRITEPROT
_DI SKCHANGED
SEEKERROR
NOMEM
.BADUNITNÜM BADDRIVETYP DRIVEINUSE POSTRESET
L' a pas pu
Fin provoquée par l'action de ABORTIO() Commande impossible Longueur impossible Erreur du tracdisk non reconnue Absence d' entête de secteur Mauvais préambule de secteur Mauvais Disk Interrupt dans le secteur Mauvaise somme de contrôle du handler Mauvaise somme de contrôle du secteur Pas assez de secteurs sur la piste Entête de secteur illisible Disquette protégée contre 1' écriture Lecteur de disquette vide Mauvaise position de la tête de lecture Pas assez de mémoire. Flûte alors Lecteur de disquettes non connecté Mauvais lecteur de disquettes Lecteur déjà en cours d' utilisation Pas d'accès autorisé après Reset
Voilà. Nous lirons les données grâce à Io et celui-ci modifiera la structure diskio% avant de se lancer avec ~DoIO(). L’affichage du secteur tient compte de quelques offsets spécifiques, puis nous libérons le système.
Le mois prochain, un vrai plasma... entièrement en GFA !
*******************************************************
r
?
COLOR 6,5
TEXT 10,20," B L O C offset colonne contenu fermeture DISPLAY ON RETURN
880 "
! L' écran redevient visible
PROCEDURE Offset COLOR 5,3 TEXT 10,30,"Type TEXT 10,40,"Somme de contrôle "
COLOR 3,0
TEXT 160,30,"Bloc racine de la disquette " +HEX$ (LONG diskbuft%),8)
TEXT 160,40,HEX$ (LONG ADD(diskbuff%,20)},8) RETURN
! Quelques valeurs
mEmoire‘36=&Hl0002 ! Chip
rEservation ! Espace pour les structures utilisées
tache%=FindTask(0) ! Recherche de notre tâche
LONG readreply%+&HlO}=tache% ! SigTask sur notre tâche
- AddPort(readreply%) ! Ajout de notre structure port messages ouvre.device ! Ouverture du tracdisk.device
io(readreply%,cmd__read&,longueur ,diskbuft%, saut%) io(readreply%,etd_motor&,stop%,diskbuff%,saut%) affichage ! Présentation des données
final ! Salut
RETURN
PROCEDURE ouvre.Écran ! Met une fenêtre
OPENS 1,0,0 640,250,3,&H8000 OPENW 1,0,10,640,240,0,&H1000
TITLEW 1,SPACE$ (62)+"Pierre Philippe, passez une agréable j ournée"
RETURN
PROCEDURE variable ! Les constantes du programme
cmd_read&=2 etd_motor&=9 espace$ =SPACE$ 1)
dispositif $ ="trackdisk.device"+CHR$ (0) dispositif%=V:dispositif$ bloc%=880 1ongueur%=512 saut%=bloc%*512 secteur%=l flag%=0 disque%=0 tÂche%=0 stop%=0 RETURN
r
PROCEDURE rEservation ! Création de mémoire pour les structures __________________________________
' Création des variables nécessaires aux structures GFA
' Réservation de 512 octets de mémoire pour les transmissions _ _____
tampon_diskio%=512 tampon_readreply%=512 tampon_diskbuff%=512
di skio%=MALLOC(t ampon_di skio%,mÉmoire%)
readreplÿ%=MALLOC(tampon_readreply%,mÉmoire%)
diskbuf f%=MALLOC(tampon_diskbuft%,mÉmoire o)
_______________________
port%=ADD(diskio%,80) ! Pointeur sur la structure message port _______________________
RETURN

PROCEDURE ouvre.device ! Mise en action d' un périphérique erreur%=OpenDevice(dispositif ,disque%,diskio%,flag%)
IF erreur% ! Est-il là ?
- RemPort(readreplÿ%) ! Suppression structure
- MFREE(diskio ô,tampon_diskio%) ! Libération io
- MFREE(readreply%,tampon_readreply%)! Libération message
- MFREE(diskbuff%,tampon_diskbuff%) ! Libération device ALERT 0,"Trackdisk Device non ouvert Ierreur : "
+STR$ (erreur ,3),0," Merci ",rÉponse|
SYSTEM ! C' est la plantade
ENDIF RETURN
- RemPort(readrepiy%)
- CloseDevicetdiskio ô)
PROCEDURE -MFREE(diskio%,tampon_diskio9è)
io(ln_replyport%,io_command&,io_length%,io_data%,io_offset%) -MFREE(readreply%,tampon_readreply%)
- ------------------------------------------------------------ -MFREE(diskbuff%,tampon_diskbuff%)
' Création des structures RETURN
' Action r
' ------------------------------------------------------------- PROCEDURE final
LONG ADD(diskio%,14))=ln_replyport% ! Pointeur port réponse clic
INT ADD(diskio%,28)}=io_command& 1 Commande CLOSES 1
LONG ADD(diskio%,36)}=io_length% ! Octets à transférer ' SYSTEM
------------------------------------------------------------- RETURN
IF NOT (io_length%=0) ! Si pas à 0 '
LONG ADD(diskio%,40)>=io_data% ! Pointeur tampon données ' C'EST FINI !!
LONG ADD(diskio%,44)}=io_offset% ! Saut relatif '
ENDIF
~DoIO(diskio%) ! Lecture des secteurs
PROCEDURE colonne ! Les colonnes d' offset
FOR y 1=1 TO 16
y1|=MUL(ADD(y|,5),10)
TEXT 10,yl|,STR$ (MUL(PRED(y|),32),3)
TEXT 570,yl|,STR$ (PRED(MUL(yI,32)),3)
NEXT y| y I =50 RETURN
t
PROCEDURE contenu ! Le résultat
couleurlI=4 couleur2I=6
FOR x&=0 TO PRED(tampon_diskbuf£%) STEP 4
Farfadet long®
ADD pasx&,66 SWAP couleurl|,couleur2|
8 lettres en topaz 8 Alternance de couleurs Si on est en fin de ligne Ligne suivante
Début de la ligne sur 1' écran Fin du test
IF x& MOD 32=0 ADD y|,10 pasx&=40 ENDIF
lecture%=LONG ADD(diskbuff%,x&)}
SELECT x&
CASE 432 TO 462 COLOR 4,7
TEXT pasx&,y|,MID$ (MKL$ (lecture ),1,1)+espace$
TEXT ADD(pasx&,16),y|,MID$ (MKL$ (lecture ),2,1)+espace$ TEXT ADD(pasx&,32),y|,MID$ (MKL$ (lecture%),3,1)+espace$ TEXT ADD(pasx&,48),y|,MID$ (MKL$ (lecture%),4,1)+espace$ DEFAULT ! Les autres cas
COLOR couleurl|,couleur2| ! Changement de couleurs
TEXT pasx&,y|,HEX$ (lecture ,8)
ENDSELECT NEXT xSc
TEXT 150,220,"Fin de la démonstration en cliquetant la souris" RETURN
PROCEDURE clic WHILE MOUSEK=0 WEND REPEAT
UNTIL MOUSEK=0 RETURN
PROCEDURE fermeture
Mickey et Maousse costaud
! A bientôt, my dear ! Suppression du port ! Fermeture du trackdisk ! Libération io ! Libération message ! Libération device
Le domaine gris Clic clic
Fermeture de 1' écran La fin la plus sûre
RETURN
Par Pierre Philippe Launay Ü
PROCEDURE affichage DISPLAY OFF CLS
! Lecture de diskbuff
L
Définition : une pile est une structure de données dans laquelle il n’est possible d’ajouter ou de retirer un élément que par son sommet (méthode dite LIFO : Last In First Out).
ïy.yÆ
PRIMITIVES POUR LES LISTES CHAINEES
Comme vous avez pu le constater, si l’on représente une pile sous forme de tableau, il est nécessaire d’avoir une variable (appelée sommet) indiquant l’indice du dernier élément de la pile. Par contre, si l’on utilise une liste chaînée, cette variable devient inutile. Pourquoi ?
Si vous avez bien compris cette petite définition, alors les piles n’auront plus aucun secret pour vous... Elles sont le plus souvent représentées par un tableau, mais comme nous maîtrisons maintenant les listes chaînées (mais si, souvenez- vous, c’était dans TANT numéro 29. Celui du mois dernier. C’est pourtant pas si vieux ! (NDLR : bof lu sais, en informatique, tout va si vite...)), nous allons étudier les deux cas.
PRIMITIVES POUR LES TABLEAUX
Les deux premières primitives (en pseudo-code) que nous vous proposons, permettent de tester l’état de la pile, à savoir si elle est pleine ou au contraire vide.
Fonction Pile_Pleine (sommet : entier) : Booléen ;
Début
Si sommet = Taille_Pile Alors Pile_Pleine - Vrai ;
Sinon
Pile_Pleine - Faux ;
Fsi ;
Fin ;
Fonction Pile_Vide (sommet : entier) : Booléen ;
Début
Si sommet = Limite_lnferieure_Pile Alors Pile_Vide - Vrai ;
Sinon
PileJVide - Faux ;
Fsi ;
Fin ;
Ces deux fonctions permettent de savoir si nous dépassons les limites du tableau. Taille_Pile est le plus souvent une constante qui détermine le nombre de cases du tableau. Quant à Limite_Inférieure, il s’agit comme son nom l’indique de la borne inférieure du tableau. Ainsi, nous pouvons parfaitement n’utiliser qu’un seul grand tableau que nous partagerons en plusieurs piles.
Procédure Empile (val : Type_Choisi) ;
Début
Si Non(Pile_Pleine(sommet_pile)) Alors sommet pile - sommet pile + 1 ; pile [sommet_pile] - val ;
Sinon
Afficher ("erreur : pile pleine") ;
Fsi ;
Fin ;
Fonction Depile : Type_Choisi ;
Début
Si Non(Pile_Vide(sommet_pile)) Alors Depile - pile[sommet_pile] ; sommet pile - sommet pile - 1 ;
Sinon
Afficher ("erreur : pile vide") ;
Fsi ;
Fin ;
La procédure Empile ajoute l’élément qu’elle a reçu en paramètre après avoir testé l’état de la pile (pleine ?) Et incrémenté le sommet. Le retrait d’un élément de la pile se fait après avoir testé l’état de la pile (vide ?) Et avant de décrémenter le sommet.
Nous aurions pu choisir une autre méthode pour empiler, qui consiste à mettre la valeur dans le tableau avant d’augmenter le sommet. De ce fait, il aurait fallu être cohérent avec soi-même en modifiant en conséquence les autres fonctions, ainsi que les différentes bornes (essayez pour voir, c’est un bon exercice).
Créons un noeud vide que nous appelons DEBUT et qui est le point de départ de notre liste. Puis, à chaque fois qu’un élément devra être empilé, il sera inséré entre DEBUT et d’éventuels autres éléments précédemment empilés. La variable indiquant le sommet de la pile devient inutile puisque nous savons toujours où se trouve le dernier élément empilé. Il en est de même pour dépiler : nous prennons le noeud qui suit DEBUT. Une liste simplement chaînée suffit amplement, car nous
ne la parcourerons pas. Nous n’avons donc pas besoin de conserver la
trace du noeud précédent (intérêt de la liste double).
Empiler :
nouveau.suivant - DEBUT.suivant ;
DEBUT.suivant - nouveau ;
nouveau.info - valeur ;
Pour empiler, il suffit d’insérer le nouveau noeud après DEBUT. Nous devons donc affecter à nouveau.suivant l’adresse du noeud qui suit DEBUT (cette adresse se trouve dans DEBUT.suivant). Ensuite, nous affectons à DEBUT.suivant l’adresse du nouveau noeud (ce dernier devient donc le successeur immédiat du noeud DEBUT). Ces deux étapes doivent se faire dans cet ordre sous peine de perdre le contenu de DEBUT.suivant.
Le test Pïle__Pleine se fait au moment de l’allocation mémoire du nouveau noeud. Si celle-ci échoue, on considère la pile pleine.
Dépiler :
Si Non(Pile_Vide) Alors
noeud_temp - DEBUT.suivant ; valeur - noeud_temp.info ;
DEBUT.suivant - noeud_temp.suivant ;
Sinon
Afficher ("Pile Vide") ;
Fsi ;
Si la pile n’est pas vide, nous repérons l’adresse de l’élément à dépiler (ceci nous est très facile puisqu’il s’agit toujours du successeur de DEBUT). Puis nous sauvegardons sa valeur dans une variable intermédiaire (variable valeur dans notre primitive). Enfin, pour le supprimer de la liste, nous le court-circuitons en faisant pointer DEBUT.suivant sur le successeur du noeud à dépiler (il s’agit de noeud_temp. Suivant).
Pile_Vide :
Si DEBUT.suivant = NIL Alors Pile_Vide - Vrai ;
Sinon
Pile_Vide - Faux ;
Fsi ;
Si DEBUT.suivant est égal à NIL, la liste ne contient aucun élément hormis le premier noeud DEBUT qui ne contient pas d’information pertinente.
PRATIQUE
Pour se familiariser avec la pile, nous vous proposons ce mois-ci un petit programme permettant l’évaluation d’une expression parenthésée.
Cependant, quelques contraintes sont imposées :
- un membre se compose de deux opérandes séparés par un signe
- tout membre doit être mis entre parenthèses
- les opérandes peuvent être des membres
Ainsi, on entrera ((a+b)*c) et non (a+b)*c.
Algorithme Expression_Parenthesee ;
(* version pour les tableaux *)
Constante
limite inférieure pile -0 ; max_operande - 26 ;
max_pile_operande - 50 ;
- 50
Déclaration
opérande
pile_operande
pile_operateur
expression
(*
(*
(*
(*
(*
*)
*)
*)
*)
0
*)
*)
*)
*)
*)
(*
(*
(*
(*
(*
*)
*)
*)
*)
Fin
(* Ici nous sommes contraints d'avoir une paire de fonctions (* pour chaque pile car chaque sommet est géré de manière (* indépendante. On évite ainsi de sortir de la zone mémoire (* et donc de remplir des portions réservées. *)
Fonction Pile_Operateur_Vide (indice : entier) : Booléen ; Début
Si indice = limite_inferieure_pile Alors Pile_Operateur_Vide - Vrai ;
Sinon
Pile__Operateur_Vide - Faux ;
Fsi ;
(* nous avons choisi d'incrementer avant d'empiler 1'element * Procédure empile_operande (val : Reel) ;
Début
Si Non(Pile_Operande_Pleine(sommet_operande)) Alors sommet_operande - sommet_operande + 1 ; pile_operande [sommet_operande] - val ;
Sinon
Afficher ("erreur : pile opérande pleine") ;
Il faut sortir avant que 1'infâme GURU ne deboule. Une bonne idée serait de sauvegarder les piles dans des fichiers juste avant de sortir, et de les consulter par soi-même. Car tous les compilateurs du DomPub ne possèdent pas forcément de Débogueur *)
Quitter ;
Fsi;
Fonction Pile_Operande_Vide (indice : entier) : Booléen ; Début
Si indice = limite inférieure pile Alors Pile_Operande_Vide - Vrai ;
Sinon
Pile_Operande_Vide - Faux ;
Fsi ;
Fin ;
oper, operateur : Caractère
x, y, long, cpt : Entier ;
sommet_operateur, sommet_operande : Entier ;
valeur, droite, gauche, resuit : Reel ;
Procédure Init_Tout ;
Début
(* initialisation du tableau des opérandes *) Pour (cpt - 1 Jusqu'a 26) Faire opérande [cpt] - 0 ;
Les tableaux commencent a 1 et nous avons choisi d'incrementer le sommet avant d'y ajouter un element. Les sommets sont alors initialises a pour qu'au premier ajout ils pointent sur la première case *) sommet_operande -0 ; sommet_operateur - 0 ;
Fonction Pile_Operande_Pleine (indice Début
Si indice = max_pile_operande Alors Pile_Operande_Pleine - Vrai ; Sinon
Pile_Operande_Pleine - Faux ; Fsi ;
Fin ;
Fonction Pile_Operateur_Pleine (indice Début
Si indice = max_pile_operateur Alors Pile_Operateur_Pleine - Vrai ; Sinon
Pile_Operateur_Pleine - Faux ; Fsi ;
Fin ;
Tableau [1..max_operande] De Reel ; Tableau [l..max pile opérande! De Reel Tableau [1..max_pile_operateur] De Caractère ;
Chaine [80] ;
entier) : Booléen ;
max__pile_operateur
entier) : Booléen
Fin
Fonction depile_operande : Reel ;
Début
Si Non(Pile_Operande_Vide(sommet_operande)) Alors
depile_operande - pile_operande[sommet_operande] ; sommet_operande - sommet_operande - 1 ;
Sinon
Afficher ("erreur : pile opérande vide") ;
Quitter ;
Fsi ;
Fin ;
Fonction depile_operateur : Caractère ;
Début
Si Non(Pile_Operateur_Vide(sommet_operateur)) Alors
depile_operateur - pile_operateur [sommet_operateur] ; sommet_operateur - sommet_operateur - 1 ;
Sinon
Afficher ("erreur : pile operateur vide") ;
Quitter ;
Fsi ;
Fin ;
Début
(* initialisation generale *)
Init_Tout () ;
(* saisie des opérandes et de leur valeur associée. Ex: A=3 *)
(* ou bien saisie d'un point pour sortir du mode de saisie *)
Faire
Afficher ('entrez vos opérandes : ') ;
Lire (oper) ;
Afficher(Majuscule(oper)) ;
Si (oper > '.') Alors Afficher(' = ') ;
Lire (valeur) ;
(* remplissage direct du tableau des opérandes par conversion *)
(* ex : a = 3 ; a = A grâce a Majuscule ? Puis Numéro[A] = 65 *)
(* enfin 65 - 64 = 1 et donc 3 est stocke dans opérande [1]
(* Ceci n'est possible que si les caractères ont des valeurs (* qui se suivent dans la table du jeu de caractères. Ce qui (* est le cas pour la table ASCII *)
opérande [Numéro(Majuscule(oper)) - 64] - valeur ;
FinSi ;
Jusqu'a (oper = '.') ;
(* j'insiste sur le "correctement parenthesee" *)
(* ex : ((A+B)-(C D)) et non (A+B)-(C D) *)
Afficher('expression correctement parenthesee : ') ;
Lire (expression) ;
(* récupéré la longueur de l'expression saisie afin de *)
(* pouvoir l'analyser caractère par caractère *) long - Longueur (expression) ; cpt - 1 ;
Tant Que (cpt = long) Faire Début
Choix expression[cpt]
(* si c'est une lettre, on empile sa valeur respective *)
'A'..'Z','a'..'z' :
empile_operande (opérande[
Numéro(Majuscule(expression[cpt])) - 64]) ;
(* empiler le signe *)
' . ! * r _ i fli
~ f i •
empile_operateur(expression[cpt]) ;
(* recuperer les deux valeurs qui entourent le dernier signe *)
(* empile. Puis depiler ce signe et effectuer l'opération en *)
(* conséquence. Ex : (A+B) avec A=3 et B=2 ; droite = 2 ; *)
(* gauche = 3 ; operateur = '+' ; resuit =3+2 *)
')' :
Début
droite - depile_operande ; gauche - depile_operande ; operateur - depile_operateur ;
Choix operateur ' + ' :
resuit - gauche + droite ;
resuit - gauche - droite ;
Fin ;
Procédure empile_operateur (val : Caractère) ;
Début
Si Non(Pile_Operateur_Pleine(sommet_operateur)) Alors sommet_operateur - sommet_operateur + 1 ; pile_operateur [sommet_operateur] - val ;
Sinon
Afficher ("erreur : pile operateur pleine") ; Quitter ;
Fsi ;
Fin ;
13
Numéro 30 - FEV 92
C= Amiga NewsTech
* r
resuit - gauche * droite ;
resuit - gauche droite ;
Fin ; (* Choix operateur *)
(* le résultat devient une opérande, dans l'expie‘.empile (5) *) empile_operande (resuit) ;
Fin ; (* pour la parenthese fermente *)
Fin ; (* Choix expression [cpt] *)
(* incrementer le compteur de progression de l'expression *) cpt - cpt + 1 ;
Fin ; (* Tant Que (cpt = long) Faire *)
(* depiler la derniere opérande qui est le résultat *) resuit - depile_operande ;
(* sortie du résultat *)
Afficher ('le résultat est : ', resuit) ;
Fin. (* programme principal *)
Listing 2 : Pile.c
* Compile avec LC -Lm -0 *
* Vous devez créer un fichier de données *
* ayant la structure suivante *
*
a 10.0 b 12.0 c 3.0 d 0.5 *
* Vous passerez ensuite ce fichier en paramètre *
* à l'éxécutable : nom executable> nom fichier de données> *
include exec types.h>
include stdio.h>
include string.h>
include ctype.h>
struct Pile_Operande float opérande;
struct Pile_Operande *suivant;
} ;
struct Pile_Operateur char operateur;
struct Pile_Operateur *suivant;
;
float lettre[26];
struct Pile_Operande *debut_operande; struct Pile_Operateur *debut_operateur;
VOID Empile_operande (float)
VOID Empile_operateur (char) float Depile_operande (VOID) char Depile_operateur (VOID)
main(int argc, char **argv)
char expression[80], signe, c;
float oper_gauche, oper_droite, resuit, val; int i, indice;
FILE *fichier;
debut_operande = (struct Pile_Operande *) malloc (sizeof (struct Pile_Operande));
debut_operande -> opérande = -1000000.00; debut_operande -> suivant = NULL;
debut_operateur = (struct Pile_Operateur *) malloc (sizeof (struct Pile_Operateur));
debut_operateur -> operateur = ' 0'; debut_operateur -> suivant = NULL;
for (i = 0; i 26; i++) lettre[i] = 0.0;
printf ("Lecture du fichier de données..."); fichier = fopen (argv[1],"r"); if (! Fichier)
puts ("Fichier introuvable !!!"); exit (5);
while (feof (fichier) == 0)
fscanf (fichier,"%c %f n",&c,&val); indice = c - 'a' ; lettre[indice] = val;
}
fclose (fichier); printf (" OK n");
printf ("Entrez l'expression correctement parenthesee n>"); fgets (expression,80,stdin); for (i = 0; i = strlen (expression); i++)
if ( isalpha (expression[i]))

indice = expression[i] - 'a
oper_droite = Depile_operande(); signe = Depile_operateur(); oper_gauche = Depile_operande(); switch (signe)

case '+' :
resuit = oper_gauche + oper_droite; break;
case '-' ;
resuit = oper_gauche - oper_droite; break;
case ' ' :
resuit = oper_gauche oper_droite; break;
case '*' :
resuit = oper_gauche * oper_droite; break;
Empile_operande (resuit); break;
case '+' :
Empile_operateur(expression[i]); break;
case '-' :
Empile_operateur(expression[i]); break;
case ' ' :
Empile_operateur(expression[i]); break;
case '*' :
Empile_operateur(expression[i]); break;
case '(': break;
}
}
printf ("Le résultat est : %t n",resuit); free (debut_operateur); free (debut_operande);
}
VOID Empile_operande (float nombre)
struct Pile_Operande ‘nouveau;
nouveau = (struct Pile_Operande *) malloc (sizeof (struct Pile__Operande) ) ;
nouveau -> opérande = nombre;
nouveau -> suivant = debut_operande -> suivant; debut_operande -> suivant = nouveau;
}
VOID Empile_operateur (char signe)
struct Pile_Operateur ‘nouveau;
nouveau = (struct Pile_Operateur *) malloc (sizeof (struct Pile_Operateur));
nouveau -> operateur = signe;
nouveau -> suivant = debut_operateur -> suivant; debut_operateur -> suivant = nouveau;
float Depile_operande (VOID)
struct Pile_Operande *element; float oper;
element = debut_operande -> suivant; oper = element -> opérande;
debut_operande -> suivant = element -> suivant; free (element); return (oper);
char Depile_operateur (VOID)
struct Pile_Operateur *element; char opérât;
element = debut_operateur -> suivant; opérât = element -> operateur;
debut_operateur -> suivant = element -> suivant; free (element); return (opérât);
Empile_operande (lettre[indice]);
}
else
switch (expression[i])
Pur Bruno Bailluet et Lucien Turski~Marcon
case ')' ;
Pourtant, les Rom Kernel Manuals donnent le moyen de détecter directement, en interrogeant le Hardware, le nombre de drives présents et surtout leur numéro (rappel : sur un A2000, le premier drive externe est DF2: et non DF1:). Enfin, quand je dis "donnent le moyen", c’est un
bien grand mot : les explications sont succintes (NDLR : et buggées,
c'est tout de même un comble !) Et aucune routine n’est présentée. Le
petit programme qui suit rattrape ce manque.
Mots pointé par AO, dans lequel elle placera l’ID de chacun des quatre drives maximum qu'il est possible de connecter à Famiga. Elle est directement utilisable telle quelle et ne modifie aucun registre du 68000. Cette routine est compatible avec tous les modèles d’Amigas passés et présents (on ne parie jamais sur l’avenir !) Et avec toutes les versions du Workbench (y compris le 2.0).
J’espère que cette routine aidera les programmeurs de jeux et de mégadémos à prendre en compte tous les drives qui peuvent être connectés à l’Amiga (et pas seulement DFO:) et qu’à l’avenir, leurs oeuvres se débrouilleront toutes seules comme des grandes pour savoir d’où charger la suite du programme...
* ****************************************************************
* TestDisks.s *
* ****************************************************************
* Interroge le hardware pour connaître le nombre de lecteurs de *
* disquettes présents. *
* ****************************************************************
* Par François Braün (Altaïr) pour Amiga NewsTech (ANT) *
* ****************************************************************
* Assembler avec Devpac 2 *
* ****************************************************************
CE QUE DISENT LES RKM
D’après Famiga Hardware Reference Manual (Revised Edition), page 311, je cite : "un mode d’identification est prévu pour lire un flux d’identification sériel 32 bits depuis un périphérique externe. Pour initialiser ce mode, le moteur doit être mis en marche, puis arrêté (...). Le signal SELxB doit être inactif. Maintenant, entrez dans une boucle dans laquelle vous activez SELxB, lisez RDY (broche 1) et désactivez SELxB. Bouclez 32 fois pour lire 32 bits de données. Le bit le plus significatif est reçu en premier. " Pas génial, comme explication...
Mais en cherchant un peu plus loin, page 357 exactement, on trouve une seconde description de ce mode d’identification, un peu plus étoffée. On y apprend que le code d’identification (ID) n’est plus sur 32 bits mais sur 16, et qu’il y a 9 étapes à suivre pour le trouver. Enfin, on y trouve une table des Ids possibles :
Buffer de 4 mots Teste les drives présents Affiche le buffer 4 lignes à afficher
0000
0000
0000
0000
($ 0000)
Pas de drive
tst .w bne. S
(a2) + Oui
1111
1111
1111
1111
($ ffff)
Drive Amiga 3" standard
lea
NonTxt(pc),aO
1010
1010
1010
1010
($ aaaa)
- réservé -
Oui moveq
'3',dO
Place le n’ du drive
0101
0101
0101
0101
($ 5555)
Drive 5" DS DD
sub.b
d4,d0
dans la chaine ASCII
1000
0000
0000
0000
($ 8000)
- réservé -
move.b
d0,2(aO)
0111
1111
1111
1111
($ 7fff)
- réservé -
move.1
d6, dl
Affiche la phrase correspondante
0000
1111
xxxx
xxxx
($ 0fxx)
Librement utilisable
move.1
a0,d2 ;
à l'état du drive
1111
0000
xxxx
xxxx
($ fOxx)
- réservé -
moveq
13, d3
(présent ou absent)
xxxx
0000
0000
0000
($ x000)
- réservé -
jsr
_LVOWrite(a6)
xxxx
1111
1111
1111
($ xfff)
- réservé -
dbra
d4,Affiche ;
Boucle pour tous les drives
0011
0011
0011
0011
($ 3333)
- réservé -
move.1
d7, al
Referme la dos.library
1100
1100
1100
1100
($ cccc)
- réservé -
move.1
4,a6
Note
: un
'x' représente un
bit quelconque (0 ou 1)
jsr
_LVOCloseLibrary(a6)
Sortie moveq
0, dO
Retour au CLI
rts
Buffer dc.w DOSName de. B OuiTxt dc.b NonTxt de. B even
0,0,0,0 ; 1 mot par drive possible
"dos.library",0 "DFx: présent",10 "DFx: absent ",10
Cette fois-ci, la description de la marche à suivre est correcte, mais les
Ids sont faux (du moins les deux premiers, je n’ai pas le matériel
nécessaire pour tester les autres) : on trouve $ ffff pour un drive Amiga 3" standard et $ 0000 si aucun drive n’est connecté. Si quelqu’un parmi vous possède un drive 5" ou un lecteur haute- densité, je serais curieux de connaître leur ID (NDLR : si c’est le cas, écrivez-nous à la rédaction, on transmettra à François).
CE QU’IL FAUT FAIRE
La démarche pour le test d’identification est donc la suivante :
1 - mettre MTRXD à l’état bas
2 - mettre SELxB à l’état bas (’x’ est le n° du drive à tester)
3 - mettre SELxB à l’état haut
4 - mettre MTRXD à l’état haut
5 - mettre SELxB à l’état bas
6 - mettre SELxB à l’état haut
7 - mettre SELxB à l’état bas
8 - lire et sauver l’état de DSKRDY
9 - mettre SELxB à l’état haut
10- Répéter les pas 6 à 9 seize fois en tout.
Arrivé ici, un petit rappel des signaux n’est peut-être pas superflu :
MTRXD : bit 7 du port B du CIA-B ($ bfdl00)
SEL3B : bit 6 du port B du CIA-B ($ bfdl00)
SEL2B : bit 5 du port B du CIA-B ($ bfdl00)
SEL1B : bit 4 du port B du CIA-B ($ bfdl00)
SEL0B : bit 3 du port B du CIA-B ($ bfdl00)
DSKRDY : bit 5 du port A du CIA-A ($ bfe001)
Le programme qui suit met en oeuvre le mode d’identification des drives. La routine TestDisks attend un seul paramètre : un buffer de 4
opt o+,ow , c+
INCDIR include:
INCLUDE exec exec_lib.i INCLUDE libraries dos lib.i
* ************* Demo lea
moveq move.1 jsr
move.1 beq move.1 jsr
move.1 lea bsr .s lea moveq Affiche lea
* **************************************************
DOSName(pc),al ; Ouvre la dos.library
0,d0 ; pour pouvoir écrire dans
4,a6 ; la fenêtre CLI courante
_LVOOpenLibrary(a6)
d0,d7
Sortie
dO, a6
_LVOOutput(a6) dû, d6
Buffer(pc),aO TestDisks Buffer(pc),a2 3, d4
OuiTxt(pc),aO
* ****************************************************************
TestDisks:
movem.l d0-d3 al,-(sp) addq.l 8,a0
Sauve les registres utilisés aO = fin du buffer parce qu'on teste en sens inverse (3 à 0) al = CIAB-PRB 3 drives (DF3: à DF1:)
lea $ bfdl00,al moveq TstDsks move. B addq.b bclr bclr bset bset bclr moveq moveq TstLoop lsl. 1 bset bclr btst bne. S bset TstNext bset dbra move.w dbra move.w movem. 1 rts
2, dO dO, dl 4, dl 7,(al) dl,(al) dl,(al)
7,(al) dl,(al)
0, d2 15,d3 1, d2 dl,(al) dl,(al)
5,$ f01(al)
TstNext
0, d2
dl,(al)
d3,TstLoop
d2,-(aO)
dO,TstDsks
dO,-(aO)
(sp)+,d0-d3 al
dl = bit pour DSKSELx (4 à 6)
DSKMOTOR low
DSKSELx low
DSKSELx high
DSKMOTOR high
DSKSELx low
d2 = ID
16 tests (1 mot)
DSKSELx high DSKSELx low
= $ bfeOOl = CIAA-PRA = DSKRDY
Met le bit dans 1'ID DSKSELx high Boucle pour les 16 bits Place l'iD dans le buffer Boucle pour les 3 drives DFO: est toujours présent ! Récupère les registres
Par François Braün
Le premier devrait maintenant vous être familier, puisque c’est celui que nous avons développé jusqu'ici. Pourtant, vous ignorez encore certaines choses sur ce mode de fonctionnement, que nous allons maintenant disséquer. L’Amiga ne dispose pas d’un circuit audio, mais de quatre, c'est-à- dire un pour chaque voix. De même, Agnus utilise quatre canaux DMA pour transférer les données (rappelons qu’Agnus n’est qu'un larbin qui porte les colis ; c’est Paula qui orchestre tout).
Après avoir initialisé les registres correspondant à un canal, vous autorisez les transferts DMA à travers ce canal. A ce moment, que ce passe-t-il ? Paula transfert l’adresse et la longueur du son dans ses propres registres internes (spécifiques à chaque canal). Après, elle génère une interruption de niveau 4 pour avertir le 68000 qu’elle a fini l’opération (nous reviendrons sur cette interruption plus tard). Le processeur peut alors changer le contenu des registres concernant l’adresse et le volume du son, celà n’influera en rien la sortie audio en cours. Pendant ce temps, Agnus charge un mot de donnée toutes les deux périodes dans le registre AUD*DAT.
VOIX DE PAULA (II)
Deuxième problème. Imaginons que vous vouliez raccorder deux sons (par exemple, deux morceaux d’une digit trop longue pour être jouée en une fois). Comment faire ? Voila la recette : vous lancez le premier morceau normalement. Après avoir chargé le contenu des registres, Paula provoque une interruption de niveau 4. C’est là que vous intervenez : vous saisissez l’interruption au vol, ré-initialisez l’adresse et la longueur du son s’il le faut avec celles du deuxième morceau (sans toucher à DMACON) et le tour est joué. En effet, lors de la seconde lecture, ce sont les registres nouvellement initialisés que Paula chargera, et ce sera donc le nouveau morceau qui sera joué. Si vous avez plus de deux morceaux, le système est bien entendu exactement le même.
Mais revenons sur cette interruption qui n’est peut être pas connue de tous. Lorsqu’une interruption est générée, l’Amiga bloque le système et saute à l’adresse correspondante (pour les interruptions de niveaux 1 à 7, l’adresse de saut est indiquée en mots longs de l’adresse $ 64 à $ 80). L’interruption est alors traitée et la main est ensuite rendue au système. Une routine de traitement d’interruption doit toujours commencer par sauver les registres du processeur qu’elle utilise, vérifier la provenance de l’interruption et voir si elle est autorisée, la traiter le cas échéant (puis indiquer qu’elle a été traitée, sinon l’ordinateur sera bloqué en traitant à l’infini la pauvre interruption qui n’en demandait pas tant), restaurer les registres et impérativament finir par un RTE.
INTENA $ DFF09A Interruptions autorisées (écriture)
INTENAR SDFF01C Interruptions autorisées (lecture)
INTREQ $ DFE09C Interruptions demandées (écriture)
INTREQR $ DFF01E Interruptions demandées (lecture)
AUD*DAT $ DFFO+A Données du son (16 bits)
Bit
Nom
Fonction
15
SET CLR
Même fonctionnement que pour
DMACON
14
INTEN
Les interruptions
ne
peuvent
avoir lieu
que si ce bit est
mis dans INTENA
10
AUD3
Interruption pour
le
canal 3
9
AUD2
Interruption pour
le
canal 2
8
AUDI
Interruption pour
le
canal 1
7
AUD0
Interruption pour
le
canal 0
* = numéro du canal, + = $ A pour le canal 0, $ B pour le canal 1, $ C pour le canal 2 et $ D pour le canal 3
Vous vous rappelez sûrement que je vous avais dit qu’une coupure du canal DMA était sans effet si elle durait moins de temps que deux fois la période du son joué... Celà vient du fait que l’état du canal DMA n’est vérifié qu’avant chaque transfert de données. Donc, si vous le coupez et le ré-autorisez entre deux transferts, c’est comme s’il ne s’était rien passé. Et le temps entre deux transferts est le double de la période du son, puisque les données sont chargées mot par mot.
Pour clarifier tout ça, voici un petit exemple de routine de gestion de l’interruption sonore.
START:
; D'abord, on sauvegarde quelques trucs
Les données situées dans AUD*DAT seront ensuite converties du format numérique au format analogique, pour être exportées vers les prises de sortie audio de l’Amiga. Une fois arrivée à la fin de l’échantillon sonore, Paula rechargera le contenu des registres sonores dans ses propres registres internes et générera à nouveau la même interruption, et ainsi de suite jusqu’à ce que les transferts DMA soient stoppés.
QUELQUES PROBLEMES... ET LEUR SOLUTION
Premier problème. Imaginons que nous voulions lancer en même temps le même son sur les quatres canaux. Si les sorties ne sont pas synchronisées entre elles, cela va produire une jolie cacophonie, voire cacapho- nie. Cela ne se produira toutefois que dans un cas précis : si l’ordinateur jouait déjà un son sur l’une ou plusieurs des voix. Dans ce cas, Paula ne tiendra compte du nouveau son qu’après avoir fini celui qu’elle était en train de jouer, d’où un décalage par rapport au son qui a débuté immédiatement.
J’en vois déjà qui pensent qu’il suffit d’interdire les transferts DMA de tous les canaux sonores, d’initialiser les registres qu’il faut puis d’autoriser ses propres canaux. Eh bien non, car qui vous dit que la coupure des canaux DMA aura duré moins de deux fois la période du son ? Pour en être sûr, il faut attendre le temps que dure deux fois la plus longue période possible, 65535 cycles, ou 20 millisecondes environ. Soit vous utilisez un timer des CI As pour cela, soit vous utilisez une petite boucle faisant au moins 65535 cycles. Mais la première solution semble la plus propre et assurera un fonctionnement sur tout Amiga, même équipé d’une carte accélératrice. Pour résumer : avant tout travail avec Paula, s’assurer que les précédents transferts DMA sont bel et bien terminés.
Move.1 $ 7 0,INT4 move.w $ dff01C,INTA or.w $ 8000,INTA move.w $ dff01E,INTD or.w $ 8000,INTD ; On met les nôtres à la place move.1 MON_INT,$ 70 move.w $ 7fff,$ dff09A move.w $ 7fff,$ dff09C move.w $ c080,$ dff09A ; On lance le premier son move.w 500,$ DFF0A6 move.w 0050,$ DFF0A8 move.1 DATA_SON,$ DFF0A0 move.w 50000,$ DFF0A4 move.w $ 8201,$ DFF096 BOUCLE_WAIT:
int. Niveau 4
met bit 15
met bit 15
int. Canal 0
PERIODE
VOLUME
ADRESSE
LONGUEUR en mots Envoie canal 0
bouton gauche de la souris
SILENCE !
Btst 6,$ BFE001 bne.s BOUCLE_WAIT ; On remet tout comme il faut move.w 0001,$ dff096 move.w $ 7fff,$ dff09A move.w $ 7fff,$ dff09C move.1 INT4,$ 70 move.w INTA,$ dff09A move.w INTD,$ dff09C rts
MON_INT:
movem.IdO aO,-(a7) move.w $ dff01c,d0
$ E,dO FIN_INT $ dffOle,dO $ 7,d0 FIN INT
; autorisation ?
Btst beq. S and.w btst beq. S
; d'où viens tu ?
P
?
; traitement
interruption:
move.1
POINTEUR,aO

pointeur sur table
move.1
(a0)+,$ DFF0A0

nouvelle adresse
move.w
(a0)+,$ DFF0A4

nouvelle longueur
cmp. 1
FIN_TABLE,a0

détection fin table
bne. S
SUITE_INT
lea TABLE, a 0

Rebouclage de la digit
SUITE_INT:
move.1
a0,POINTEUR
f
réactualise pointeur
move.w
$ 0080,$ dff09C

Interruption traitée
FIN_INT:
movem.1(a7)+,d0 a0
rte
DATA_SON:
blk.b
384000,0

le son
TABLE :
de. 1
DATA SON
de .w
50000
de. 1
DATA SON+100000
de .w
50000
de. 1
DATA SON+200000
de .w
50000
de. 1
DATA_SON+ 300000
de .w
42000
FIN_TABLE:
POINTEUR:
de. 1
TABLE+6
INTA : de. W
0
INTD : de. W
0
INT4 : de. 1
0
Attention ! Si le morceau suivant n’a pas la même fréquence, ou si vous voulez changer son volume, vous devrez attendre le commencement de ce morceau (chargement des registres) pour effectuer les changements. En effet, le volume et la fréquence ne sont pas sauvegardés par Paula : elle travaille directement dessus.
ON EST JAMAIS MIEUX SERVI QUE PAR SOI-MEME !
Bon, il est temps de passer au mode manuel. Ici, plus question de remplir AUD*LCH, AUD*LCL et AUD*LEN ni d’autoriser un quelconque DMA : vous devrez vous occuper vous-mêmes de ces choses. Par contre, il est toujours aussi nécessaire d’indiquer le volume (AUD*VOL) et la période (AUD*PER) de l’échantillon sonore. Après, vous transférez le premier mot de données sonores dans l’AUD*DAT correspondant au canal choisi (vous vous rappelez ? C’est l’adresse où Agnus chargeait les données sonores pour chaque canal). Celà aura pour effet d’amorcer le processus : le mot sera lu à la fréquence indiquée et une interruption de niveau 4 sera générée (la même que tout à l’heure) indiquant que vous pouvez envoyer le mot de données suivant. Si vous ne saisissez pas l’occasion, la transmission sonore prendra fin. Pour repartir, vous serez obligé de réamorcer le processus. Sinon, tout se poursuivra normalement.
Je ne vais pas vous présenter une routine d’application parce qu’elle serait la même que celle ci-dessus, à quelques détails près :
• plus de table définissant les fragments, mais seulement un pointeur et les données sonores (la détection de fin se fait ici sur la fin de l’échantillon).
• plus d’autorisation de DMA concernant les canaux utilisés. Il faut même les interdir, sinon les intervalles entre interruptions ne seront plus ceux attendus.
• plus de transfert d’adresse et de longueur durant l’interruption. Un seul mot de données sonores dans AUD*DAT (ne pas oublier le premier transfert permettant l’amorçage).
Ce mode de fonctionnement est tout de même assez contraignant, puisqu’il demande une attention constante de votre part. De plus, il coûte cher en cycles, puisque le 68000 est sans cesse requis (de ce fait, il n’est pas trop conseillé dans une démo ou tout autre programme demandant un travail conséquent en parallèle). Mais là s’arrêtent ses défauts... Vous me direz : c’est déjà pas mal... A mon avis, ce système est parfait pour un programme se bornant à faire de la musique. Certains semblent sceptiques... Je m’en vais les convaincre.
Le premier avantage de ce système est qu’il permet d’utiliser des échantillons dont la longueur ne dépend que de la capacité mémoire (c’est sûr, ce n’est pas très percutant comme argument).
Le second avantage est que n ous pouvez faire de la modulation de fréquence et d’amplitude en utilisant uniquement la voix que vous voulez moduler. En effet, rien n'empêche de modifier le volume et la période du signal en même temps que nous chargez un mot de donnée dans AUD*DAT. Vous serez bien synchronisé cette fois-ci (là, ça commence à devenir intéressant...).
Le troisième avantage est que vous pouvez réaliser assez "facilement" de nombreux effets, tels que de l'inversion de son, du scraching, entre- coupement de samples, etc... (certes, ce serait possible en mode automatique, mais ce ne serait guère plus simple).
Le quatrième avantage n’est pas des moindres : vous pouvez mélanger en temps réel plusieurs signaux. En d’autres termes, vous pouvez faire croire que l’Amiga a plus de 4 voix (comment croyez-vous que font les logiciels de musique en 8 voix ?). Le subterfuge est simple : on sait que le son est défini par ses variations d'amplitude. Pour mixer deux sons, il suffit donc d’ajouter les amplitudes instantanées des deux signaux, en tenant compte des différences de volume et de fréquence (eh oui, sinon vous devrez toujours avoir des signaux dont la fréquence d’échantillonnage est la même et des notes de musique identiques sur un même canal). De plus, vous devez vous assurer que le résultat de l’opération a les dimensions requises (amplitude comprise entre +127 et
- 128). Je ne développerai pas ici les méthodes permettant de le faire, mais si celà vous intéresse, n’hésitez pas à écrire à TANT.
NDLR : Cinquième avantage du mode manuel, les données sonores n’ont plus besoin de se trouver en mémoire CHIP puisque le DMA n’est pas sollicité. Ca aussi, c’est intéressant !
On le voit, ce mode d’utilisation est tout de même assez riche. De plus, il existe des cas particuliers dans lesquels on peut difficilement s’en passer. Par exemple, pour messieurs les samplers fous, si vous voulez écouter ce que vous allez (où êtes en train de digitaliser) ce mode est obligatoire.
UN FILTRE D’AMMMMMM...(IGA) ?
Mais à quoi sert-il donc, le filtre passe-bas ? Ceux qui pensent qu’il s’agit d’une sorte de loudness ont tout faux ! Ce filtre a pour effet de couper toutes les hautes fréquences (sons aigus). Il ne reste alors que des sons graves (basses fréquences). Plus de risques de distorsion (en anglais : aliasing) qui apparaît dans ces bandes de fréquences. C’est donc pour ravir vos oreilles que l’Amiga est équipé de ce filtre. Pour ma part, je préfère quand il n’est pas actif, car la bande de fréquence est alors beaucoup plus complète. Pour l’activer ou le désactiver, il suffit d’enlever ou de mettre le bit 1 du port A du CI A-A (adresse $ BFE001). Celà a aussi pour effet d’allumer et d’éteindre la power-led (pour les anciens Amigâs uniquement).
Exemple : eor.w
inverse l'état du filtre
2,$ BFE001
DERNIERES RECOMMANDATIONS
Essayez l’effet d’écho obtenu en désynchronisant légèrement deux voix jouant le même son (l’effet est encore meilleur si elles sont sur le même canal stéréo).
Quelques conseils pour avoir des sons de bonne qualité :
• Utilisez toute la bande d’amplitude qui vous est offerte (+127 à -128). Sinon, il y aura plus de bruit qu’autre chose.
• N’échantillonnez pas trop bas en fréquence (7000 est vraiment un minimum !).
• Faîtes débuter et finir vos sons par la même valeur pour éviter les parasites lors des bouclages où des enchaînements.
Et voilà, vous savez tout... A vos claviers !
Par Philippe Rivai lion
YACC
Æ
».
Comme promis, voici (enfin) l’étude de la version Amiga de Yacc, un utilitaire Unix aussi célèbre que Lex et que l’on utilise d’ailleurs avec ce dernier pour réaliser, entre autres choses, des compilateurs.
Comme base notre langue, le Français.
Toute phrase en Français est constituée de mots agencés suivant des règles précises. Par exemple, une phrase va être constituée d’un nom, d’un verbe et d’un complément. La reconnaissance d’un mot appartenant à la langue française est du travail de Lex, alors que l’analyse de l’assemblage de ces mots pour déterminer si la phrase formée a un sens du point de vue grammatical est du ressort de Yacc.
En Français, la grammaire est basée sur une série de règles utilisant des notions basiques tels les noms, verbes, adjectifs et d’autres plus complexes, comme les phrases, attributs, sujets, objets.... Dans un programme Yacc, toutes ces notions sont représentées par des symboles grammaticaux.
Notre exemple d’analyseur va pouvoir traiter les notions suivantes :
- VERBE pour un verbe
- NOM pour un nom
- ADJECTIF pour un adjectif
- NOMBRE pour représenter un indication numérique
- Phrase pour représenter une phrase
- Prédicat pour représenter un prédicat
- Complément pour un complément
- Sujet pour le sujet
- Objet pour représenter un objet
Une différence a été faite intentionnellement entre majuscules et minus
cules, pour marquer les différences de niveau grammatical. Un symbole comme 'VERBE", qui représente un mot de la langue française, est une entité de bas niveau. Ce symbole est dit aussi "terminal", car il représente l’entité manipulable de plus bas niveau. Les symboles plus complexes comme "Phrase", qui sont réalisés à partir d’un assemblage de mots, sont dits de haut niveau ou "non-terminaux", car formés d’autres symboles, terminaux ou non.
Nous allons maintenant étudier comment transposer cette hiérarchie à Yacc, en prenant comme exemple la phrase suivante : "Stéphane range trois disquettes vertes".
En Français, l’ordre normal pour représenter une phrase est un sujet suivi d’un prédicat (c’est le cas de notre exemple). Toutefois, sous la forme impérative, seul le prédicat subsiste ("range trois disquettes vertes !"). Cette première règle peut être symbolisée en Yacc sous la forme:
Le nom de Yacc est la contraction de "Yet Another Compiler Compiler", c'est-à-dire un compilateur de compilateurs. En effet, Yacc a été créé pour réaliser rapidement des compilateurs de langage : ces programmes lisent en entrée un source sous la forme d’un langage de programmation et génèrent un code de sortie dans un autre langage. Il s’agit d’un point de départ pour l’écriture de compilateurs Basic, Pascal, C ou de toute autre langage de votre choix. Il ne s’agit bien sûr que d’un outil d’aide à la réalisation de telles applications, il ne fait pas tout, tout seul non plus.
COMPARAISON ENTRE FLEX ET YACC
Sous bon nombre d’aspects, Yacc est similaire à Flex. Les deux programmes lisent un fichier d’entrée qui contient une liste de règles et la convertissent en une fonction C qui peut être compilée et exécutée (fig. 1). La différence est toutefois essentielle dans l’utilisation que l’on peut faire de ces deux logiciels : Flex est utilisé pour produire un analyseur lexical dont le but est de reconnaître dans un flot d’entrée, un certain nombre de symboles, alors que Yacc permet de réaliser un analyseur grammatical dont le but est de reconnaître des associations de symboles sous la forme de phrases. Ces deux programmes sont donc totalement complémentaires, car la base de tout programme de transcription est la combinaison d’un analyseur lexical et d’un analyseur syntaxique.
Programme Yacc
’v- _
Yacc
-?
Y. tab.c
i*' -*¦
Programme Lex
•-
Lex
-?
Lex.yy.c
Phrase
Sujet
Prédicat
Prédicat
"Phrase"représente ce qui va être défini. Il est suivi du caractère ":" et de sa définition, composée de "Sujet" et "Prédicat". A la ligne suivante, la définition continue avec une barre verticale "I", symbole du "ou", lui-même suivi d’une deuxième définition. Ce genre de syntaxe est appelée une définition alternative ; la définition de Phrase s’arrête à la présence du point-virgule.
La définition de la phrase n’est pas encore complète, dans la mesure où elle est composée de symboles non-terminaux. En effet, Prédicat et Sujet n’ont pas encore été définis.
Un prédicat est constitué, dans notre cas, d’un verbe et d’un complément, ce qui se représente fort simplement par :
f h
y. tab.c
f
cornp C
¦p* | a,oui
j
Prédicat
Complément
VERBE
Comme on peut le remarquer, nous avons déjà une branche de notre arbre d’analyse qui se termine, puisque VERBE est terminal.
Etudions maintenant le sujet et le complément : tous deux peuvent être définis par un objet, ce qui ce traduit par :
lex.yy.c
Fig 1.
Sujet

Complément
Objet
Objet
UNE APPROCHE PAR L’EXEMPLE
Enfin, l’objet peut être :
- un nom
- un nombre + un nom
- un adjectif + un nom
Afin de mieux vous sensibiliser au fonctionnement de Yacc, nous allons réaliser un petit exemple d’analyseur syntaxique en prenant
- un nombre + un adjectif + un nom Soit en Yacc :
Objet
NOM
NOMBRE NOM ADJECTIF NOM NOMBRE ADJECTIF NOM
Etant arrivés à décrire l'ensemble des entités complexes sous forme de symboles terminaux, le travail d'analyse est, par conséquent, terminé. Bien entendu, les règles de grammaire française sont beaucoup plus complexes que ce petit exemple, en particulier pour les exceptions qui font tout le charme de notre langue, et le malheur des programmeurs d'analyseurs syntaxiques.
PROGRAMMATION DE L’EXEMPLE
Maintenant que nous avons décrit les règles syntaxiques de notre exemple en langage Yacc, nous allons pouvoir bâtir un programme complet autour ce ces règles.
Comme pour Flex, un programme Yacc est décomposé en trois sections :
- la section Déclarations
- la section Règles
- la section Fonctions Utilisateur
Chaque section étant séparée par
Nous commencerons notre programme par la section "Règles", qui représente le corps du programme.
La section REGLES
Nous allons donc transformer nos règles grammaticales en un programme qui reconnaît les phrases françaises. Nous devons également spécifier quelles sont les actions qui vont être effectuées lorsqu'une règle sera déclenchée : ces actions sont des fragments de code C qui sont positionnés à la droite de la déclaration de la règle, entourées par des accolades. Pour notre exemple précèdent, la section règles devient :
Phrase : Sujet Prédicat ' n' printf(" Phrase déclarative n');}
Prédicat ' n' printf(" Phrase impérative n');}
I error printf("Erreur de Syntaxe n");}

Prédicat : VERBE complément printf(" Prédicat n");}

Sujet : Objet printf(" Sujet n");}

Complément : Objet printf(" Complément n");}

Objet : NOM
I NOMBRE NOM I ADJECTIF NOM
I NOMBRE ADJECTIF NOM
Ce morceau de programme représente le coeur de notre analyseur. Pour le taire fonctionner correctement, il nous faut désormais ajouter les autres sections.
Par rapport à ce que nous avions défini précédemment, nous avons ajouté la directive error pour le traitement des erreurs de syntaxe ainsi que des caractères "retour chariot", pour déterminer la fin d'une phrase.
Lu section DECLARATIONS
Nous allons maintenant étudier la première section du programme Yacc, à savoir la section Déclarations. Dans cette zone du programme, nous allons définir les symboles terminaux ainsi que toutes les variables globales dont nous aurons besoin dans le reste du programme.
Dans notre exemple, les symboles terminaux sont VERBE, ADJECTIF, NOM, NOMBRE. Ce sont des constantes entières, appelées "tokens", qui représentent des symboles grammaticaux en provenance de Flex et passés à Yacc.
La directive token fait en sorte que chaque symbole reçoive une valeur constante. Cette valeur est systématiquement supérieure à 256 pour qu’il n’y pas de conflit avec le passage de caractères, dont les codes ASCII sont compris entre 0 et 255.
Dans notre exemple, la directive token serait :
%token VERBE NOM ADJECTIF NOMBRE
Le caractère % se place immédiatement devant la directive, sans espace entre les deux. Lors de la phase de compilation, Yacc assignera une valeur à chaque symbole. Cette valeur est totalement transparente pour le programmeur qui n'a, du reste, pas à s'en soucier.
La section FONCTIONS UTILISATEUR
La dernière section du programme Yacc contient les fonctions de support, comme le programme principal, les routines de gestion des erreurs et le programme Lex. Dans un premier temps, créer les routines minimales pour chaque support.
Nous allons
Pour éviter de taper ces routines, nous pourrions nous en remettre directement à la bibliothèque de fonctions associée à Yacc. Mais pour une meilleure compréhension de l'ensemble et, compte tenu de la faible taille des fonctions, il est préférable de les écrire soi-même.
Programme principal : comme dans tout programme C, le programme principal s'appelle main(). Il est le point de départ du programme généré par Yacc. Dans la première version de notre exemple, le programme principal se contentera d'appeler la fonction yyparse(), qui est le nom de l’analyseur syntaxique qu’aura généré Yacc (de l’anglais par se, acte de séparer en plusieurs parties). Notre programme principal est donc :
main()

printf("? "); yyparse();
}
On demande une phrase, on l’analyse et on rend la main à l’utilisateur. C’est tout.
Gestion des erreurs : deux fonctions de gestion des erreurs sont normalement nécessaires : yyerrorf) et yywrapf). La première est appelée lorsqu’en cours de fonctionnement, le programme Yacc découvre une erreur. La deuxième est invoquée à la fin, pour tout remettre en ordre avant de terminer l’exécution du programme. Pour une première version, ces deux routines seront vides.
Yyerror()
(}
yywrap()
}
La fonction Lex : elle peut être définie dans cette section. Elle doit s appeler yylexf). Son rôle est de créer des tokens pour Yacc. Dans un premier temps, nous laisserons cette routine vide.
Yylex()
}
Voilà, nous avons maintenant tout ce qu’il nous faut pour réaliser un analyseur syntaxique. Le mois prochain, nous verrons comment compiler ce programme Yacc sur Amiga et nous continuerons notre apprentissage de ce langage en améliorant notre exemple.
En attendant, pour ceux qui connaîtraient déjà ce langage, et également pour vous permettre de vous le procurer pour le mois prochain, je vous signale que Yacc version Amiga est disponible dans la collection Fred Fish sur la disquette numéro 419.
Je me permets de vous souhaiter une bonne et heureuse année 1992, avec TANT, bien entendu. A bientôt.
Par Herr Doktor von GlutenStimmellmDorf *tj
Votre pantagruélique serviteur revient faire des siennes dans les pages de votre cher ANT, suite à une absence prolongée pour cause de programmation intense d’utilitaires déments.
GREFFEUR
00000000, 00000001 et 00000002, puis trois mots longs donnant leur longueur respective, et ainsi de suite.
ÜSiii
Vous avez compris ? Je continue.
Pour parvenir à nos fins, il suffit de connaître tous les identificateurs existants (ou plutôt ceux utilisés dans un fichier exécutable) et de savoir comment chaque hunk est structuré.
Et puisque l’on parle d’utilitaires, celui que je vous propose aujourd’hui, n’est pas dément, mais presque. Il s’agit d’un programme capable de greffer des programmes sur des programmes. Autrement dit, vous disposez d’un excellent programme, mais vous regrettez que lors de son lancement, telle ou telle fonction ne soit pas accomplie : il vous suffit de
programmer vous-même ladite fonction et de la greffer sur le
programme fautif grâce à l’utilitaire du mois.
RESTEZ SAGES !
Je vois venir certains petits malins, qui ont immédiatement compris qu’ils leur sera possible de se servir de cet utilitaire (au demeurant génial) afin de répandre d’odieux virus coquilles tout partout. Oui cela est possible, mais je maudis les coupables jusqu’à la 52ième
incarnation. Voilà !
COMMENT CELA SE PEUSSE ?
Puisque l’Amiga est multitâche, n’en déplaise aux bidouilleurs de démos (que je maudis cette fois jusqu’à la 104ième incarnation), il n’est pas possible de savoir à l’avance à quelle adresse mémoire sera chargé tel ou tel programme.
Pour résoudre cette difficulté, les développeurs de l’Amiga ont
employé la solution suivante : un programme exécutable n’est pas sauvé tel quel sur disquette. Ainsi, toutes les addresses de saut ne sont pas réelles mais comptées à partir de l’adresse 0. Lors du chargement du programme en mémoire, AmigaDOS se charge d’ajouter à chaque adresse répertoriée, l’adresse absolue de chargement et le programme se trouve relogé et donc en état de fonctionner. Bien entendu, pour que cela se fasse correctement, le fichier binaire contient, en plus du code lui-même, toute une pagaille de renseignements que nous verrons plus loin.
Ce n’est pas tout. En plus de ceci, et pour des raisons analogues (bonne gestion de la mémoire), un programme peut être constitué en seul bloc ou en plusieurs. Ce sont les segments ou HUNKS.
Pour y voir clair, entrez sous CLI la commande suivante :
type c:list opt h
Vous obtiendrez un dump hexa de la commande List, qui devrait ressembler à celui reproduit ci-dessous :
0000: 000003F3 00000000 00000002 00000000 ..
0010: 00000001 0000004F 00000961 000003E9 0...a____
0020: 0000004F 286A0164 700C4E95 2401223C ...0(j.dp.N.$ ."
0030: 00000095 49FAFFEE ... ... etc etc ...
000003F3 signifie que l’on a à faire à un programme exécutable 00000000 ne signifie rien d’important
00000002 signifie que le programme est en deux morceaux (HUNKS)
00000000 est le numéro du premier hunk chargé
00000001 est le numéro du second hunk chargé. Donc, il y a deux
hunk à charger, puisque qu’en informatique 0 plus 1 égale 2, comme
chacun sait
0000004F est la longueur en mots longs du premier hunk 00000961 est la longueur en mots longs du second hunk 000003E9 est un identificateur particulier
Si le programme comportait un nombre plus élevé de segments, trois par exemple, on aurait 00000003 à la place de 00000002, suivi de
Nous allons donc passer tout ça en revue, sachant que les renseignements proviennent de la Bible de l’Amiga, donc qu’il vaut mieux de méfier. Mais c’est le seul livre à ma connaissance où il soit possible de trouver ces renseignents, depuis que Commodore a décrété que lAmigaDOS Technical Manual était devenu obsolète. Il fallait oser...
REVUE DES TROUPES
$ 3E9 hunk_code
Précède une partie de programme susceptible de fonctionner une fois relogé en mémoire. L’identificateur $ 3E9 est suivi du nombre de mots longs de cette partie de programme, puis du code lui-même.
$ 3EA hunkjdcita
Précède une partie de datas (données initialisées). Le reste est comme ci-dessus.
$ 3EB : hunk_bss
Précède une zone BSS. Il s’agit d’une zone de mémoire sans valeur initiale particulière que le système alloue dynamiquement avant de la mettre à disposition du programme. L’identificateur $ 3EA n’est donc suivi que de la taille de cette zone, exprimée en mots longs.
$ 3EC hunk_reloc32
Précède la fameuse table de relogement. Ceci nous intéresse de près. Derrière $ 3EC se trouvent le nombre d’offsets à corriger, le numéro de hunk concerné et enfin, les offsets eux-mêmes. Ces informations se répètent autant de fois que nécessaire (jamais plus souvent qu’il n’y a de hunks !) Et la fin de la table est marquée par un nombre d’offsets égal à 0.
Pour greffer un programme sur un autre, il suffit donc de modifier l’en-tête afin de déclarer la présence du hunk supplémentaire, puis d’incrémenter de 1 chaque numéro de hunk concerné par un relogement. C’est aussi simple que cela.
$ 3ED hunk_relocl6 $ 3EE hunk_reloc8
Ces deux identificateurs sont tout-à-fait similaires au précédent, si ce n’est qu’ils concernent des relogements sur 16 ou 8 bits respectivement. Les tables elles-mêmes sont toujours constituées de mots longs.
$ 3F0 hunk_symbol
Précède une table de symboles susceptibles d’intéresser un débogueur. D’après la Bible, cette table de mots longs est conclue par un mot long nul. Je suis désolé, mais j’ai vu de mes yeux des tables de symboles conclues par deux mots longs nuls. Mais la Bible sans erreurs ne serait pas la Bible...
$ 3F1 hunk_debug
Dans cette table peut être mis à peu près n’importe quoi qui peut aussi intéresser un débogueur en tant qu’informations supplémentaires à celles contenues dans hunk_symbol. Voyez par exemple ce qu’en fait le Lattice (pardon le SAS) lorsque vous compilez et linkez avec les options de débogage activées. $ 3F1 est suivui du nombre de mots longs constituants la table, puis des longs mots eux-mêmes. Aucun format n’est donc imposé.
Y’EN A D’AUTRES
$ 3 F3 hunkjieader
C’est le "mot de passe" qui, placé en début de fichier, assure le DOS qu’il a bien à faire à un programme exécutable.
Il existe encore d’autres identificateurs, qui concernent les fichiers objets et les overlays. Le programme que je vous propose devrait pouvoir greffer sans problème sur un programme comportant des overlays, mais je n’ai pas eu le temps d’essayer (bon d’accord, je n’ai pas eu le courage).
COMMENT ECRIRE LA GREFFE
C’est très simple. Il faut l’écrire en assembleur avec un macroassembleur (SEKA ist streng verboten!), en deux segments dont un bidon (cf. Exemples), sauver les registres au début, les récupérer puis sauter avec un JMP dans le segment bidon.
ET CA MARCHE
Parfaitement. J’ai même greffé un programme sur Excellence!, qui "pèse" tout de même la bagatelle de 221 segments. Lorsque vous avez greffé un programme, rien ne vous empêche, si ça vous amuse, de le greffer un fois de plus ou d’en greffer un autre, il n’y a pas de limite.
DEUX EXEMPLES
Le premier : Cls. Vous pouver le greffer sur une commande CLI quelconque, list par exemple, en procédant comme suit :
Greffeur c:List QuelquePart:Cls Ram:SuperList
Vous obtenez en Ram: l’exécutable SuperList. Si vous lancez ce programme, vous avez un effacement du CLI courant avant le listage du répertoire.
Le second : CLI_Regs. Avez vous essayé de tracer sous Monam2 une commande CLI telle que List ? Oui ? Alors vous avez constaté que cela n’était pas possible, car les commandes CLI utilisent le contenu des registres transmis par le CLI. Greffez donc CLI_Regs sur vos commandes CLI et vous pourrez les tracer ! Il est vrai que cela plantera (beaucoup) plus tard au cours du traçage, mais pour une autre raison... que je vous laisse élucider.
CA MARCHE TOUJOURS ?
Non, certains programmes, comme par exemple Deluxe Paint iîî, ont des hunks trafiqués. En ce cas, le greffeur le signale et essaie quand même, mais vous aurez presque à coup sûr un gourou lorsque vous essaierez le résultat (c’est pas ma faute !).
LE LISTING
Ecrit en C, il ne présente aucune difficulté de compréhension si vous maîtrisez convenablement les pointeurs, sinon bonsoir, je suis fatigué.
* **************************************************************

*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
* Programme: Greffeur.c *
* Auteur: L'incroyable HUNK F Mazué pour ANT le 04 11 1991 *
* Fontion: Greffer un éxécutable écrit par vous sur * n'importe quel éxécutable de votre choix
*
* Remarque: Vous êtes vivement prié de ne pas vous servir de * cet utilitaire de grande classe afin de répandre
* d'odieux virus coquilles tout partout....
*
* Utilisation: Greffeur executable> greffe> résultat>
*
* Ecrit en SAS 5.10 *
* Compiler et linker d'un seul coup par: include exec types.h>
include exec memory.h>
include libraries dos.h>
include libraries dosextens.h>
include stdio.h>
struct FileLock *LockGreffe; * because longword alignment * struct FileLock *LockSource, *LockTarget;
struct FilelnfoBlock *InfoGreffe, *InfoSource, *InfoTarget;
LONG ?Source, *Greffe, *Target, *Fixe, *FixeSource, *FixeGreffe; BPTR source, greffe, target;
VOID CleanUp();
LONG *Reloc(LONG *,int), *Code(LONG *), *Debug(LONG *);
LONG *Symbol(LONG *), *Data(LONG *), *Bss(LONG *);
LONG *Overlay(LONG *);
VOID maintint arge, char **argv)
int i, nbr_hunk = 0, compteur;
LONG *pointeur, Test;
printf(" nGreffeur de Programmes ! ! ! n"); printf("Par F Mazué, L'incroyable HUNK ! n");
if(argc != 4)
printf("La ligne doit avoir comme paramètres 3 noms de fichiers n n");
printf("D'abord le fichier source qui doit être éxécutable n");
printf("Ensuite le fichier à greffer sur le fichier source n");
printf(" ce fichier doit aussi être
éxécutable n");
printf("Enfin le fichier cible n");
CleanUp );
lnfoGreffe=(struct FilelnfoBlock *)
AllocMem(sizeof(struct FilelnfoBlock),MEMF_PUBLIC|MEMF_CLEAR); if ( !InfoGreffe) CleanUpO;
InfoSource=(struct FilelnfoBlock *)
AllocMem(sizeof(struct FilelnfoBlock),MEMF_PUBLIC|MEMF_CLEAR); i f(!InfoSource) CleanUp();
lnfoTarget=(struct FilelnfoBlock *)
AllocMem(sizeof(struct FilelnfoBlock),MEMF_PUBLICIMEMF_CLEAR); if(!InfoTarget) CleanUp();
LockSourc = (struct FileLock *)Lock(argv[1],SHARED_LOCK); Examine(LockSource,InfoSource);
if(InfoSource->fib_DirEntryType >0)
printf("Nom de fichier ou de répertoire incorrect n"); CleanUp();
}
LockGreffe = (struct FileLock *)Lock(argv[2],SHARED_LOCK);
Examine(LockGreffe,InfoGreffe); if(InfoGreffe->fib_DirEntryType >0)
printf("Nom de fichier ou de répertoire incorrect n"); CleanUp();
Source=(LONG *)AllocMem(InfoSource->fib_Size,
MEMF_PUBLIC|MEMF_CLEAR); if(! Source) CleanUp();
FixeSource=Source; source=Open(argv[l],MODE_OLDFILE);
Read(source,Source,InfoSource->fib_Size);
Greffe=(LONG *)AllocMem(InfoGreffe->fib_Size,
MEMF_PUBLIC|MEMF_CLEAR); if(!Greffe) CleanUpO;
FixeGreffe=Greffe;
greffe=Open(argv[2],MODE_OLDFILE);
Read(greffe,Greffe,InfoGreffe->fib_Size);
Target=(LONG *)AllocMem(InfoGreffe->fib_Size+
InfoSource->fib_Size,
MEMF_PUBLICIMEMF_CLEAR);
if (! Greffe) CleanUpO;
Fixe=Target;
target=Open(argv[3],MODE_NEWFILE); if(!target) CleanUpO;
LockTarget = (struct FileLock *)Lock(argv[3],ACCESS_WRITE); if(*Source != 0x3F3 |I *Greffe != 0x3F3)
printf(" nll faut des fichiers éxécutables !! n"); CleanUp();
}
pointeur = Greffe+2; if(*pointeur 1=2)
printf("XnLa Greffe ne rempli pas les conditions
nécéssairesXn");
printf("Opération terminée... désoléXn");
? 3F3 ?
?Target++=*Source++;
?Target++=*Source++; nbr_hunk=?Source;
printf(" nLe programme Source contient %d HUNKSXn",nbr_hunk);
? Nom du HUNK (à priori NULL) ?
?un HUNK de plus ?
?Source +=1?
?Target++= Source++; ?Target++= Source++;
compteur = ?Source; compteur +=1;
? Nbre de HUNK placé ?
? N° du premier HUNK chargé ?
if(compteur != nbr_hunk)
printf(" n le nombre de HUNKS de la source et le nombre de HUNKSXn");
printf("effectivement chargés ne se correspondent pas
! n");
printf(" nOn essaie quand meme ! [RETURN] n"); getchar();
? Dernier HUNK à charger plus 1 ?
?Source +=1; ?Target++=*Source++;
? Source pointe sur le premier identificateur de la source ? pointeur=Greffe+5; * Taille du HUNK à ajouter ?
printf(" nLa Taille du Hunk greffé est %d octetsXn", ?pointeurM ) ;
?Target++=*pointeur++; for(i=0;i compteur;i++)
?Target++=ASource++; ? Tranfert des longueurs de hunk de la source ?
}
? Source pointe sur le premier identificateur de la source ? pointeur=Greffe+7;
******* TRAITEMENT de la greffe *??????? while(?pointeur != 0x3F2) ? Fin d'un HUNK ? ?
switch(?pointeur) * Quel HUNK ? ?
case 0x3EC:
pointeur=Reloc(pointeur,0 ) ;
printf("Relogement 32 bits de la greffe effectuéXn"); break;
case 0x3ED:
pointeur=Reloc(pointeur,0);
printf("Relogement 16 bits de la greffe effectuéXn"); break;
case 0x3EE:
pointeur=Reloc(pointeur,0);
printf("Relogement 8 bits de la greffe effectuéXn"); break;
case 0x3E9:
pointeur=Code(pointeur);
printf("Transfert du code de la greffe effectuéXn"); break;
case 0x3F0;
pointeur=Symbol(pointeur);
printf("Table de symboles de la greffe traitéeXn"); break;
case 0x3Fl:
pointeur=Debug(pointeur);
printf("Table de debogage de la greffe traitéeXn"); break;
default:
?Target=?pointeur++; break;
?Target++= pointeur++;
********* TRAITEMENT DE LA SOURCE ?*****?? printf(" n nA Traitement de la source ??? n n");
? ?Source 1 OxOFFFFFFF pour filtrer les HUNK chip-ram etc ? while((Test=(?Source & OxOFFFFFFF)) '= 0x3F2 || compteur ï=0) switch(Test)
case 0x3EA:
SourceaData(Source);
printf(" nHunk Data traité n");
compteur -=1;
break;
case 0x3EB;
Source=Bss(Source);
printf(" nHunk Bss traité n");
compteur -=1;
break;
case 0x3EC:
Source=Reloc(Source,1);
printf("Relogement 32 bits effectué n"); break;
case 0x3ED:
Source=Reloc(Source,1);
printf("Relogement 16 bits effectué n"); break;
case 0x3EE:
Source=Reloc(Source,1);
printf("Relogement 8 bits effectué n"); break;
case 0x3E9:
Source=Code(Source);
printf(" nHunk Code traité n");
compteur -=1;
break;
case 0x3F0:
Source=Symbol(Source);
printf("Hunk Symbol traité n");
break;
case 0x3Fl:
Source=Debug(Source);
printf("Hunk debug traité n");
break;
case 0x3F5:
printf("Xn ttention, la source contient des OverlaysXn") printf("Le résultat n'est pas garanti... n"); Source=Overlay(Source); printf("Overlay traitéXn"); break;
default:
?Target++=ASource++;
break;
?Target++=?Source;
Write(target,Fixe,(int)Target-(int)Fixe); * Et voilà ! ? printf("XnXnOPERATlON TERMINEE F Mazué vous salue n n"); CleanUp();
}
VOID CleanUp()

if(FixeSource) FreeMem(FixeSource,InfoSource->fib_Size); if(FixeGreffe) FreeMem(FixeGreffe,infoGreffe->fib_Size);
if(LockSource) UnLock(LockSource); if(LockGreffe) UnLock(LockGreffe); if(LockTarget) UnLock(LockTarget);
if(source) Close(source); if(greffe) Close(greffe); if(target) Close(target);
if(InfoSource) FreeMem(InfoSource,
sizeof struct FilelnfoBlock)); if(InfoGreffe) FreeMem(InfoGreffe,
sizeof(struct FilelnfoBlock)); if(InfoTarget) FreeMem(InfoTarget,
sizeof(struct FilelnfoBlock)); exit(O); ? Fin du match ! ?
LONG ?Reloc(LONG ?pointeur,int déplacement)
int i, encore*1, limite;
?Target++= pointeur++; ? 0x3EC est mis while(encore)
1imite*?pointeur;
?Target++= pointeur++; ? Nbr de correction placé ? ?pointeur +=deplacement; * HUNK corrigé ?
?Target++= pointeur++; ? Et placé *
for(i=0;i limite;i++)
?Target++=?pointeur++;
if(?pointeur==0)
*Target++=*pointeur++; * pour le 0 final * break;
}
}
return(pointeur);
LONG *Code(LONG *pointeur) int limite, i;
; CLI ou SHELL courante par F MAZUE ; offsets d'EXEC.LIBRARY
OldOpenLibrary = -408 ;une fois n'est pas coutume ! CloseLibrary = -414
; offsets de dos.library Output = -6 0
Write = -48
* Target++=*pointeur++; limite = *pointeur; ‘Target++=*pointeur++;
* 0x3E9 est mis*
* nombre de longs mots à copier * * Ce nombre lui-même copié *
for(i=0;iclimite;i++)
* Target++=*pointeur++;
return(pointeur);
}
LONG *Debug(LONG ‘pointeur)
int limite,i;
* Target++=*pointeur++; * 0x3Fl est mis *
limite = ‘pointeur; * nombre de longs mots à copier *
* Target++=*pointeur++; * Ce nombre lui-même copié *
for(i=0;i limite;i++)
* Target++=‘pointeur++; return(pointeur);
}
LONG ‘Symbol(LONG ‘pointeur)
int perpet=l;
LONG Test;
* Target++=*pointeur++; * 0x3F0 est mis * while(perpet)
switch(Test=(‘pointeur & OxOFFFFFFF))
* cf article *
case 0x3E9 case 0x3EA case 0x3EB case 0x3EC case 0x3ED case 0x3EE case 0x3Fl case 0x3F2 case 0x3F5 return(pointeur);
default;
* Target++=*pointeur++; break;
}
LONG ‘Data(LONG ‘pointeur)
int limite,i;
* Target++=*pointeur++; * 0x3EA est mis*
limite = ‘pointeur; * nombre de longs mots à copier *
* Target++=*pointeur++; * Ce nombre lui-même copié *
for(i=0;i limite;i++)
‘Target++=*pointeur++; return(pointeur);
}
LONG *Bss(LONG ‘pointeur)

* Target++=*pointeur++; *0x3EB mis *
‘Target++=‘pointeur++; * Longueur mise * return(pointeur);
}
LONG ‘Overlay(LONG ‘pointeur)
int limite,i;
* 0x3F5 est mis*
* Target++=*pointeur++; limite = ‘pointeur;
* Target++=*pointeur++;
* Target++=*pointeur++;
for(i=0;iclimite;i++)
* Target++=*pointeur++;
* nombre de longs mots à copier * ‘Ce nombre lui-même copié *
* Niveau d'Overlay *
‘Target++=‘pointeur++; * fin d'overlay * return(pointeur);
Listing 2 : Cls.s
Par Frédéric Mazué T3
CLS
; Programme d'effacement de la fenetre
section l,code
movem.l dO-d7 aO-a6,-(sp) tst.l dO beq erreur
move.l $ 04,a6
lea dosname(pc), al
jsr OldOpenLibrary(a6)
beq erreur
move.l d0,a6
jsr Output(a6)
lea début(pc),aO
move.1 aO,d2
moveq (fin-debut),d3
jsr Write(a6)
move.l a6,al
move.l $ 04,a6
jsr CloseLibrary(a6)
erreur
movem.l (sp)+,d0-d7 a0-a6 jmp suite
dosname
dc. b 'dos.library',0 even
début
dc. b $ 9b, ";H"
dc. b $ 9b,"J" fin
; ATTENTION, respecter les majuscules !
Section 2,code suite nop
Listing 3 : CLI_Regs.s
* ****************************************************************
* prog: reg_cli.s
* Auteur: F Mazué pour ANT le 04 11 1991
*
* Programme qui initialise les registres A2,A5,A6 afin qu'il
* soit possible de tracer sous le débogueur de votre choix la
* commande CLI sur laquelle il sera greffé.
* Nécessite le programme 'Greffeur' du même auteur
* ****************************************************************
incdir "include " ;mettez ici votre path personnel
inc lude 11 exec exec. I "
include "exec exec_lib.i"
include "exec execbase.i"
include "libraries dos.i"
include "libraries dosextens.i"
section l,code
Obligatoirement INDISPENSABLE ! Execbase
movem.l d0 a0,-(sp) move.l 4,a6 move.l LibList(a6),aO lea dosname,al CALLEXEC FindName
chercher et trouver la dos.library et obtenir la valeur de DOSBASE c'est ici une solution préférable à ouvrir puis fermer la librairie
cf RMK 'libraries dosextens.h (ou .i)
move.l d0,a0 move.1 dl_A2(aO),a2 move.1 dl_A5(aO),a5 move.1 dl_A6(aO),a6
movem.l (sp)+,d0 a0 jmp dummy
dosname DOSNAME even
section 2,code dummy rts
n'importe quoi ou autre chose
Aujourd’huije vais vous parler d’un sujet qui me tient à coeur, les ellipses et les cercles, ces derniers n’étant rien d’autre qu’un cas particulier des premières.
Il ne reste plus qu’à donner un petit coup de Blitter pour remplir le tout.
Quelques remarques : il n’y a pas de clipping, c’est-à-dire que si l’ellipse vient à sortir de l’écran, elle risque fort d’aller embêter la mémoire des petits copains ! De plus, ce listing trace une ellipse pleine. Pour ne dessiner que le contour, il suffit d’enlever le remplissage au Blitter et d’afficher un point à chaque itération du calcul de delta.
Hé oui, je vais vous expliquer comment afficher de magnifiques ellipsesà l’écran, et ce de manière extrêmement rapide. Hélas, petite contrainte : nos ellipses seront parallèles aux axes ! Pour mieux comprendre, regardez le schéma 1 ci-dessous, vous y trouverez toutes les informations nécessaires.
Le listing ci-dessous est l’application de cet algorithme. Il est bien entendu sous Devpac, car je pense que toute personne sensée à définitivement cessé d’utiliser Seka. Vous pouvez changer les paramètres A et B (rayons de l’ellipse) en manipulant la souris et déplacer lecentre de Fellipse à l’aide du joystick. Cette dernière opération est à utiliseraveLmiodération pour ne pas faire sortir l’ellipse de l’écran.
Voila, j’éspère que tout le monde n’a pas abandonné la lecture ! La prochaine fois, j’essaierai de trouver quelque chose de plus simple.
* ******************************
* Ellipse.s *
* Auteur: Thomas LANDSPURG
*
? ?
* A assembler avec Devpac 2 *
* ******************************
01+, 02+,W-,A-, C-
OPT
NB_BYTE_LIGNE=40 ; Taille sur x de l'ecran en octets NB_LIGNE=200 ; Hauteur de l'ecran en lignes
prof=l ; profondeur de l'ecran
CUSTOM= $ df f 0 0 0
incdir "include:" include exec exec_lib.i include graphies graphies_lib.i include hardware custom.i
; DEBUT DU PROGRAMME move.1 graphname,al CALLEXEC OpenLibrary move.1 dO,_GfxBase move.1 dO,aO move.l $ 32(aO),oldcop
ouverture de graphies.library
Ancienne copperlist
C

y
Fiff 1
start
Une ellipse est donc définie par son centre (xc,yc) et ses rayons a et b. Si a=b on obtient un cercle. Cette ellipse est définie mathématiquement par l’équation
x a +y b =1.
Une fois obtenus x et y vérifiant cette équation, il ne reste plus qu’à additionner les coordonnées du centre xc et yc à l’ensemble des coordonées des points.
Pour tracer notre ellipse, nous allons dessiner un point par ligne d’écran et ses symétriques, puis remplir le toutà l’aide du Blitter en mode remplissage. Il y a en effet deux symétries dans une ellipse de ce type : une horizontale et une verticale. Il suffit donc de calculer un seul quart de l’ellipse puis d’afficher les autres points par symétrie.
Nous partons du centre de l’ellipse, du point de coordonnées (x=a,y=0), qui vérifie évidemment l’équation de l’ellipse. Puis nous cherchons le point suivant d’abscisse xl=x+delta et d’ordonnée yl=y+L Tout le problème est de calculer delta. La démonstration est un peu longue pour tenir dans la place qui m’est impartie, mais on arrive au résultat suivant.
Pour calculer delta, on effectue la somme (2*y-I)*a*a b*b puis on ajoute -2*x+l, en décrémentant x à chaque itération jusqu’à ce que cette somme devienne négative. Je m’explique un peu : cette valeur delta est représentative de la différence entre l’ellipse affichée et l’ellipse théorique. Si delta est positif, cela veut dire que le point (x,y) est à l’extérieur de la surface de l’ellipse, et si delta est négatif ce point est à l’intérieur de l’ellipse. Si enfin, delta est nul, alors (x,y) est sur l’ellipse. Plus delta est petit, plus le point de coordonées (x,y) est proche de l’ellipse. C’est vrai, ce n’est pas évident à comprendre, mais une ellipse est un peu plus compliquée qu’une droite !
Nous avons donc le point (x,y) que nous affichons, ainsi que ses symétriques. Une fois que cela est fait pour tous les points de l’ellipse,
bsr allocscr
en cas d'erreur mémoire, on sort
tst.1 abitplaneO ;
beq. S errorjnem ;
CALLGRAF OwnBlitter CALLEXEC Forbid move.b %10000111,$ bfdlOO
Arrête le drive (BEURK!)
Lea CUSTOM,a5
move.w $ 20,dmacon(a5) ; Desactive DMA sprites
pg principal
bsr main pg ;
lea CUSTOM,a5
move.w $ 8020,dmacon a5) CALLEXEC Permit CALLGRAF DisownBlitter ; bsr freescr ;
out :
reactive DMA sprites reautorise le multitâche et l'utilisation du blitter libération de l'ecran
restore la copperlist
move.1 _GfxBase(pc),aO move.l oldcop,$ 32(aO) move.l $ 26(aO),CUSTOM+copllc
error_mem:
move.1 _GfxBase(pc),al CALLEXEC CloseLibrary moveq.l 0,d0 rts
referme les librairies
* ***************
* PG PRINCIPAL *
* ***************
; attente de la synchro ; switching de page ; effacement de l'ecran
main_pg bsr bsr bsr bsr lea
move.w move.w lsr .w and.w move.w lsr .w move.w bsr bsr
btst bne. S rts
sync
switch
bltclear
waitblit
CUSTOM,a5
joyOdat(a5),d4
d4,d5
8,d5
$ 7f,d4
d4,rayona
2,d5
d5,rayonb
joyst
draw_ellipse
6,$ bfe001 main_pg
; récupéré coord de la souris
; A de l'ellipse ; B de l'ellipse ; affichage de l'ellipse ; test bouton droit de la souris
; switching de page switch move.1 abitplaneO(pc),a5
move.l bbitplaneO(pc),a4
gestion switching de page on inverse les deux buffers
tst.b flagcop ;
bne copb
exg a5,a4 ;
$ l,d0 ; x=x-l
out_ellipse_before2 ; x 0 ->fin de l'ellipse
copb
not.b flagcop
move.1 a4,bitplaneO ;
bsr initbrush j
move.1 _GfxBase(pc),aO ;
move.1 mycop,$ 32(aO)
lea CUSTOM,a5
move.1 mycop,copllc(a5 ;
clr.w copjmpl(a5)
rts
; ATTENTE DE LA SYNCHRO
btst 0,vposr+1(a5)
beq sync
cmp.b 20,vhposr(a5)
bne sync2
rts
subq.w bmi. S add.w add.w subq.w bmi. S swap delta_pos2:
move.w sub.w move.w lsr .w and.b move.b lsr .b eor .b eor .b move.w add.w move.w lsr .w and.b move.b lsr .b eor .b eor .b lea lea dbf
travail la copperlist. La copperlist
buffer de on change on reaffiche
dO, dl dO, dl $ l,dl delta_neg2 dl

;--delta+2*x ; delta+2*x-l ; tant que deltacO ; dl=dl*65536
d5=centrex
d5=centrex-x
d6 contient cette position on se préparé a l'affichage de ce point
a5,d5 ;
dO, d5
d5,d6 ;
3,d6
$ 7,d5
$ 80,d3
d5,d3
d3,(al,d6.w)
d3,(a2,d6.w) ;
a5,d5
dO, d5
d5,d6
3, d6
$ 7,d5
$ 80,d3
d5,d3
d3,(al,d6.w) d3,(a2,d6.w) ;
- 40(al),al 40(a2),a2 ;
d7,loop_ellipse2;
sync
sync2
on l'affiche en haut a gauche et son symétrique bas gauche d5=centrex d5=centrex+x
d6 contient cette position on se préparé la aussi a l'affichage de ce point
; JOYSTICK
j oyIdat(a5),dO 1, dO pasdroite 1,circlex
joyst
move. W btst beq add.w pasdroite: btst beq sub.w pasgauche:
move.w asr .w eor .w btst beq add.w pashaut btst beq sub.w
pasbas: out_joy rts
9, dO pasgauche 1,circlex
d0,dl
l,dl
dl,d0
8,d0
pashaut
1,circley
0,d0
pasbas
1,circley
on l'affiche en haut a droite
et en bas a droite
prochaine ligne haut
prochaine ligne bas
tant qu'il y aura des lignes...
un petit coup de blitter
out_ellipse_before2:
bsr blit_circle out_ellipse: rts
Remplissage au blitter. On remplit tout l'écran, même
; si ce n'est pas nécessaire. Blit_circle:
move.l bitpla ; on se placer"a
pc),a0; pointeur sur 1 la fin de 1'écran (mode
'écran
'descendant')
; Allocation mémoire pour les buffers écran allocscr:
move.l taillescr*2,d0 move.1 $ 10002,dl
CALLEXEC AllocMem move.1 dO,abitplaneO move.l dO,bitplaneO add.l taillescr,d0
add.l lea bsr move.1 move.1 move.1 move.w move.w
5_LIGNE*prof *NB_B YTE__LIGNE, aO 'CUSTOM, a5 waitblit
a0,bltdpt(a5) ; source
a0,bltapt(a5) ; destination
-l,bltafwm(a5) ; masque $ 09f0,bltcon0(a5) ; D=A
$ 000a,bltconl(a5) ; mode remplissage et descente
2 écrans car double buffering
premier buffer
move.1
dO,bbitplaneO ; deuxième buffer
clr.w
bltdmod(a5) ; modulo=0
rts
clr.w
bltamod(a5) ;
; desallocation mémoire des buffers
move.w
NB_LIGNE*prof 6+NB_BYTE_LIGNE 2,bltsize(a5)
freescr move.l
abitplaneO(pc),al
rts
move.1
taillescr*2,dO
CALLEXEC
! FreeMem
rayona : de. W
100
rts
rayonb: dc.w circlex:dc.w
80
160
; MET DANS LA COPPER LIST LES Addresses DES BITPLANES
circley:dc.w
100
initbrush:
; dO = l'adresse du prem plan
lea
p_bitpl,aO
; EFFACEMENT AU BLITTER D'UN BITMAP
move.w
$ 00e0,dl ; addresses de bitpOh
bltclear:
move.w
prof-l,d2
bsr
waitblit
ibrush
move.1
a5,d0
move.1
bitplaneO(pc),bltdpt(a5)
bsr
metadr
move.w
$ 01f0,bltcon0(a5) ; D=A en desactivant A
bsr
metadr
clr.w
bltconl(a5)
lea
NB_BYTE_LIGNE(a5),a5
clr.w
bltadat(a5) ; donnee=0
dbf
d2,ibrush
clr.w
bltdmod(a5) ; modulo=Q
rts
move.w rts
NB_LIGNE*prof 6+NB_BYTE_LIGNE 2,bltsize(a5)
metadr
swap
dO ; c'est ici qu'on le met dans la coplist
move.w
dl,(aO)+
waitblit:
move.w
dO,(aO)+
btst
14,dmaconr(a5)
addq.w
2,dl
bne. S
waitblit
rts
rts
GRAFNAME
; Le coeur du programme: la routine de trace d'ellipses ; utilise les variables rayona,rayonb,circlex et circley ; ainsi que bitplane 0 draw_ellipse:
rayona(pc),d6
$ ff,d6 ; Pour éviter les ellipses trop grandes
graphname
even _GfxBase: oldcop: bitplaneO: abitplaneO: bbitplaneO: flagcop:
dcb.l de. 1 dcb.l dcb.l dcb.l de .b
move.w
and.w
beq
move.w and.w beq clr. 1 move.w
out_ellipse rayonb(pc),d7 $ ff,d7 out_ellipse d4
d6,d4
si A=0 on sort
pour les ellipses trop grandes! Si B=0 on sort
tailleplan=NB_LIGNE*40
taillescr=tailleplan*prof
d4=A
lsl.1
8,d4
section
coplist,DATA C
divu
d7, d4
d4=A B
mycop dc.w
$ 1081,$ fffe
mu lu
d4,d4
d4=(A B)carre
de .w
$ 180,0,$ 182,$ ff
move.1
d4,a0
d4=alpha
de .w
$ 008e,$ 2581
neg. 1
d4
de .w
$ 0090,$ 40cl
add.l
aO, aO
de .w
$ 0092,$ 0038
clr. 1
dO
de. W
$ 0094,$ 00cc
move.w
d6,dO ;
d0=a=x
de .w
$ 102,0
move.1
dO, dl
de .w
$ 108,(prof-1)*40
add.w
dl, dl
dl=delta=2 *x-1
de .w
$ 10a,(prof-1)*40
swap
dl
de .w
$ 009c,$ 8010
move. 1
bitplaneO(pc),al;
pointeur sur le buffer écran
de .w
$ 3101,$ fffe
move. W
circley(pc),d2 ;
d2 contiendra la ligne haute
de .w
$ 0100,prof 12
move. W
d2,d5
p_bitpl
move.w
d2, d3
d3 la ligne basse
dcb.w
prof*4,0
subq.w
1, d3
de. 1
$ fOdffffe
mu lu
NB_BYTE_LIGNE,d5
; d5 contient l'offset en octets
de. 1
$ 01000000
add. 1
d5,al ;
al pointe sur la ligne du bas
de. 1
$ fffffffe
move.1
al,a2 ;
a2 pointe sur la ligne du haut
fincop4:
lea
- 40(al),al
subq.w
1, d7
a cause du dbf!
END
circlex(pc),a5
a5 contient coord x du centre
move.w loop_ellipse2: add.l sub. 1 bpl. S swap delta_neg2:
alpha=alpha+2*(a*a) (b*b) delta=delta-alpha delta>0 on affiche le point sinon on itéré
a0,d4 d4, dl delta_pos2 dl
Par Thomas Lanâsparg
LES_ETOILES 3D
§L 'w
Salut, coucou et bonjour ! Pour bien commencer l’année, vous allez avoir droit au programme qui a permis à beaucoup de faire de très jolis effets avec les étoiles, autrement dit vous avez compris : nous allons faire des étoiles en 3D.
Le principe est simple : on choisit des points et on les fait bouger (ha, ha, mooorrrreeee de rire ! (NLDR : je sais, Jérôme, je sais : mort s ’écrit avec un ’t ’, pas un ’e ’, mais bon, si ça peut te faire plaisir...))- Pour les faire bouger, il faut se servir des touches du pavé numérique : les touches de
gauche servent à décrémenter la vitesse, les touches du milieu à la
stopper et les touches de droite à l’incrémenter. La première ligne de chiffres contrôle la composante X de la vitesse, la seconde, la
composante Y et la troisième, la composante Z.
Bon allez, sérieusement, la première étape consiste à choisir une centaine d’étoiles au hasard (sous entendu, les coordonnées X, Y et Z) dans un cube de côté 1024. D’ailleurs, vous trouverez dans le source une routine de hasard à peu près convenable (la technique n’est pas de moi, mais j’ai oublié de qui c’était. De toute façon, je vous expliquerai la routine ainsi que le principe plus tard). Donc, l’observateur se situe perpendiculairement à l’une des faces du cube et la ruse consiste à bouger les étoiles elles-mêmes, et non pas le point d’observation. Avantage : la gestion en est grandement simplifiée.
En effet, si vous allez vers l’avant, les étoiles vont avancer vers vous et vous allez fatalement rencontrer la face opposée du cube. Conclusion, vous allez voir de moins en moins d’étoiles au fur et à mesure que vous avancerez vers la face. Il faut donc éviter ce
problème, sinon ça fait pas très beau. D’où l’avantage de la technique : en effet, les étoiles qui vont nous dépasser et donc se trouver derrière nous, vont être remises de l’autre côté. Exemple sur une droite : je suis en 1, l’étoile est en 4 ; quand j’avance, l’étoile vient en 3, puis en 2, en 1, puis en 0. Au moment où elle atteint le 0, on la remet en 4 et donc je vois toujours une étoile arriver vers moi. En faisant cela pour les 150 étoiles, vous obtenez le mega extra super d’enfer hyper géant... (à compléter) effet d’univers 3D alors qu’en fait, vous restez bêtement devant une vulgaire boîte de 1024 de côté.
Vous devez vous demander pourquoi j’ai choisi 1024 comme taille du cube ? Oui, c’est vrai : pourquoi, Maître ? Hé bien, Disciple, pour deux raisons principales (les 36037 autres étant mineures, quoi que la 8342 soit limite) : la première est que 1024 a l’immense privilège d’être une puissance de 2 (2 puissance 6 exactement) et qu’en informatique, tout ce qui est relié aux puissances de 2 est d’une grande facilité de gestion. Dans notre cas, nous avons pu, grâce à ce chiffre particulier, ne pas tester si le point sort ou non du cube : en effet, nous n’avons qu’à faire sur chaque coordonnée une simple opération logique de type AND 1023, et la méchante coordonnée qui s’aventure en 1027 par exemple est immédiatement remise en 3. La seconde raison est que 150 étoiles est juste le nombre qu’il faut dans une cube de 1024 de côté, car si on avait fait un cube de 2 puissance 32 de côté on aurait vu une étoile tous les 3 ans, ce qui aurait nettement atténué l’effet du déplacement.
Je vais quand même vous expliquer la 8342ième raison, car elle a son importance. En effet, quand on fait joujou avec la 3D, l’échelle des coordonnées n’est pas du tout imposée par le calcul ou un autre facteur, elle est choisie selon le goût du programmeur lors de la projection (tranformation des coordonnées 3D en 2D). J’aurais très bien pu choisir un carré de 2 puissance 24 de côté et adapter les coordonnées des points en conséquence (NDLR : et augmenter le nombre d’étoiles, ça n’aurait pas marché ?).
Pour ce qui est de la gestion de la couleur en fonction de la distance, cela se révèle assez simple : on se sert de la coordonnée Z de chaque étoile pour connaître sa distance d’avec l’observateur. Les puristes auraient peut-être préféré faire le calcul suivant x + y + z . En effet, cela aurait donné un chiffre proportionnel à la distance exacte entre chaque étoile et le centre de l’écran, c’est-à-dire l’observateur... En utilisant la coordonnée Z, on obtient la distance entre chaque étoile et le plan représenté par l’écran. L’erreur est donc réelle mais elle est si minime que personne ne s’en aperçoit. De plus, on gagne un temps- machine considérable.
Pour gagner du temps lors de l’affichage (qui prend quand même environ 50% du temps d’exécution total), j’ai fait une routine de point pour chacune des quatre couleurs utilisées. On y perd en souplesse, mais on y gagne en rapidité et dans une démo, le temps machine prime. Autre astuce qui alourdit le programme : chaque étoile doit bien évidemment être affichée puis effacée, et pour disposer de 4 couleurs d’étoiles (plus le fond), il faut 3 bitplans. Pour gagner un peu de temps, j’ai défini 4 bitplans et j’installe la palette de telle sorte que chacune des 4 couleurs corresponde à un bitplan différent. Grâce à cela, je trace et efface chaque étoile sur un seul plan, d’où un gain relativement important. Autre astuce (déjà utilisée des milliers de fois), je pré-calcule les addresses de début de lignes ; cela me permet de ne pas avoir à faire une multiplication à chaque fois que je trace un point.
Pour ceux qui n’ont jamais fait de 3D, je vais expliquer comment on parvient à rendre l’effet de perspective. Tout part d’un principe simple et connu de tous : la perspective est une notion selon laquelle plus un objet est loin de l’observateur, plus il lui apparaît petit. C’est donc cette notion qu’il faut simuler. Dans un premier temps, on attribue à chaque étoile une coordonnée en 3 composantes (X, Y, Z), ce qui permet de la situer dans l’espace. X représente l’horizontale, Y, la verticale et Z la profondeur. On s’aperçoit assez vite qu’il faut faire intervenir Z dans les calculs, car la taille de l’objet dépend directement de la distance qui le sépare de l’observateur. Le calcul que tout le monde utilise est la division de x et y par z (je ne sais pas s’il est juste par rapport à la réalité, mais en tout cas, il correspond suffisamment bien pour que personne ne voie d’erreur).
Prenons un exemple concrêt : on est dans une voiture roulant en permanence à 50km h et on regarde un panneau routier se trouvant 300 mètres devant. A 300 m, le panneau grossit lentement, à 100 m, il grossit plus vite et à 20 m encore plus vite, donc le grossissement ne s’opère pas de façon linéaire et seule la division permet de donner l’effet voulu. Détail : il faut avant de diviser x et y, les multiplier par un gros chiffre, sinon on obtiendra un objet de taille minuscule.
La phrase du" mois : 'Ton meilleur repos est le sommeil. Tu le provoques souvent intentionnellement, cependant tu crains la mort, qui n est rien de plus. "
Aujourd’hui, je me sens généreux, je vais donc vous en donner une deuxième : "I ’m not préjudice, 1 hate everybody. "
* AUTEUR: j.etienne
* SUJET: déplacement d'étoiles en pseudo 3d
* ASSEMBLEUR: devpack 2.14
* UTILISATION: Les touches du pave numérique
* opt case off
C-
OPT
incdir 'include:' include 'exec exec_lib.i' include 'hardware eustom.i'
$ dff000 4
320
256
4
BPL_X 8
BPL_WIDTH*BPL_Y BPL_SIZE * BPL_DEPTH
180
custom
execbase
BPL_X
BPL_Y
BPL_DEPTH
BPL_WIDTH
BPL_SIZE
SCR_SIZE
NB_STAR
vsync: macro .wait_vsync @: move.1 and. 1 cmp. 1 bne. S endm
vposr(a5),d0 $ lff00,dO *$ 100,dO .wai t_vsync @
* ************* programme principal ***************** move.l (execbase).w,a6 lea custom,a5 CALLEXECForbid
move.w
$ 03e0,dmacon(a5) * ail dma off except disk
KEY DEC Z KEY STP Z
$ ld
$ le
move.w
(BPL_DEPTH 12)+$ 200,bplconCUa )
KEY_INC_Z
$ lf
clr.w
bplconl(a5)
clr.w
bplcon2(a5)
moveq
0,d0
move.w
BPL WIDTH*(BPL DEPTH-1),bpllmod(a5)
move.b
$ bfec01,dO
move. W
BPL_WIDTH*(BPL_DEPTH-1),bpl2mod(a5)
not
ror.b
dO
1, dO
move.w
$ 2981,diwstrt(a5) *
move.w
$ 29cQ,diwstop(a5) * > init un écran 320*192
cmp.b
KEY_DEC_X,dO
move.w
$ 0038,ddfstrt(a5) * > avec un mot en plus
bne. S
.DONT DEC X
move.w
$ 00d0,ddfstop(a5) * pour le shift
sub.w .DONT_DEC_X
ACC,SPEED_X
bsr
BUILD_COPLIST
cmp.b bne. S
KEY_STP_X,dO .DONT_STP_X
move.1
coplist_adr,copllc(a5) * > run my coplist
clr.w
SPEED_X
clr.w
copjmpl(a5) *
.DONT_STP_X cmp.b
KEY INC X,dO
move.w
$ 8380,dmacon(a5) * blitter,copper,bitplane
bne. S add.w
.DONT INC X ACC,SPEED_X
* init
. Color map
.DONT_INC_X
COLOR_
1 =
$ fff
cmp .b
KEY_DEC_Y, dO
COLOR_
2 =
$ bbb
bne. S
.DONT_DEC_Y
COLOR_
3 =
$ 888
sub.w
ACC,SPEED_Y
COLOR_
4 =
$ 444
.DONT_DEC_Y cmp .b
KEY_STP_Y,dO
DUMMY
SET
0
bne. S
.DONT_STP_Y
move.w
0,color+DUMMY(a5)
clr .w
SPEED_Y
DUMMY
SET
DUMMY+2
.DONT_STP_Y
move.w
COLOR_4,color+DUMMY(a5)
cmp .b
KEY INC Y,dO
DUMMY
SET
DUMMY+2
bne. S
.DONT_INC_Y
rept
2
add.w
ACC,SPEED_Y
move. W
COLOR 3,color+DUMMY(a5)
.DONT_INC_Y
DUMMY
SET
DUMMY+2
cmp .b
KEY_DEC_Z,d0
endr
bne. S
.DONT DEC Z
rept
4
sub.w
ACC,SPEED_Z
move.w
COLOR_2,color+DUMMY(a5)
.DONT_DEC_Z
DUMMY
SET
DUMMY+2
cmp .b
KEY_STP_Z,dO
endr
bne. S
.DONT_STP_Z
rept
8
clr .w
SPEED_Z
move.w
COLOR 1,color+DUMMY (a5)
.DONT_STP_Z
DUMMY
SET
DUMMY+2
cmp .b
KEY_INC_Z,dO
endr
bne. S add.w
.DONT_INC_Z ACC,SPEED_Z
move.w
3,SPEED X * init the first speed
.DONT_INC_Z
move. W
3,SPEED_Y
rts
move.w
-4,SPEED_Z
bsr
INIT_TAB_STAR * init the first coords
SPEED_X ds. W
1
MAIN_LOOP:
SPEED_Y ds . W 1
SPEED_Z ds . W 1
* **********************************************
* >capture the key pressed
* and put is code RAW in dO
DELTA_Z =
move.1 move.1 lea lea
move.w move.w
* > barre de couleur pour
* > montrer le temps d'exécution *
*
* >capture the key pressed
* and put is code RAW in dO *
* sort si on press sur esc
add.w add.w add.w and.w and.w and.w move.w move.w move.w
* quand une etoile sur du cube,
* > on la remet dedans.
*
sub.w
sub.w
ext.l
ext. 1
asl. 1
asl.l
add.w
divs
divs
add.w
add.w
* dont clear the page
* the first stars
tst .w bit cmp.w bgt tst .w bit cmp .w bgt
lsl .w add.l move.w lsr .w lea and.w
.LOOP_CLEAR_STAR:
move.l (a0)+,al
move.b dl,(al)
dbf dO,.LOOP_CLEAR_STAR
.END_CLEAR_STAR rts
* ***************************************************
END_CLEAR_STAR
GESTION_KEYBOARD: * this routine computes the speed according to the keyboard
ACC = 1
KEY_DEC_X = $ 3D
KEY_STP_X = $ 3e
KEY_INC_X = $ 3f
KEY_DEC_Y = $ 2d
KEY_STP_Y = $ 2e
KEY_INC_Y = $ 2f
vsync $ f
bsr BUILD_COPLIST
* double buffering for tab
move.1 LOG_TAB_ADR_POINT,dO
move.1 PHY_TAB_ADR_POINT,LOG_TAB_ADR_POINT move.1 dO,PHY_TAB_ADR_POINT
* double buffering for screen
move.l LOG_SCR_ADR,dO
move.1 PHY_SCR_ADR,LOG_SCR_ADR
move.1 dO,PHY_SCR_ADR
bsr GESTION_KEYBOARD
bsr C LEAR_S TAR
bsr AFF_STAR
move.w $ f00,color(a5) *
move.w $ 2f,d0
dbf dO,*
clr.w color(a5)
move.b $ bfec01,d0
not dO
ror.b l,dO
cmp.b $ 45,dO
beq INIT_END
btst 6,$ bfe001
bne MAIN_LOOP
bra INIT END
move.1 LOG_TAB_ADR_POINT(pc),a 0 tst.l (aO) beq.s .END_CLEAR_STAR
move.1 NB_STAR-1,dO moveq 0,dl
CLEAR STAR: * clears ail the old stars which were on this screen
.POINT_NOT_ON_SCREEN BPL_X-l,dO .POINT_NOT_ON_SCREEN dl
.POINT_NOT_ON_SCREEN BPL_Y-l,dl .POINT_NOT_ON_SCREEN
* compute the adr and the shift for the point move.1 LOG_SCR_ADR,a 0 2, dl
ADR_LINE_ACORDING_TO_Y(pc,dl.w),aO dO, dl 3,dl
(aO,dl.w),aO %lll,dO
END_GESTION_KEYBOARD
AFF STARî *************************************
* This routine computes the coord and displays each star 250
NB_STAR-l,d7 LOG_TAB_ADR_POINT(pc),al TAB_STAR(pc),a2 SPEED_X(pc),a3 $ 03ff,d4 $ 01ff,d3
dO
clip the point: test if the point is in or out of screen
d3,d0
d3,dl
dO
dl
7,dl 7, dO
DELTA_Z,d2
d2,d0
d2, dl
BPL_X 2,dO BPL_Y 2,dl
compute the transformation in plane
(a3),d0 2(a3),dl 4(a3),d2 d4,d0 d4,dl d4,d2 dO,(a2)+ dl,(a2)+ d2,(a2)+
.LOOP_EACH_STAR:
* compute the new coord for each star movem. W (a2),dO-d2
?
Cmp.w bgt. S lea
move.1 bset dbf rts
.NOT COLOR_2
3*1024 4+DELTA_Z,d2 .NOT_COLOR_3 1*BPL_WIDTH(aO) , aO
* save adr to clear point later
move.1
VPOSR+CUSTOM,dO
STAR
and. 1
RND_M,dO
cmp. 1
RND M-l,dO
bit. S
.SEMENCE LT RANDM M
subq.1
2, dO
.SEMENCE_LT_RANDM_M:
* save adr to clear point later
addq.1
1, dO
move.1
dO,RND_Z
STAR
rts
aO,(al)+ dO,(aO)
aO,(al)+ d07(aO)
* BUG SI ON PLACE CETTE LIGNE PLUS HAUT
* OPT o+ code en absolu court ! !
RND_Z: ds . 1 1
END AM INIT RANDM
* réactivation de l'ancienne coplist
GfxName(pc),al * nom de la library ds al
version 0 (the last) lib graphique ouverte adr de graphiebase ds a4
* chargement de l'adr de l'old coplist et lancement
* activation des canaux dma multi switching autorise flag d'erreur desactive
END_INIT_TAB_STAR
3*NB_STAR TAB_ADR_POINT_1 TAB_ADR_POINT_2 NB_STAR NB STAR
ds .w de. 1 de. 1 ds.l ds.l
z % q ) - r * int ( z q )
* t=
* if
a ~ t > z=t else z=t+m
datas variables de de de
* endif
* ******* *
* pour
plus d'
'explications, voir st magazine n~45 page 108
LOG SCR_ADR: PHY SCR ADR:
RND_A
equ
$ 41a7
COPLIST_ADR:
RND_M
equ
$ 7ffffff
section
RND_Q
equ
$ f 31d
RND R
equ
$ 8bl4
ecranl ds.1
movem.
1 dl-d3,-(sp)
ecran2 ds. 1
move. 1
RND_Q,d0
coplist ds.1
move.1
RND Z,dl
end
moveq
0, d3
divu
dO, dl
bvc. S
.RESULT
move.1
dl,d2
clr.w
dl
swap
dl
(scr_size*2) 4 (scr_size*2) 4
1000*4 * taille inconnue, on prévoit gros
ecranl
ecran2
coplist
Par Jérôme Etienne
.POINT_NOT_ON_SCREEN
move.1 LOG_SCR_ADR,(al)+ dbf d7,.LOOP_EACH_STAR
rts
* *********************************************** END_AFF_STAR
* Ce tableau contient l'adresse de chaque ligne relative au début de 1'écran
ADR_LINE_ACORDING_TO_Y:
DUMMY SET 0
REPT BPL_Y
de. 1 DUMMY
DUMMY SET DUMMY+BPL_WIDTH * BPL_DEPTH
ENDR
INIT_TAB_STAR:
* *****************************************************
* This routine computes the coords X Y Z of each star
bsr AM_INIT_RANDOM
lea TAB_STAR(pc),aO
move.w NB_STAR-1, dl
.LOOP INIT STAR:
bsr
RANDOM * choose
X
at
random
and. 1
$ 3ff,dO
move.w
dO,(aO)+
bsr
RANDOM * choose
Y
at
random
and. 1
$ 3ff,dO
move.w
dO,(aO)+
bsr
RANDOM * choose
Z
at
random
and. 1
$ 3ff,dO
move.w
dO,(aO)+
dbf
dl,.LOOP_INIT_STAR
rts
* *******************************************
RANDOM:
* ***************************************************************
* OUT: dO = Random Number 24 bits
neg.w dO addq.w 7,d0
* display the point
* the color is chosen according to the Z coord.
* test if this color 1
cmp.w 1*1024 4+DELTA_Z,d2
bgt.S .NOT_COLOR_l
lea 3*BPL_WIDTH(aO),a0
move.l aO,(al)+ * save adr to clear point later
bset dO,(aO) dbf d7,.LOOP_EACH_STAR
rts .NOT_COLOR_l
* test for color 2
2*1024 4+DELTA_Z,d2 .NOT_COLOR_2 2 *BPL_WIDTH(aO),aO
a0,(al)+ * save adr to clear point later
dO,(aO)
d7, .LOOP_EACH_STAR
cmp.w bgt. S lea . Move.1 bset dbf rts
.NOT_COLOR_3
* color 4.
Move.1 bset dbf rts
* test for color 3
.RESULT
divu
dO, dl
move.w
dl, d3
move.w
d2, dl
divu
d0,dl
move.1
dl, dO
swap
dl
move.w
d3, dl
swap
dl
*
clr .w
dO
swap
dO
*
mulu
RND A,dO
*
move.w
dl, d3
swap
d3
mulu
RND R,d3
swap
d3
clr.w
d3
mulu
RND R,dl
add.l
dl, d3
sub. 1
d3,d0
move.1
dO,RND Z
movem.1
(sp)+,dl-d3
* dl= ql qO = int ( z q )
rts
BUILD_COPLIST:
move.1 COPLIST_ADR,aO
move.1
PHY SCR_ADR,d2
moveq
BPL_DEPTH-1,dO
move.w
bplpt,dl
.LOOP_INIT_BPL
_IN_CLIST
move.w
dl,(aO)+
addq.1
2, dl
swap
d2
move.w
d2,(aO)+
swap
d2
move.w
dl,(aO)+
addq.1
2, dl
move.w
d2,(aO)+
add.l
BPL WIDTH,d2
dbf
dO,.LOOP INIT BPL IN_CLIST
move.1
$ fffffffe,(a0)+* montre la fin de la clist
rts
* ********************************************************
AM_INI T_RANDOM :
* **********************************************************
* cette routine initialise la semence de RANDM
* Il est impérativement nécessaire de lancer cette routine avant
* de se servir de RANDOM sinon RANDOM donnera toujours la meme
* suite de chiffres a chaque fois que vous lancerez le programme
INIT_END:
lea
moveq 0,d0 *
CALLEXEC OpenLibrary *
move.1 dO,a4 *
move.l 38(a4),copllc(a5)
clr.w copjmpl(a5) *
move.w $ 83e0,dmacon(a5)
CALLEXEC Permit *
moveq 0,d0 *
rts
END_BUILD_COPLIST TAB_STAR:
LOG_TAB_ADR_POINT: PHY_TAB_ADR_POINT: TAB_ADR_POINT_1: TAB ADR POINT 2:
GfxName de .b EVEN end INIT END
"graphies.library",0
Ce mois-ci, quatre routinettes pour le prix d’une seule : l’ANT vous en donne toujours plus
Les Basics (GFA et AMOS) offrent un belle fonction : =Exist(), qui permet de savoir si un fichier existe réellement avant de le charger. Je vous propose donc aujourd’hui la version routinette de cette fonction : vous lui donnez un nom de fichier, et elle retourne vrai si ce fichier existe, ou faux s’il n’existe pas.
La routinette détourne pour un bref instant le requester du système : on peut ainsi demander la présence d’un nom de disque, ou d’un device. Ultra-pratique n’est-il pas ?
Plusieurs points d’entrée sont à votre disposition :
• _IntOpen : cette routine ouvre l’intuition.library en positionnant un flag (Wb2.0). Ce flag n’est à -1 que lorsque l’Amiga utilise le Workbench 2.0 (ou une version postérieure).
• _Exist : la fonction elle-même.
• _ReqOff : arrêt du system-request et passage en mode Forbid. Veillez à ne pas rester dans ce mode trop longtemps.
• _ReqOn : retour au requester normal et au mode Permit.
Pour appeler ces routinettes, vous devez comme d’habitude mettre l’adresse de la page de données en A5. L’exemple d’utilisation de ces routinettes est une nouvelle fonction CLI, recherchant le fichier (ou le disque) donné en argument. Le résultat est affiché dans la fenêtre courante.
On se retrouve le mois prochain pour de nouvelles routinettes, nettes.
* Copie la ligne de commande dans un buffer
subq.w 2,d0 ; Attention, une ligne de
; commande est terminée pas ; $ 0A, ne pas le copier !
.Err2
bmi
lea
Loop move.b dbra clr .b
Buffer-Dt(a5),al (aO)+,(al)+ dO,.Loop (aO ) +
* Ouvre intuition
bsr _IntOpen beq.s .Err2
* Ouvre le dos
lea
DosName-Dt(a5),al
moveq
0, dO
ExeCall
_LVOOpenLibrary
move.1
dO,DosBase-Dt(a5)

Une erreur
beq. S
.Errl

improbable !
* Appelle la fonction
lea
Buffer-Dt(a5),aO
bsr
_Exist
* Imprime la réponse sur le canal
standart
lea
Buffer-Dt(a5),al
lea
Existe-Dt(a5),aO
moveq
LExiste,d3
tst. 1
dO
bne. S
.Skip2
lea
ExistePas-Dt(a5), aO
moveq
LExistePas,d3
.Skip2 move.1
a0,d2
DosCall
_LVOOutput
Demande
le canal de
move.1
dO, dl
DosCall
_LVOWrite
* Ferme le dos
move.1
DosBase-Dt(a5), al
ExeCall
_LVOCloseLibrary
* ___
ROUTINETTES numéro 3
_Exist: recherche un fichier sans requester
_ReqOff:plus de requester
_ReqOn: requester de nouveau là
_IntOpen: ouvre intuition.library avec détection de
version
*
*
*
*
*
*
*
*k
* Par François Lionet, paru dans l'A.N.T. du 1er Février 1992
2______________________________________________________________
* ---------------------- Définition de variables système
* Appel du dos DosCall MACRO move.1 move.1 jsr
a6,-(sp)
DosBase-Dt(a5),a6 l(a6)
(sp)+,a6
move.1 ENDM
* Appel de fonction exec ExeCall MACRO
move.l a6,-(sp) move.l $ 4.w,a6 jsr l(a6)
move.l (sp)+,a6 ENDM
* Ferme intuition
.Errl move.l IntBase-Dt(a5), al ExeCall _LVOCLoseLibrary
* Sortie
.Err2 moveq 0,d0
rts
* ----------------------Routinette 1: =Exist
* Entrée
* A0= Nom du fichier
* Sortie
* - D0= Faux (0) si fichier inexistant
* D0= Vrai (-1) si fichier trouve
_Exist
movem.l dl aO-al,-(sp)
* Plus de requester
bsr _ReqOff
* Essai de faire un lock sur le fichier
move.l a0,dl DosCall _LVOLock move.1 dO, d2
* Remet le requeter, sans attendre
bsr _ReqOn
* Enleve le lock si il existe
move.1 d2,dl beq.s .NoLock
DosCall LVOUnLock
* Offsets des fonctions
dos
.NoLock
_LVOLock
equ
- 84
* Retourne la réponse
__LVOUnLock
equ
- 90
move.1 d2,dO
_LVOWrite
equ
- 48
beq.s .Fin
_LVOOutput
equ
- 60
moveq -l,d0 .Fin movem.l (sp)+,dl aO-al
* Offset des fonctions
EXEC
rts
_LVOOpenLibrary
equ
- 552
* ----------------------Routinette
_LVOCloseLibrary
equ
- 414
_LVOSetFunction
equ
- 420
* Entrée :
_LVOForbid
equ
- 132
* Néant
_LVOPermit
equ
- 138
* Sortie:
* Néant
* Offset des fonctions
intuition
*
_LVOAut oReque s t
equ
- $ 15c
_ReqOff
_LVOEasyRequest
equ
- $ 24c
movem.l aO-al dO-dl,-(sp)
* _______ _ __
Démon s t ra t i on
* Inhibe le multitâche
Start
ExeCall _LVOForbid
* Branche le faux requester
lea _ReqNul(pc),aO
bsr.s _SetReq
move.l dO,ReqSave-Dt(a5)
movem.l (sp)+,aO-al dO-dl rts
Faux requester: ramene toujours "CANCEL"
_ReqNul
0, dO
moveq
rts
Routinette 3: Remet le requester
Néant
Néant
* Entrée:
*
* Sortie:
*
*
_ReqOn
movem.l aO-al dO-dl,-(sp)
* Remet 1'ancien requester
move.1 ReqSave-Dt(a5), aO bsr.s _SetReq
* Re-autorise le multitâche.
ExeCall _LVOPermit
movem.l (sp)+,aO-al dO-dl rts
* ----------------------Routinette 4: Ouvre intuition
* en trouvant la version
* Entrée:
* Néant
* Sortie:
* DO= 0 si ouverture ratée
* DooO si réussie
*
_lntOpen
movem.l a0-al dl-d2,-(sp)
* Essai 1'ouverture de la version 2.0
moveq -l,d2
moveq 36,dO
lea intName-Dt(a5),al
ExeCall _LVOOpenLibrary
tst.l dO
bne.s .Skip
* Ca n'a pas marche: WB 1.3!
Moveq 0,d2 moveq 0,d0 lea IntName-Dt(a5),al ExeCall _LVOOpenLibrary
* Stocke le flag et l'adresse de base .Skip move.w d2,WB2 . 0-Dt (a5)
move.l dO,intBase-Dt(a5 ) movem.l (sp)+,a0-al dl-d2 rts
* ****** Routine interne: fait le changement de vecteur
* et retourne l'ancien...
_SetReq
move.l a0,d0 move.l IntBase-Dt(a5),al lea _LVOAutoRequest, aO
tst.w WB2.Q-Dt(a5) beq.s .Skip lea _LVOEasyRequest,aO
.Skip ExeCall _LVOSetFunction rts
C= Amiga NewsTech
* La base des accès à la zone de donnée Dt
WB2.0
de. W
0
ReqSave
de. 1
0
DosBase
de. 1
0
IntBase
de. 1
0
DosName
de .b
"dos.library",0
IntName
de .b
"intuition.library"
Existe
de .b
"Existe !",10
ExistePas
de .b
"N'existe pas !",10
Finito
de .b
0
Buffer
ds .b
128
Lexiste
equ
ExistePas-Existe
LexistePas
equ
Finito-ExistePas
even
DE LA BONNE VOLONTE, MAIS... PEUT MIEUX FAIRE !
Tonton François serait-il un adepte du vie! Adage : '‘pourquoi faire simple quand ou peuf faire compliqué’" t Four interdire les Requesters "FI case insert volume XXXXX: in any drive" de
FÀmigaDOS, iJ y a beaucoup plus simple. Et surtout, plus "Amiga
Il suffit en effet de jeter un oeil à la structure Process que tout programme, à l’exception des tâches simples créées par CreateTmkO ou équivalente, possède : a F offset "$ B8 (184 décimal) de cette Mlciure. Se trouve un champ nommé pr_WindowPtr, qui eontienl un Blbteur sur la fenêtre dans laquelle ÂmiguDOS affichera au besoin K Requesters. Ce champ contient normalement la valeur NOIX» indiquant que c’est la fenêtre (et donc l’écran) du Workbench qu'il faudra utiliser. Bu mettant ici F adresse d’une fenêtre à soi» on pourra faire en sorte que ces Requesters apparaissent dans le même écran que cette fenêtre, par exemple dans un Custom Screen.
Mais ie : plus intéres sant est. Qu’en initialisant pr„WindowFtr à -1,
fg|||jjj||j|g§!i
exactement comme si F utilisateur avait cliqué sur CanceL II faudra bien entendu pas oublier de remettre ce champ dans l’état dans lequel on Favait trouvé en arrivant (normalement NULL. Donc, mais on ne sait jamais : autant sauver la valeur originale quelque part).
L’avantage de cette méthode, outre que c’est celle recommandée par les Rom Kernel Manuals, est qu’on n’est pas obligé d’interdire le rnuîti-tâche et que les autres programmes essayant de cohabiter ne seront pas influences : leurs Requesters à eux apparaîtront bel et bien,,, À moins qu’ils n’aient eux aussi usé du même truc.
Max
NoReqÀus s i E t Plu s Propre « s Par Max pour Aœiga NewsTech
INCDïR «include:"
IMCLUDE «ex.ec exoc_iib. i*
INCLüDE . ”1ibrari e s dos.iw .
XNCL0DB ”llbraries dosextens.i«
INCL0DE «libraries Uos„lib.i«
; pour ceux qui aiment pas les includes : pr WindowFtr = $ B8
CALL MACRO
jST
_LVOU a6)
ENDM III
tlliilflBIll:
do sname(pc),al ; Ouvre la dos.1ibrary
moveq
movea.1
CALL
OpenLibrary
move.l
U0 d7 ; DOSBase dans d?
Beq. A
suba.1
al al ; Ou suis-je 7
CALL
FindTask
movea.1
dO, a4
move.l
pr„WindowPtr(a4),d6 ; d6 * WindowPtr
MOVeq
-I,d0
IllÉlÉ
dO,pr_WindowPtr(a4} ; WindowPtr - -1
lea III
name(pc),a0 ; nom ï 'PipiCaca:'
move.l
moveq
ACCESS READ,d2
movea.1
|||||6 .
CALL
Look ; Y''a peu de chance qu'un
move.1
dO,dl ; disque de ce nom existe.
Beq. S
.nolock mais on ne sait jamais !
CALL
UnLock
.nolock move.l d5,pr„WindowPtr (a4) ; Remet WindowPtr
movea.1
$ 4.w,al ; Referme la dos.library
exg
CALL
CloseLibrary
NoDos moveq
0,d0 ; That's ail folks !
Rts
dosname
dc. b «dos.library"r 0
even name dc.b
"PipiCaca: f,,0
even
END
Par Tonton François Lionet
¦
1 Numéro 30 -
FEV 92
Cher ANT, je vous écris pour vous poser quelques questions et vous faire part de quelques remarques. Tout d’abord, pourquoi n’y a-t-il pas plus de pages ? Manquez-vous de journalistes ? Si oui, pourquoi ne pas vous adresser à des gens comme Xavier Leclerc ?
Est-ce que des rubriques sur le GFA et le Pascal sont réellement nécessaires ? Le C, l’assembleur et même TAMOS ne couvrent-ils pas tous les besoins ? Par contre, une rubrique sur l’algorithmie est la bienvenue (enfin !).
Vous demandez l’avis des lecteurs sur les "carrés noirs". Si ça peut faire baisser le prix de la revue, ou augmenter le nombre de pages, ce n ’est que bénéfique !
Merci de votre attention, bon courage et bonne année !
Christophe Laratte, 87410 le Palais sur Vienne
Nous avons déjà expliqué ici et à plusieurs reprises pourquoi TANT ne fait que 32 pages : nous n’y reviendrons donc pas, le débat est clos. Et non, nous ne manquons pas de journalistes, bien au contraire.
Pascal et GFA sont deux langages de programmation de haut niveau disponibles sur l’Amiga. A ce titre, nous pensons qu’ils méritent leur place dans nos pages, même si le nombre de lecteurs intéressés est forcément moins élevé que celui des fanas du C, de l’assembleur et de l’AMOS. Bien sûr, et comme d’habitude dans TANT, c’est avant tout vous qui décidez de l’avenir du magazine : si vous n’aimez pas ces rubriques (ou d’autres !), écrivez-nous en masse, on les arrêtera illico. Quant aux "carrés noirs" (pour la pub), l’idée fait son chemin. Il est évident que si elle aboutit (c’est-à-dire, si des annonceurs sont intéressés - et ils seront triés sur le volet), cela entraînera une augmentation du nombre de pages.
Messieurs, dans le numéro 21 d’avril, vous donnez les explications pour installer le compilateur C DICE. Je me le suis donc procuré et l’ai installé : celà marche, sauf que je n’ai pas les include s Commodore. J’cd donc écrit à Commodore France pour les acquérir et vous joins une photocopie de leur réponse... C’est déjà bien, il y a une réponse, je devrais prendre cela comme un honneur. Malheureusement, je le prends mal.
Lorsque j’ai fait mon chèque pour mon Amiga 2000, extension mémoire, disque dur et imprimante, on ne m’a pas demandé mon statut de développeur. Mon argent était bon. Aujourd’hui, je veux développer avec un logiciel du domaine public, alors là non, il faut être reconnu comme développeur par Commodore, et être béni par eux.
Au fait, si je mets 2800 F dans le commerce pour acheter le Lattice, j’aurai peut-être les includes, mais je serai toujours aussi nul en C.
Bon, Commodore me dit qu’une version des includes existe sur une disquette Fred Fish. Sauriez-vous me dire laquelle ?
Dominique Marquaïlle, 02000 Etouvelles
Il faut savoir que les includes ne sont pas du domaine public ; Commodore possède un Copyright dessus, et ne les vend qu’accompagnés du pack développeur (RKM, outils de développement, etc.). Leur réponse est donc normale, même si elle vous semble amère. Désolé, je viens de parcourir les 530 premières disquettes Fish (merci Aquarium !) Et n’y ai pas trouvé d’autres includes que ceux fournis par Electronics Arts pour le format IFF (FF n°185). Si quelqu’un en a entendu parler, qu’ils nous écrive afin que tout le monde puisse en bénéficier.
Salut à tous ! Je vous écris pour signaler à Jérôme Etienne qu’un Modulo, c'est le reste d’une division, et qu’il est regrettable de voir une routine telle que ça (numéro 27, page 25, scroll "Kick Off") :
Alors que la vraie façon est
MOD:
MACRO

2,

EXT.L
DIVU
SWAP
ENDM
Je remarque aussi que l’auteur de cette routine devait être fatigué d’utiliser ses propres labels, puisqu à la fin du listing on a droit à :
MOVE. W 26*BPL_DEPTH*64+1,BLTSIZE(A5)
au lieu de :
MOVE. W BLK_HEIGHT*BPL_DEPTH*64+BLT_WIDTH 16, BLTSIZE(A5)
et ce à deux reprises, si bien que si l’on veut changer la taille d’un bloc :
BLK_WIDTH =32 BLT_HEIGHT = 32
tout l’affichage déconne !
Autre petit détail : LSL et LSR insèrent des 0 lors des décalages (contrairement à ASR et ROR), donc dans le petit morceau de programme suivant (extrait de la routine BUILD_COPLlSTdu numéro 26, page 11) :
LSL. W 8,D1
AND. W $ FF00,D1
le masquage AND ne sert à rien ! Votre journal ayant un but d’apprentissage, il est dommage d’apprendre d’aussi mauvaises habitudes (surtout dans une routine qui doit tourner à 50 Hz).
Dernière chose : "source" est un nom féminin, donc on n’écrit pas "un source" mais "une source". Et ne me répondez pas que c’est un terme technique et qu ’on en fait ce qu ’on veut !
Je pense qu’on pourrait ne plus voir pareilles erreurs de débutant dans votre journal, qui est par ailleurs fort bien conçu. Bye.
Misfit (les Félés)
Merci pour toutes ces précisions. Jérôme Etienne a copié 100 fois "le modulo est le reste de la division" et 200 fois ’Isl et lsr insèrent des 0". Il s'en souviendra.
"Source" est un nom féminin peut-être, mais lorsque l’on écrit "un source", on sous-entend "un fichier source" (puisque c’est l’expression exacte en infonnatique). Ce n’est pas un terme technique, c’est une abréviation. Et avant de donner des leçons de Français, vérifiez-donc votre propre orthographe...
Bon, ben voilà qui conclut notre Requester de ce mois. Je ne vous quitterai pas sans vous rappeler que cette rubrique est là pour (essayer de) répondre à tous vos problèmes de programmation, quels qu’ils soient. Pour les problèmes d’ordre pratique (imprimantes, quel disque dur choisir...), merci de plutôt adresser vos courrier à Amiga Revue, ils en feront bon usage. Evitez également de téléphoner, nous n’avons pas envie de passer nos journées suspendus au combiné !
Bonne année à tous !
MOD MACRO . MODULO_GO_ ON & :
CMP.W 2,
BLT. S . MODULO_IS_FINISH @
SUB.W 2,
BRA.S . MODULO_GO_ON &
. MODÜLO_IS_FINISH &
ENDM
Par Ok A Cancei
?
Règlement e t Proposition d ’Article
1. ) La rubrique Transactor de l’Amigci NewsTech est la rubrique écrite par les lecteurs, pour les lecteurs. Son but est de leur permettre de s’exprimer librement sur tout sujet qui les passionne, dans quelques langage de programmation que ce soit. Seule restriction : la Rédaction doit avoir la possibilité de tester le bon fonctionnement de tous les programmes fournis, complets ou simples routines d'illustration. Non mais.
2. ) Tout lecteur désirant participer à la rubrique Transactor devra envoyer son article sur disquette au format Amiga, en respectant les quotas définis aux
paragraphes 4 et 5. Les disquettes ne seront pas retournées, sauf demande expresse et écrite de l’auteur, et encore.
3. ) Tout article publié sera rémunéré au tarif forfaitaire de 1. 500 F HT les deux pages ; un article de plus de deux pages sera publié en plusieurs fois, à concurrence de trois épisodes maximum. Le paiement a lieu le mois suivant celui de la parution. Les auteurs des articles retenus pour parution seront directement prévenus par téléphone.
4. ) Les articles doivent parvenir au format ASCII standard (utilisez un éditeur de textes ou l’option de sauvegarde "text only'hou "ASCII" de votre traitement de texte). Les programmes illustrant les articles, qu’ils soient complets ou de simples routines d’exemple, doivent également être fournis sous forme ASCII, et éventuellement en format "brut" le cas échéant Basic, AMOS...). Le(s) fichier(s) exécutable(s) doivent se trouver sur la disquette. L’auteur doit fournir à TANT la possibilité de recompiler ou d’interpréter ses programmes sans difficulté (joindre au besoin une version "run-only" du langage utilisé).
5. ) La taille totale du fichier ASCII donne le nombre de signes de votre article ; une page de F ANT contient en moyenne 7, 000 signes de texte. Une colonne de listing contient 80 lignes de 65 caractères de large. Un programme de 160 lignes occupe donc une page entière. Les programmes en assembleur particulièrement longs peuvent être publiés en trois colonnes de 40 caractères chaque (240 lignes par page).
6. ) Toute proposition d’article pour Transactor devra être accompagnée du bon ci-dessous, découpé ou photocopié, mais en aucun cas reproduit à la main.
7. ) Toute proposition d’article pour Transactor entraîne l’acceptation par l’auteur que les programmes joints soient placés sur la disquette d’accompagnement de l’ANT. L’auteur doit préciser dans des commentaires en tête de listing s’il place son programme dans le Domaine Public ou s’il désire conserver son copyright. Aucun commentaire implique la mise en Domaine Public.
8. ) Ni TANT, ni Commodore Revue SARL, ni les auteurs collaborant à la rubrique Transactor ne sauraient être tenus pour responsables en cas de dommages quelconques survenus à l’ordinateur, suite à une mauvaise utilisation des documents (textes et programmes) publiés dans cette rubrique.
9. ) Toute proposition d’article pour Transactor entraîne l’entière acceptation par l’auteur de ce règlement, et sans discuter.
Abonnement à L’AfhPT et Anciens Numéros
C’est nouveau, ça vient de sortir : dorénavant, si vous êtes déjà abonné, vous pouvez parrainer un nouveau venu dans la grande famille de TANT et gagner des cadeaux offerts par CIS.
Si votre abonnement arrive à échéance, n’oubliez pas de le renouveler le plus tôt possible. Ce serait quand même dommage de rater un ou plusieurs numéros !
Ca aussi c’est nouveau, vous pouvez commander les anciens numéros de l’ANT à la pièce, afin de compléter votre collection. Tout est indiqué dans le bon de commande ci-dessous.
Bon de commande à découper ou photocopier (pas de recopie manuelle SVP) et à retourner correctement rempli et accompagné de votre règlement à MCM-ANT) 16, Quai Jean-Baptiste Clément, 94140 Alfortville, France. Merci de libeller vos chèques à l’ordre de Commodore Revue. Etranger : paiement par mandat international uniquement..
• OUI, je m’abonne à Amiga NewsTech. Je choisis la formule :
? 1 an (11 numéros) sans disquette 350 FF
? 1 an (11 numéros) avec disquette 600 FF
? (cochez cette case pour un ré-abonnement)
• Je commande les anciens numéros suivants :
? N°20 (? Avec disquette) ? N°25
(? Avec disquette)
(? Avec disquette)
(? Avec disquette)
(? Avec disquette)
(? Avec disquette)
pour un total de F.
? N°21 (? Avec disquette) ? N°26
? N°22 (? Avec disquette) ? N°27
? N°23 (? Avec disquette) ? N°28
ü n°24 (? Avec disquette) ? N°29
au tarif unitaire de 45 F (60 F avec disquette),
Nom :
Prénom :
Adresse :
Code Postal :
Ville :
Pays :
1
On récupéré les listes coppers dans une banque Reserve As Work 10,11*1024
2
Adresse de la base des données lea Dt(pc),a5
font-family:Times New Roman, serif;"> Merci de libeller vos chèques à l’ordre de Commodore Revue. Etranger : paiement par mandat international uniquement..
• OUI, je m’abonne à Amiga NewsTech. Je choisis la formule :
? 1 an (11 numéros) sans disquette 350 FF
? 1 an (11 numéros) avec disquette 600 FF
? (cochez cette case pour un ré-abonnement)
• Je commande les anciens numéros suivants :
? N°20 (? Avec disquette) ? N°25
(? Avec disquette)
(? Avec disquette)
(? Avec disquette)
(? Avec disquette)
(? Avec disquette)
pour un total de F.
? N°21 (? Avec disquette) ? N°26
? N°22 (? Avec disquette) ? N°27
? N°23 (? Avec disquette) ? N°28
ü n°24 (? Avec disquette) ? N°29
au tarif unitaire de 45 F (60 F avec disquette),
Nom :
Prénom :
Adresse :
Code Postal :
Ville :
Pays :
1
On récupéré les listes coppers dans une banque Reserve As Work 10,11*1024
2
Adresse de la base des données lea Dt(pc),a5

Click image to download PDF

AMIGA NEWS TECH numero 30 (02-1992)

Merci pour votre aide à l'agrandissement d'Amigaland.com !


Thanks for you help to extend Amigaland.com !
frdanlenfideelhuitjanoplptroruessvtr

Connexion

Pub+

63.2% 
8.7% 
3% 
2.1% 
1.9% 
1.6% 
1.2% 
1.1% 
0.8% 
0.8% 

Today: 108
Yesterday: 94
This Week: 405
Last Week: 863
This Month: 2250
Last Month: 3511
Total: 84464

Information cookies

Cookies are short reports that are sent and stored on the hard drive of the user's computer through your browser when it connects to a web. Cookies can be used to collect and store user data while connected to provide you the requested services and sometimes tend not to keep. Cookies can be themselves or others.

There are several types of cookies:

  • Technical cookies that facilitate user navigation and use of the various options or services offered by the web as identify the session, allow access to certain areas, facilitate orders, purchases, filling out forms, registration, security, facilitating functionalities (videos, social networks, etc..).
  • Customization cookies that allow users to access services according to their preferences (language, browser, configuration, etc..).
  • Analytical cookies which allow anonymous analysis of the behavior of web users and allow to measure user activity and develop navigation profiles in order to improve the websites.

So when you access our website, in compliance with Article 22 of Law 34/2002 of the Information Society Services, in the analytical cookies treatment, we have requested your consent to their use. All of this is to improve our services. We use Google Analytics to collect anonymous statistical information such as the number of visitors to our site. Cookies added by Google Analytics are governed by the privacy policies of Google Analytics. If you want you can disable cookies from Google Analytics.

However, please note that you can enable or disable cookies by following the instructions of your browser.

Visitors

Visite depuis
03-10-2004
Visite depuis
23-02-2014