Sponsors

FacebookTwitterGoogle Bookmarks

La rubrique Transactor de l’Amiga 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. La 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 ou 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 à l’ANT 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 l’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).

Click image to download PDF

AMIGA NEWS TECH numero 27 (11-1991)

Document sans nom TRANSACTOR
E D I T O
Vous avez vu ? Ce numéro est en retard. Et pour une fois, on ne pourra accuser ni les PTT) ni le mauvais temps, ni même le destin. Non, nous avons tout simplement tout changé à notre système de PAO, passant de Professional Page à Publishing Partner Master. Résultat des courses, on a lamentablement merdoyé avant de réussir à sortir quelque chose d’imprimable. Toutes nos excuses donc, ça ne devrait plus se reproduire. On maîtrise.
Hop, j’en profite rapidement pour caser deux annonces.
La première pour vous rappeler que nous recherchons toujours des collaborateurs, avis aux amateurs. Téléphonez-nous pour prendre contact.
La seconde pour vous signaler que si vous avez envie de voir une de vos oeuvres en couverture de TANT, c’est possible. Là encore, il suffit de nous passer un petit coup de bigophone et on s’arrangera.
A part ça, hé ben ça va, s ’il s ’passe quek’chose, on vous l’dira.
Stéphane Schreiber
1. ) La rubrique Transactor de l’Amiga 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. La 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 ou 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 à l’ANT 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 l’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.
8. ) Ni l’ANT, 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.
PROPOSITION D’ARTICLE
A photocopier et à retourner à :
Amiga NewsTech (Transactor), 5, rue de la Fidélité, 75010 Paris, France
Je soussigné,
Nom ......
Prénom .
Adresse .
Code Postal .Ville.
Pays.
N° de téléphone .
déclare être l’auteur de l’article et du (des) programme(s) ci-joints. Je désire les soumettre à l’appréciation de la rédaction de l’Amiga NewsTech, pour une éventuelle publication dans le cadre de la rubrique Transactor.
Sujet de F article ..
Taille du fichier texte ....signes
Taille du (des) programmes(s) ......lignes
Je déclare avoir pleinement pris connaissance du règlement ci-dessus et l’approuver entièrement.
Signature (précédée de la mention "lu et approuvé") :
OURS
Amigu NrwsTcdx est une publication nien.sud(e et scnduc ¦uniquement par abonnement, de Commodore Revue SARI,, 5,
xîrHqucineïU par m
rue de la Fidélité,
75810 Paris.
ABONNEMENTS A L’ANT
5. Rue de la Fidélité, 75010 Paris.
Tel : fi ) 42.46.92.90. Fax : m 47.70.08.2i.
Directeur de ta publication : Jean-Y es Primas Rédacteur en chef : Stéphane Sehrcü>er Rédacteur en 'chef adjoint : Stéphane Schreiber Adjoint du red’ehef adjoint : Stéphane Schreiber Premier Secrétaire de réduction : Stéphane Schreiber Secrétaire de réduction : Stéphane Schreiber Chef de rubrique : Stéphane Schreiber A collaboré à ce numéro : Stéphane Schreiber
Flashage par ColorWav, 5, rue de la Fidélité, 75010 Paris.
Tel : (1) 47.70.87.87. fax : (F) 48.24.97.77
Imprimé par ÏNO-Montrouge, 9,
,92120?
Tel :( ) 46.56.27.69
Chef de fabrication : Stéphane Schreiber
Premier moqueUiste : Stéphane Schreiber
PAO : Stéphane Schreiber
Support technique. YvesHuttric
Support moral : Get 27, Vodka Eristoiï. Lucky Strike
Support phssique : 3615 t hla
Chef Je publicité : Stéphane Sdirciber Assistant : Stéphane Schreiber
¦ Stéphane Schreiber
Dessous de
s et futurs numéros
fcfeparMCM Europe. 16
04140 Alfurtville.Tel : (t j43.78.92.10. Fax : H)41.78.99.89.
L'intelligence, c’eut pas, m s'écrase. (Pierre Desprov.es 1
C’est nouveau, c’est pas encore sorti : à partir du mois prochain, si vous êtes déjà abonné, vous pourrez parrainer un nouveau venu dans la grande famille et gagner des cadeaux offerts par CIS. Je ne veux pas gâcher la surprise, vous en saurez plus en temps voulu.
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 par chèque bancaire ou postal libellé à l’odre de à :
MCM Europe, 16, Quai Jean-Baptiste Clément, 94140 Alfortville, France
Je m’abonne h. Amiga NewsTech. Je choisis la formule
? 1 an (11 numéros) sans disquette 350 FF
? 1 an (11 numéros) avec disquette 600 FF
I I (cochez cette case pour un ré-abonnement)
Je commande les anciens numéros suivants
[J n°20 Q n°21 Q n°22 Q n°23 Q n°24 Q n°25 Q n°26
au tarif unitaire de 45 F, pour un total de ..F.
Nom ...Prénom.
Adresse
Code Postal .....Ville ...
Pays.
I
La technique de programmation par "overlays" permet de disposer d’une application de plusieurs centaines de kilo-octets sur disque mais en utilisant beaucoup moins en mémoire centrale.
Comment ça marche ?
OVERLAYS|r | ?
Routines de l’Overlay 2 ainsi que celles de la Racine et des
Overlays 3 et 4.
• toutes les routines de l’Overlay 3 peuvent appeler d’autres
routines de l’Overlay 3 ainsi que celles de la Racine et des
Overlays 2 et 5.
• etc...
On le voit, deux noeuds de même niveau sont mutuellement exclusifs : l’Overlay 1 ne peut pas appeler des routines de
l’Overlay 2.
C’est très simple, tout du moins dans la théorie : on ne charge les routines en mémoire qu’au fur et à mesure de leur utilisation, et on les libère lorsqu’elles se terminent. Vous vous doutez certainement que pratiquement, c’est plus cimpliqué : une routine du programme principal doit s’occuper de libérer et de charger les nouveaux overlays au moment opportun. Fort heureusement, le compilateur Lattice Le et son linker associé Blink réduisent au strict minimum le travail du programmeur : celui-ci n’a plus qu’à simplement prévoir quelles fonctions de son application seront ou ne seront pas des overlays. Avouez que c’est déjà quand même plus simple.
Une représentation classique des overlays est sous forme d’arborescence, comme dans la figure 1 ci-dessous.
1
tacine
1
Overlay 1
Over
ay 2
Over
ay 3
H
Overlay 4
Over
ay 5
La Racine est le programme principal, le premier à être chargé. Quoiqu’il arrive, il réside toujours en la mémoire et ne pourra pas en être viré par le gestionnaire d’overlays.
Les Overlay 1 et Overlay 2 s’exécutent indépendement l’un de l’autre, un seul à la fois : le programme principal ne peut pas appeler de fonctions de l’Overlay 1 puis de l’Overlay 2 sans que le gestionnaire ne le charge d’abord. De cet arbre, quelques règles simplissimes découlent :
• si l’Overlay 1 est chargé, les 2, 3, 4 et 5 ne le sont pas.
• si l’Overlay 2 est chargé, le 1 ne l’est pas, mais 3, 4 et 5 peuvent l’être (ce n’est pas obligatoire).
• si l’Overlay 3 est chargé, le 2 l’est aussi, les 1 et 4 ne le sont pas, et le 5 peut l’être.
• si l’Overlay 4 est chargé, les 1, 3 et 5 ne le sont pas.
• si l’Overlay 5 est chargé, les 2 et 3 aussi, mais les 1 et 4 ne le
sont pas.
Malheureusement, quelques restrictions existent :
• la Racine peut appeler des routines de n’importe lequel des cinq overlays.
• les 5 overlays peuvent appeler des routines de la racine.
• toutes les routines de l’Overlay 1 peuvent appeler d’autres
routines de l’Overlay 1 ainsi que toutes celles de la Racine.
• toutes les routines de l’Overlay 2 peuvent appeler d’autres
LES OVERLAYS, POUR QUI ?
Le mécanisme des overlays n’est pas à utiliser n’importe quand. On le réserve en général pour les applications réellement énormes (plus de 100 Ko).
Imaginons par exemple que vous écriviez un programme de traitement de texte. Le programme principal - la Racine - contiendra toutes les routines d’entrée de caractères, d’éditions, de manipulations de bloc... Un premier overlay pourrait contenir les routines d’impression, qui n’ont pas besoin d’être chargées en permanence. Un second overlay pourrait s’occuper de l’impression en PostScript, qui est totalement différente de l’impression matricielle ou laser (on pourrait également partager le premier overlay en deux, l’un pour PostScript, l’autre pour les matricielles). Enfin, un troisième overlay pourrait gérer l’analyse du document (comptage du nombre de mots, de caractères, repérage des répétitions, histogrammes...). C’est uniquement au programmeur de décider de tout cela.
LES OVERLAY, COMMENT ?
C’est Blink qui se charge de presque tout, lors de la phase
d’éditions de liens du programme définitif : il remplace chaque
appel d’une routine appartenant à un noeud différent de celui en cours par un saut au gestionnaire d’overlays. Celui-ci décide alors,
en fonction du niveau des deux noeuds concernés dans
l’arborescence, s’il doit d’abord charger quelque chose depuis le disque ou non. Le gestionnaire est inclus dans les bibliothèques du Lattice, mais on peut au besoin le remplacer par un fait maison. Ce qui ne présente pas réellement beaucoup d’intérêt.
Blink utilise un fichier WITH spécial dans sa ligne de commande qui décrit l’arborsence des overlays. Le format de ce fichier est strictement défini et doit être respecté à la lettre. L’arbre de la figure 1 pourrait être décrit dans le fichier suivant :
OVERLAY
Overlayl
Overlay2
* Overlay3
* *Overlay5
* Overlay4

Le mot-clé OVERLAY introduit le premier niveau de l’arborescence et le signe dièse ( ) termine la liste. La Racine n’est pas incluse : Blink utilise comme racine le premier fichier objet de sa ligne de commande (le fichier FROM ou ROOT). Une astérisque introduit un niveau supplémentaire d’overlay, pas d’astérisque du tout correspondant au niveau 1. Remarquez enfin que l’Overlay 5 a été placé tout de suite derrière le 3, dont il dépend dans l’arbre.
Bonjour les z’amis, aujourd’hui je m’en vais vous proposer un programme d’évaluation d’expressions. Holà, ça va être rasoir ça. Je vous vois déjà tourner la page pour aller lire les excellents jeux de mots de Max...
UN INTERPRETEUR D’EX
on, restez, ce que je vous propose est complètement hypra-dément (excusez du peu). Parmi les 10,385,129 questions posées sur AMOS à ce jour, une revient très souvent (10,385,128 fois exactement), je cite : "Pouvez- vous mettre une fonction EVAL, pour que je puisse faire de courbes ?". Ma réponse étant invariablement, je cite : "NON!".
Un traceur également
Le rôle d’une telle fonction est de permettre à l’utilisateur d’entrer une équation, et de pouvoir la calculer à l’intérieur du programme.
C’est très pénible à réaliser dans un interpréteur : la fonction EVAL fait appel à des routines de l’éditeur, se trouvant à un niveau totalement différent de celui de l’interpréteur. De plus, la compilation d’une telle fonction est tout-à-fait impossible.
La seule solution pour programmer un traceur de courbe est de réaliser une fonction EVAL en AMOS. C’est ce que je vous propose aujourd’hui.
LE PRINCIPE
Le principe du programme que je propose, est totalement pompé sur les routines d’évaluation d’expressions de l’AMOS et du compilateur. Je vais me gêner, tiens ! Vous allez voir que c’est complètement récursif : les procédures n’arrêtent pas de s’appeler l’une-l’autre.
Il faut, pour tracer une courbe sur un écran HIRES, calculer la même expression au moins 640 fois. Le programme a donc intérêt à être rapide, pour ne pas finir à la poubelle. Or, explorer une chaîne de caractères n’est pas des plus performant. C’est pourquoi le programme d’aujourd’hui se divise en deux parties :
• analyse de la chaîne de caractères et fabrication d’une liste des fonctions à appeler pour le calcul. Cette partie, la plus lente, s’appelle la tokenisation (transformation en "tokens"). Elle n’est effectuée qu’une fois.
• suit une petite boucle d’initialisation de variables internes, à titre d’exemple.
• la boucle principale du programme se contente de demander une expression et d’afficher le résultat.
LA TOKENISATION
Nous n’allons pas trop nous éterniser dessus. Elle est constituée de quatre procédures :
• Procédure _TOKENlSE[a$ ] : c’est la procédure à appeler pour lancer la tokenisation. Elle initialise les variables, et appelle la procédure suivante.
• Procédure _TEVALUE : se charge de digérer une expression entière, comme "1+1". Elle peut être appelée plusieurs fois, pour les expressions entre parenthèses (quand je vous disais que c’était récursif !).
• Procédure _TOPERAND : se charge d’appréhender un opérande, un chiffre ou une fonction. Dans le dernier cas, elle appelle la procédure _TEVALLJE pour récolter les paramètres, etc.
• Procédure CHAR : ramène les lettres de la chaîne à tokeniser, une par une.
Donc, il suffit d’appeler _TOKENISE["Expression”] pour faire la première partie du travail. En sortie, la variable SYNT vous indique la présence d’une erreur de syntaxe dans l’expression. Dans ce cas, vous ne devez évidemment pas appeler les routines de calcul !
Le calcul
Beaucoup plus courte, cette partie n’est constituée que de deux procédures :
• Procédure EVALUE : point d’entrée d’un calcul, cette procédure est également appelée par la suivante. Le résultat du calcul se trouve dans la variable réservée Param .
• Procédure EVAL1T : une petite boucle explore le tableau PILE$ , contenant les noms des fonctions à appeler. Chaque fonction fait son travail et revient à la boucle.
• les opérateurs (PLUS, MINS, DIVD, TIME, POWR) prennent les valeurs sur la pile PP (), effectuent leur opération, et empilent le résultat.
• les fonctions appellent la routine d’évaluation pour récupérer les paramètres, puis poussent le résultat sur la pile. Seule exception, la fonction =PI.
LE PROGRAMME
Comme aujourd’hui je veux vous faire comprendre le principe de l’évaluation, hé bien, je garde le traceur de courbes pour le mois prochain.
Initialisation
• la variable MXE contient la taille maximum des expressions. Chaque élément d’une expression compte pour une unité. Ainsi, 1+1 prend trois positions dans les tableaux.
• suit la définition des variables. PILE(), PILE () et PILE$ () sont les tableaux importants, contenant les noms des routines à appeler.
• la variable OPE$ contient les opérateurs disponibles. Voir plus loin pour rajouter vos propres opérateurs.
• la variable FUN$ contient, elle, les fonctions disponibles. Voir également plus loin pour ajouter vos propres fonctions.
FONCTIONNEMENT
Le programme affiche les noms des fonctions appelées par la procédure EVALIT. Pour mieux comprendre, voici quelques expressions et la liste des fonctions appelées pour chacune.
1
pousser la valeur 1 sur la pile (le paramètre n'est pas utilisé)
CONST END 0
4 2+3>
CONST 4 CONST 2 DIVD 0
pousse 4 dans la pile pousse 2 dans la pile
(paramètre non utilisé), divise les deux derniers chiffres de la pile, ici 4 2, et pousse le résultat. La pile contient donc 2
CONST 3 : pousse 3 dans la pile
FUN$ =FUN$ +" SQR(}SQRRl(LOG(}LOGG1 EXP(}EXPPl LN()LNNNl"
FUN$ =FUN$ + ” ABS (} ABSS1 (INT ( } INTTl SGN (} INTT1 RND (}RNDDl "
' Example de variables internes Dim VAR (10,10)
Global VAR ()
For A=0 To 10 For B=0 To 10
A$ =Str$ (A)+"."+Str$ (B)
VAR (A,B)=Val(A$ )
Next Next
r
' Petite boucle de tests Do
Cls
Print "Evaluation d'expression en AMOS."
Print
Line Input "Entrez une expression S.V.P :";A$
If A$ ="" : Exit : End If
' Va tokeniser TOKENISE[A$ ]

' Calcule le résultat Print
If Not SYNT EVALUE
Print "Résultat :" Param

' Pour voir le procédé de calcul Print
For N=0 To PPILE
Print PILE$ (N); PILE (N)
Next Else
Print "Syntax error!"
End If
Print
Print "Pressez une touche..." : Wait Key Loop End
PLUS : additionne 3 et 2
END : le résultat est donc 5
La logique de calcul utilisée ici est donc la logique polonaise inverse, que tous les utilisateurs de calculatrices HP connaissent bien. Plus compliqué :
SQR 2)+3
SQRR 4 : le paramètre n'a pas d'importance ici. La
routine SQRR appelle EVALUE, pour récupérer le paramètre CONST 2 : pousse le 2 de la racine carrée
END 0 : fin de l'évaluation du paramètre, on
revient à la fonction SQR, qui pousse le résultat dans la pile CONST 3 : pousse le 3
PLUS 0 : fait l'addition finale
END 0 : fin de l'expression
AJOUTER DES FONCTIONS
Il est très facile d’ajouter ses fonctions et ses opérateurs.
Par exemple, ajoutons l’opérateur Modulo, qui sera codé par le caractère tilde Nous devons modifier la chaîne OPE$ : celle- ci est classée en ordre inverse des priorités. MOD ayant une priorité basse, nous devons le mettre en premier dans la liste.
OPE$ ="~MODU+plus...
Vous devez maintenant programmer la fonction dans la procédure EVALIT :
_MODU: Dec PP : PP (PP)=PP (PP) Mod PP (PP) : Return
Par la même méthode, on peut ajouter des fonctions. On va mettre la fonction HSIN().
FUN$ =FUN$ +" HSIN}HSINI"
Entre les accolades } se trouve le nom de l’instruction. Vient ensuite le nom du label de la routine, sur quatre lettres. Le chiffre qui suit donne le nombre de paramètres à récupérer.
' PROCEDURES DE TOKENISATION
f
Procédure _TOKENISE[A$ ]
EV$ =Upper$ (A$ ) : PEV=1 ; LEV=Len(EV$ ) : PPILE=0 : SYNT=0
_ TEVALUE[0] : PILE$ (PPILE)="_ENDn : Inc PPILE
If NPAR : SYNT=-1 : End If End Proc[SYNT]
f
Procédure _TEVALUE[N]
If SYNT : Pop Proc : End If P=0 : NPAR=N Do
Inc PP : PP(PP)=P Do
_TOPERAND DO
CHAR : P=Instr(OPE$ ,CHAR$ )
Exit If P>PP(PP),2 Dec PEV .- P=PP(PP) : Dec PP Exit If P=0,3
PILE$ (PPILE)="_"+Mid$ (OPE$ ,P+l,4) : Inc PPILE
Loop Loop Loop
If CHAR$ =")n : Dec NPAR : Inc PEV : End If End Proc[RES]
Et la routine de traitement :
_HSIN: EVALIT : PP (PP)=Hsin(PP (PP)) : Return
Voila, c’est tout pour aujourd’hui, j’ai déjà dépassé la taille autorisée par mon rédacteur en chef adoré (sur tranche). Le mois prochain, Daisy fera le traceur de courbes, et moi je parlerai d’autre chose. A plus !
Fsuite page 31
Ami g a N e w s T e c hWïWNuméro 27 - NOV 91
Dans le chapitre précédent, nous avons écrit et expliqué le premier module de notre projet d’édition du catalogue du disque de notre choix. Nous nous attaquons aujourd’hui au second module.
Rappelons que ce premier module permettait d’obtenir de manière quasi-certaine, c’est-à-dire sans risque d’erreur, le nom du disque dont on désire obtenir le catalogue, ce nom pouvant être logique (par exemple, Arexx:) ou bien physique (par exemple, DHO:). Mais afin de permettre l’interfaçage entre les deux modules, nous proposons une petite, toute petite, modification. Il s’agit d’ajouter juste avant l’instruction EXIT les deux instructions PUSH suivantes :
PUSH device
PUSH 'rx ram :Traitement'
EXIT * Ligne 48 du module précédent *
Ces deux instructions "poussent" dans la pile de la console, d’une part la variable Arexx device (qui contient la définition du disque choisi) et d’autre part, une instruction du type CLI, Shell ou ConMan.
Voici alors ce qui va se passer (on supposera que le nom du programme décrit ci-après est Traitement et qu’il est placé en RAM:).
Il faut d’abord lancer le premier programme, qui va obtenir le nom légal du disque à analyser. A la fin de ce programme, juste avant EXIT, ces deux instructions vont pousser dans la pile le nom du disque à analyser et celui du programme qui devra le faire. Notons en passant que ces deux instructions appartiennent chacune à des mondes différents. Dès la sortie par EXIT, la main revient à l’hôte, ici la console. L’instruction rx ram:Traitement, insérée en dernier par PUSH, est celle qui va être rappelée la première (différence entre PUSH et QUEUE). C’est une instruction légale pour la console, elle lance donc le programme Arexx Traitement.
Dans ce programme qui prend alors la main, la première instruction est PULL nomvariable ; cette instruction extrait de la pile celle qui reste, à savoir le nom du disque à traiter. Voici donc une particularité intéressante qui permet de passer à peu près n’importe quoi d’un programme à un autre, sans égard pour la différence des types de programme.
PROGRAMME TRAITEMENT
Ce programme permet d’obtenir le répertoire du disque précédemment défini avec la longueur de chaque fichier. Voici son mécanisme :
• en utilisant la fonction dir opt a du DOS, création d’un fichier en RAM: nommé repertoire.
• analyse ligne par ligne de ce fichier et obtention de la taille de chaque entrée.
• création d’un fichier en RAM: nommé resul comprenant les indications nécessaires au prochain programme pour afficher notre forme personnelle du répertoire.
Note : on aurait pu utiliser les fonctions spécialisées d’ARexx pour obtenir le répertoire désiré (en l’occurence, la fonction DIR). Nous avons bien précisé que nous ne cherchions qu’à montrer des fonctionnements et des mécanismes !
Pour analyser le fichier placé en RAM, il faut en connaître la structure. C’est un fichier texte, dans lequel chaque ligne peut
RATIQUE (II)
contenir soit un nom de répertoire, soit deux noms de fichiers. Le décalage des répertoires entre eux, selon leur rang, est de 5 caractères, le premier étant à 3 caractères de la marge gauche. Les fichiers d’un répertoire sont décalés de 2 caractères à droite.
.
C’est le calcul de la position du répertoire qui donne son rang et les fichiers qui lui sont affectés. Il faut donc retenir les différentes positions pour affecter correctement les fichiers aux répertoires, surtout en revenant d’un emboîtement de répertoires ! Les fichiers, eux, sont placés à raison de 2 par ligne, lorsque le nombre est pair. Dans le cas contraire il n’y en a qu’un. Un intitulé de fichier peut contenir des espaces ce qui leurre Arexx, qui l’interprète comme un séparateur. En conséquence, l’extraction des fichiers se fera par position, car elle reste heureusement constante.
Enfin, le fichier résultat aura une forme particulière. Ce sera une chaîne de caractères contenant toutes les informations nécessaires, concaténées sans espaces et avec des séparateurs spécifiques. Cette chaîne sera construite par concaténations consécutives des valeurs utiles dès leur obtention.
Avertissement : une erreur de programmation initiale fait que la chaîne finale n’a pas tout-à-fait la régularité souhaitée. Au moment d’effectuer la correction, nous avons pensé tirer parti de la chose en transformant un bug en feature ! En effet, dans le programme suivant, nous verrons comment filtrer et corriger l’erreur. Ceci pour montrer la souplesse des programmes qui peuvent traiter des logiciels extérieurs sans s’occuper vraiment de la logique matérielle de ces programmes.
Ce programme fait partie d’un tout. On peut toutefois le lancer seul (par exemple, rx ram traitement) ; l’intruction PULL en tête attend alors un argument qui aurait été placé dans la pile par le programme précédent. Comme il n’y est pas, il faut le fournir à partir du clavier. Par exemple, DF1: et return.
LISTING DU PROGRAMME
Ce programme est listé ci-contre. Rappelons que les numéros de lignes n’ont été mis que pour faciliter la lecture ; il ne faut pas les taper lors de la recopie du programme. Les explications nécessaires ont été directement incluses dans le listing, mais il semble toutefois utile d’apporter quelques compléments.
Certaines lignes de programme sont trop longues pour la largeur de la page de l’éditeur utilisé. Dans ce cas, il est possible de couper la ligne sans affecter son intégrité logique, en plaçant une virgule à l’endroit de la coupure, suivie bien sûr d’un retour- chariot.
La création d’une chaîne de caractères continue pour matérialiser le résultat est totalement arbitraire. Mais cela représente une bonne introduction à la lecture de chaînes qui n’ont pas de sens ASCII, c’est-à-dire du code ou tout autre chose, dans lesquelles la notion de ligne n’a pas de sens. C’est un des aspects sympathiques et uniques d’ARexx de traiter uniquement des caractères.
Dans ce programme, nous voyons comment nous pouvons d’une part acquérir des noms de répertoires ou de fichiers, les traiter, mélanger, construire et leur faire subir tout ce qu’on peut effectuer sur des chaînes de caractères et d’autre part, pourvu qu’ils aient une forme légale, les utiliser comme commandes par exemple pour changer de répertoire !
Enfin pour passer des éléments : variables, chaînes, valeurs, tableaux, commandes, etc., nous avions utilisé et nous utilisons encore cette fois-ci, le passage par la pile. Cependant, nous avons ici utilisé concuremment une autre méthode qui s’apparente aux POKE et PEEK du Basic, avec cette différence essentielle toutefois que l’opération se fait au travers de l’allocateur général de l’Amiga et qu’en conséquence, il n’y a pas de risque d’écraser
un espace occupé par quelqu’un d’autre, non plus que de risquer pour soi les mêmes déboires. Dans le présent programme, nous n’avons pas pris la précaution de vérifier que l’allocation avait été correctement effectuée en regardant le RC (pour Return Code), ce qui est imprudent en milieu multitâche.
Cette allocation survient à la fin du programme demandeur d’allocation. Cela impose de gérer la désallocation lorsque il n’est plus utile de conserver cet espace. L’instruction est FREESPACE(adresse,taille), d’où la nécessité de passer ces valeurs aux programmes utilisateurs.
Fichierl=STRIP(SUBSTR(ligne,pos,32))
64 if STRIP(ligne) ~= " THEN fichier2=STRIP(SUBSTR(ligne,pos+33,32))
65
66
67
68
69
70
71
72
IF pos posancien THEN DO *fin de la boucle DO ligne 82* posit=(pos-3)%5 dir=disque
IF posit ~=0 THEN DO i=l TO posit *fin de la boucle DO ligne 74*
73 dir=dir|Idir.iII' ' * la concaténation sans espace est
due à: 74
I I * END
*début de la boucle principale qui se termine en ligne 37 *
1 *TRAITEMENT*
2 *call trace i r* *pour la mise au point uniquement*
3 PULL disque *extrait le nom du disque de la pile*
4 x=PRAGMA('D',disque) *installe le répertoire courant* ?commande CLI pour mettre le sous forme de fichier texte*
5 ADDRESS COMMAND 'dir > ram:repertoire opt a'
6 x=OPEN('lec', 'ram:repertoire', 'R' ) *ouvre le fichier*
7 x=OPEN 'ecr ','ram:resul','W' ) *création du fichier résultat *
8 posancien=l *préparation de variables *
9 soustotal=0 *idem*
10 total=0 *idem*
11 taquet=0 *idem*
?préparation d'une ligne de taille déterminée chaine blanche*
12 d='
13 file=STRIP('| 'disque' |') *obtention d'une chaine sans caractères blancs en tête et en queue*
?boucle de lecture des lignes du fichier repertoire en ram*
14 DO k=l BY 1 WHILE -EOF('lec')
15 ligne=READLN('lec')
16 pos=WORDINDEX(ligne,1)
17 IF pos=0 THEN DO
18 pos=posancien-l
19 ITERATE k
20 END
?sélection du traitement selon que c'est un répertoire ou un fichier, la clause étant de trouver ou non la chaine 'dir' dans la ligne analysée*
21 IF INDEX(ligne,'(dir)',1) ~= 0 THEN CALL repertoire
22 ELSE CALL fichier
23 END
24 CALL traitement
25 SAY 'Le grand total est:'total
26 SAY "J'ai terminé"
27 SAY file
28 x=WRITELN('ecr',file)
?préparation d'un espace mémoire pour stocker résul en dehors de ce programme afin de l'utiliser dans le programme d'affichage*
29 say LENGTH(file) *calcul de la taille nécessaire*
30 alpha=GETSPACE(5000)
31 EXPORT(alpha,file) *stockage dans la mémoire réservée*
32 PUSH alpha *place l'adresse dans la pile*
33 PUSH 5000 *met la taille dans la pile*
34 PUSH disque *place le nom du disque dans la pile *
35 PUSH 'rx RAM:affichedir' *prépare le lancement du troisième et dernier module *
37 EXIT *fin du programme avec retour à la console*
?premier sous-programme: traitement des répertoires*
40 repertoire: *nom du sous-programme*
43 p=(pos-l)%5
44 PARSE VAR ligne courantdir '(' . *analyse de la ligne
jusqu'au séparateur désigné: ( *
45 dir.p=STRIP(courantdir) *1'indice du tableau est le rang
du répertoire*
46 dir=disque
?boucle de création du chemin de sélection lorsque il y a plusieurs répertoires imbriqués*
47 DO i=l TO p
48 dir=dir|ldir.il|' '
49 END
50 dir=STRlP(dir,'B', ' ')
51 x=PRAGMA('D',dir)
52 file=STRlP(file' 'dir p'>')
53 CALL traitement *appel à un sous programme*
54 CALL imprime 'Le dir en cours est :'PRAGMA('D') *appel à
un sous-programme avec passage d'une chaine*
55 soustotal=0
56
57 RETURN *fin du sous-programme*
58
59
?sous-programme de traitement des fichiers*
60 fichier: *nom du sous programme*
61
62 *exemples d'imbrications de fonctions*
63 if STRIP(ligne) ~= " THEN
75
dir=STRIP(dir,'B',' ') *élimination en tête et queue de:
76 *
77
78
x=PRAGMA('D',dir) file=STRlP(file' 'dir posit CALL traitement
CALL imprime 'Le dir en cours est : END
79
80
81 CALL imprime 'Le dir en cours est :'PRAGMA('D')
82
83
84
85 *STATEF permet d'obtenir divers renseignements sur le fichier défini par son nom et bien entendu par son chemin d'accès*
86 IF fichierl ~=" THEN nombre1= '('
WORD(STATEF(fichierl),2) ')'
87 ELSE nombre=''
88 IF fichier2 -='' THEN nombre2= '('
WORD(STATEF(fichier2),2) ')'
89 ELSE nombre2=''
90 IF nombre1 ~=" THEN al=STRIP(STRIP(nombre1,'b','()'))
91 IF nombrel ~=" THEN a2=STRIP(STRIP(nombre2,'b','()'))
92 ‘calcul des cas particuliers selon l'existence des fichiers*
93
SELECT
94
WHEN
al~=" &
a2~=''
THEN
soustotal=soustotal+al+a2
95
WHEN
al~=" &
a2 =' '
THEN
soustotal=soustotal+al
96
WHEN
al =" &
a2~=''
THEN
soustotal=soustotal+a2
97
WHEN
al =" &
a2 ="
THEN
soustotal=soustotal
98
END
‘pas de
sortie
OTHERWISE ici non nécessaire c'est
imprudent*
99 ll=OVERLAY(fichierl,d,10) ‘OVERLAY surimpose des chaines en lieux et tailles bien déterminés*
100 ll=OVERLAY(nombrel,11,50)
101 CALL imprime 11
102 12=OVERLAY(fichier2,d,10)
103 12=OVERLAY(nombre2,12,50)
104
file=STRIP(file'III'STRIP(fichierl)STRIP(nombrel)STRIP(fichier2)S TRIP(nombre2)'III')
105 CALL imprime 12
106 taquet =1
107 fichierl="
108 fichier2='' '
109 nombre1='''
110 nombre2=''
111 posancien=pos
112
113 RETURN *fin du sous-programme*
114
115
116 imprime:
117
118 ARG texte ‘récupère la chaine transmise à l'appel de ce sous-programme*
119
120 SAY texte
121 RETURN
122
123 traitement: * création de la chaine résultat*
124
125
126
127
128 13=OVERLAY(soustotal,d,48)
129 13=OVERLAY('SOUS-TOTAL=',13,37)
130 CALL imprime 13
131 total=total+soustotal
132 13=OVERLAY(total,d,48)
133 13=OVERLAY(' TOTAL=',13,37)
134 file=STRIP(file' 'soustotal','total' ')
135 CALL imprime 13
136
137 RETURN
Pu r. G)
Contrairement à ce que voudrait la théorie, le programmeur moyen sur des machines telles que Vamiga passe la moitié de son temps à taper des lignes de code et le reste à essayer d’en faire quelque chose d’utilisable.
ADEBUG
Pour assister les pauvres développeurs que nous sommes, la société Arobas va bientôt mettre sur le marché un nouveau débogueur : Adebug, version Amiga d’un programme Atari, qui laisse loin derrière lui les différents outils déjà existant... Les débogueurs que l’on trouve sur Amiga ne sont pas très nombreux, les principaux étant Monam2 du Devpac, Metascope, AsmOne (éditeur, compilateur, débogueur), ainsi que des moniteurs intégrés à de petits assembleurs (K-Seka et autres mutants qui en découlent) ou des petites brutes écrites par des traumatisés du mode trace (RomCrack et autres).
Ils se répartissent en deux principaux groupes : ceux qui sont évolués et conservent le multitâche et Intuition (Monam2, Metascope, ou même Profimat) et ceux qui apprécient le calme d’un ordinateur débarrassé de son système d’exploitation. Adebug se situe à l’intersection de ces deux ensembles puisqu’il élimine le multitâche lorque l’on utilise son écran de contrôle, mais remet la configuration courante (niveau IPL, registres hardware...) lorsqu’on désire tracer des instructions. Enfin faisant office de relique, K-Seka est décidément positionné sur le marché de l’assembleur pour jeunes...
PRESENTATION
touche TAB et faire agir dessus diverses commandes d’édition.
Les informations fournies par le système de fenêtrage sont suffisament complètes pour permettre des débogages relativement complexes. La zone d’affichage des registres montre non seulement les valeurs de ces derniers, mais aussi le contenu des addresses pointées par ceux-ci, ce qui épargne au programmeur de nombreux dumps-mémoire. La fenêtre de désassemblage ne se contente pas non plus d’un simple affichage de code assembleur mais grâce à de petits dessins, indique la position du PC, l’endroit où il se retrouvera après l’exécution d’une instruction de branchement, ainsi que les différents points d’arrêt.
Adebug est un débogueur symbolique, ce qui veut dire que les lignes désassemblées utilisent au maximum les étiquettes définies dans le code source, ainsi que celles que l’on peut définir dans des fichiers de définition de variables. Il n’est plus étonnant dans ces conditions de voir des programmes en ROM avec leurs labels. Un petit détail qui a son importance : quand on appuie sur la touche de curseur haut, le pointeur de début de fenêtre remonte d’une ligne désassemblée et non de deux octets comme c’est le cas dans certains débogueurs moins soucieux du confort de l’utilisateur. Il faut noter que toutes les sorties graphiques sont dynamiques, c’est-à-dire qu’elles sont réactualisées en permanence. Adebug peut de plus rediriger son affichage sur un Minitel (mode 80 colonnes), permettant donc d’afficher à la fois les données du débogueur et l’écran du programme.
Le travail est facilité par un certain nombre de raccourcis clavier. Par l’appui d’une touche on peut demander de voir le contenu de la mémoire pointée par n’importe quel registre (aO à a6, sp, ssp et pc). On peut aussi définir une macro (oui, une !), sorte de script contenant les commandes tapées au clavier. Ce système est indispensable, lors de débogages répétitifs, à la santé mentale du pauvre codeur...
Adebug se démarre comme un utilitaire externe, contrairement à Monam, AsmOne, Profimat et K-Seka qui sont inclus dans l’éditeur-assembleur. Une fois le lancement effectué, le programme va chercher sur le disque les préférences définies lors de la cession précédente et après quelques flash blancs (épileptiques s’abstenir) on se retrouve avec un écran noir rappellant celui de Monam2. Il faut bien avouer que le premier contact avec ce genre de débogueur n’est pas en général en sa faveur, puisqu’il faut abandonner le doux confort d’intuition pour l’aridité d’un éditeur avec ligne de commande. On se retrouve en fait avec une gestion clavier du type de celle du Shell, avec posibilité de remonter l’historique des commandes déjà exécutées. Mais ceci ne doit en aucun cas décourager le lecteur, la majorité des commandes ne nécessitant l’appui que d’une ou deux touches (les mêmes que Monam2, d’ailleurs), ce qui est, après un court apprentissage, extrêmement pratique et rapide.
Finalement que demande-t-on à l’interface d’un débogueur ? C’est de permettre une utilisation efficace et rapide des opérations les plus courantes, ce qui n’est sûrement pas le cas d’interfaces plus visuelles à base de fenêtres et de menus déroulants. L’utilisation exclusive du clavier n’est pas un handicap pour un logiciel destiné aux programmeurs qui, jusqu’à preuve du contraire, tapent leurs listings manuellement.
L’aspect de l’écran se rapproche fortement de celui de Monam2, dont le look caractéristique est basé sur la division de l’écran en zones (jusqu’à 5 fenêtres de taille variable) auxquelles sont associées un type d’information précis (listage des registres, désassemblage, dump mémoire hexa et ASCII). On peut sélectionner la fenêtre sur laquelle on compte agir grâce à la
LE TRAVAIL AVEC ADEBUG
Bien que cette interface soit relativement perfectionnée, le degré de "bestialité" du programme tracé n’influence en aucun cas le confort d’utilisation. Ainsi, un programme tracé en niveau d’IPL 7 se présentera de la même manière qu’une routine Intuition (bien évidemment utilisée en IPL 0) de façon à conserver les interruptions du multitâche. Adebug permet de choisir de cette manière un niveau de résistance aux agressions diverses, telles que celles que l’on a l’habitude de rencontrer en traçant une démo (rip, rip) ou un jeu (crack, crack).
Mais Adebug n’est pas une forteresse coupée du monde extérieur, il permet de voir la tâche se dérouler avec son propre écran si celle-ci a une CopperList. Il suffit d’en spécifier le label et Adebug se charge de l’afficher à chaque traçage d’instruction.
La fenêtre active est celle sur laquelle on va faire agir des fonctions. Ces dernières sont plus ou moins classiques : on retrouve tout d’abord les habituels copie de bloc mémoire, remplissage, édition... Le principal défaut des fonctions d’édition est l’absence d’assembleur-ligne, qui aurait par exemple permis de remplacer directement un SUBQ.W l,d0 par un NOP, au lieu de passer par l’éditeur du dump hexadécimal. Par contre, la fonction de recherche est très complète puisqu’elle prend en charge plusieurs types de données : octets, mots, mots longs, chaines ASCII et intructions (par exemple, chercher un LEA $ 70000,aO dans un code objet de VcC).
D9:*00086001 Di! 00C06634 D2: 00000FA9 D3: 00090FA8 D4:*90000001 D5: 0000903E D6: 903936BD D7: 00C0634C SR: 0009 9
PC: 00C00276
0839 2499 0039 E121 9000 0996 0039 2D6D 0000 9FR0 265F 48F1 4CE9 9800 00FF 492E 002E 03D0
FFFF FFFF 9939 : 0000 93E8 0039 1 90FF 9908 09FF ¦ 6098 2D2D 4172 I 00C0 69C8 09C3 i 1800 08F4 D3C9 ‘ FFF4 2869 FFFC * 0000 0F89 00C9 i 8009 3ER7 FFFF !
I960 00C9 65E3 1980 0000 0001 495E 00FF 4899 662E 2D2D 48E7 8488 00C5 4BC0 48D1 001E 4ED4 4ED3 265F 48F1 69C8 00C3 8488 2D56 0404 F101
2 DIS
3 HEXfl
80C90276
00C09278
00C0927R
00C9027C
00C0927E
00C09280
00C09282
00C00276 00C9 1916 A36
00C00278 09C0 03F0 À
00C0027E 9900 00FC 0 ü 00C00282 0088 0400 "b
00C09286 9276 024C ML 00C0028R 0022 0002 " A
00C0028E 00FC 0018 ü 8
4 8SC
5 HEXfl
00C00276 00C0 1916 A3S
80C00270 00C0 63F0 A
00C0827E 0980 00FC Q li 00C00282 0888 0409 "b
00C90286 9276 024C ML 09C0028A 0022 0002 " A
00C0028E 00FC 0018 ü 8
00C0O292 672D 0000
Que ce soient des labels ou des valeurs définies d’une autre manière, Adebug considère toute variable de la même manière, et ceci n’est rendu possible que par l’existence d’un puissant évaluateur d’expressions, qui constitue la principale innovation de Adebug puisque sa seule présence prend en charge une bonne partie de la tâche qui incombait jusque là au programmeur, à savoir la prise de décisions. Nous verrons dans la suite à quels endroits précis ce système démontre clairement sa puissance.
L’évaluateur possède une syntaxe classique qui ne dépaysera pas le programmeur habitué à un langage évolué quelconque. Il permet de faire des opérations arithmétiques (+, -, *, ), logiques (EOR, AND, OR, EOR), des décalages (LSL, LSL) sur des valeurs numériques ou sur des variables. Ces dernières sont soit définies par l’utilisateur, soit réservées à l’avance par Adebug. En effet, une grande partie des variables internes sont accessibles. Les addresses des points d’arrêt et de début des fenêtres, le contenu des registres... ont des noms réservés. De plus, une fonction autorise la lecture ou l’écriture en mémoire d’octets, de mots ou de mots longs. Certaines variables sont en fait des expressions à évaluer à chaque pas de programme qui est tracé. Il devient aisé de faire suivre à une fenêtre le contenu d’un registre.
Un exemple d’expression serait w2=4*dO+ TableBase}.L, ce qui revient à demander de positionner la deuxième fenêtre sur un mot long d’une table dont le numéro est fourni par le registre dO. On réalise aisément l’aide réelle apportée par un évaluateur puissant associé à une gestion correcte des variables.
Le mode trace autour duquel est construit tout débogueur qui se respecte peut se voir grandement amélioré par quelques fonctions peu courantes. Tout d’abord, Adebug trace sans problème la ROM de l’Amiga grâce à une recopie automatique en RAM
des instructions. Il est tout aussi capable de tracer
une routine de gestion de mode trace. Pour ce qui est des aides à la vie courante, Adebug émule le fameux mode trace du 68020 qui ne s’arrête que sur les instructions interrompant le flux (branchements conditionnels ou non). Il peut aussi sauter une instruction ou même forcer un branchement. Tout ceci est consigné dans un petit historique, tenu à jour en permanence.
Mais la grande nouveauté est le traçage conditionnel, qui fait appel à l’évaluateur d’expression. On peut
avec ce dernier demander à Adebug de tracer un programme
jusqu’à ce qu’une condition spécifiée (par exemple d0=10 et a0 >5) devienne vraie. Evidemment, ce système est très gourmand en temps-machine puisqu’il faut évaluer l’expression à chaque instruction exécutée. Heureusement, une technique plus rapide est mise à notre disposition grâce aux points d’arrêts conditionnels. Rappelons qu’un point d’arrêt est une sorte de marque que le débogueur positionne à l’endroit spécifié par l’utilisateur de façon à récupérer la main quand le microprocesseur "passe" dessus. Cette marque est en général une instruction du type Line A ou Trap (selon les besoins du moment) qui bloque le 68000 et remet en route Adebug. Jusque là, les points d’arrêts bloquaient à coup sûr le programme, mais avec l’évaluateur d’expressions, ceux-ci peuvent être associés à une condition. Le point d’arrêt et la condition seront affichés en permanence dans les fenêtres de désassemblage.
L’adjonction d’un certain nombre de petites fonctions qui ne seraient pas du luxe : un sélecteur de fichiers, un classement alphabétique des labels ou un affichage des préférences sur un panneau de contrôle.
On pourra, plus sérieusement, regretter l’absence de version cartouche telle qu’elle existe sur le ST. Un débogueur de ce type serait d’une efficacité redoutable s’il n’avait plus besoins de cohabiter en mémoire avec les programmes erronés.
Adebug est donc un excellent programme qui s’adressera à des programmeurs ayant une bonne connaissance du langage assembleur et qui se sont déjà retrouvés confrontés aux insuffisances de leur débogueur habituel. La configuration nécessaire au déploiement de ce char d’assaut aura tout intérêt à comporter une extension mémoire, la taille du programme étant actuellement de 97600 octets !
ET ALORS ?
Finalement, le principe général d’Adebug est de répondre point par point aux besoins des programmeurs. Ces besoins ne peuvent être recensés qu’à l’usage du programme, et le fait qu’Adebug vienne tout droit du monde Atari lui permet de bénéficier d’une plus grande maturité dès sa sortie. Espérons que ce logiciel saura évoluer dans le sens d’une plus grande simplicité d’emploi, par
Une autre fonction ouvre de nouveaux horizons dans l’art de la recherche d’erreurs. En effet, des routines externes peuvent être exécutées par Adebug à chaque pas du mode trace. Ces routines, crées par chaque utilisateur, reçoivent comme paramètre une structure reflétant l’état du débogueur et de la tâche tracée. On retrouve bien entendu toutes les valeurs des registres mais aussi les addresses des fenêtres.
4 H0:*00C06É34 AF4 R1: 00C06740 IL 82: 00C05F98 II" 83: 00838484 A R4: 00C4F8A9
85. ' 00FF4134 0615 flé: 00FF4128 AcL H7: 90C4F89C SSP: 00C80000
DC. FI ÎC9
$ DC.FI ÎC0 HOVE.B (R6),-(R4)
DC .14 ÎC0
DC. H Î3F0 BTST D4.D0 DC y $ FC
ORÎ.L Î4000276.Î24C(R0)
Eric Brun et et François Fleuret
Puisque vous savez maintenant tout ou presque sur les loyers, il est temps de passer à la seconde partie de notre découverte de ces petites bébêtes sympathique et d’aborder une autre notion tout autant mystérieuse : les régions.
Ci)
verrouille un layer donné Déverrouille ledit layer verrouille les layers d'une layer_info Déverrouille ces même layers verrouille la layer_info elle-même
Voici listées pour vous et dans le tableau ci-dessous, les fameuses "fonctions adéquates" dont il est question dans le paragraphe précédent. Rendez-vous tout de suite après pour les explications de rigueur.
LockLayer() UnlockLayer() LockLayers() UnlockLayers() LockLayerlnfo()
ais avant que de commencer, il semble important d’apporter quelques précisions à l’article du mois dernier, le mécanisme de verrouillage des layers et de la layer_info ayant été odieusement passé sous silence, alors qu’il s’agit d’une particularité fort intéressante. En vous présentant toutes mes excuses, je vous invite donc à un petit détour par le monde étrange et pénétrant des Sémaphores...
C’EST MA FAURE
De quoi s’agit-il Auguste ? Tout simplement et en simplifiant quelque peu, de permettre à une tâche donnée d’empêcher une autre tâche quelconque d’accéder à une zone de mémoire particulière. Bref, c’est à un véritable "verrou de la mémoire" que nous avons à faire. Ce verrou étant purement logiciel (aucun matériel hardware n’intervenant dans l’histoire), celà suppose évidemment que la seconde tâche soit disciplinée et accepte de se soumettre à ce procédé. Ce qui est souvent le cas, parfois même à son nez et à sa barbe. Les exemples les plus frappants étant d’une part la structure IntuitionBase, que l’on peut verrouiller en appelant la fonction LockIBase() et, donc, nos layers.
La principale utilité des sémaphores est donc d’arbitrer l’accès à la mémoire entre les différentes tâches. Imaginez par exemple que vous soyiez en train d’ajouter un noeud dans une liste publique, tandis qu’au même moment, une autre tâche parcourt cette même liste à la recherche d’un autre noeud ; il y a alors une chance sur deux pour que l’insertion de votre noeud fasse se mélanger les pinceaux à l’autre tâche. Le cas est encore pire si deux tâches essaient chacune d’ajouter un noeud à une même liste en même temps. Dans tous ces cas, interdire le multitâche avec Fobrid() ou même DisableQ ne suffit plus, étant donné que l’autre tâche peut avoir déjà commencé son travail avant que vous ne lui coupiez les vivres. Bien sûr, lorsqu’il s’agit de données "statiques" (non modifiables) comme la Rom, un mécanisme de verrouillage devient superflu.
Les sémaphores sont entièrement basés sur le double système des listes et des signaux. Lorsqu’une tâche a obtenu avec succès un sémaphore, toutes les autres tâches qui essaieraient d’y accéder sont mises en état d’attente (avec Wait()) jusqu’à ce que celui-ci soit libéré. De cette manière, on est sûr que deux tâches n’accéderont jamais aux mêmes données en même temps.
Si le sujet vous intéresse, nous le décrirons sans doute plus avant dans ces mêmes pages, mais pour l’heure, il est temps de revenir à nos moutons, les layers, que l’on peut donc verrouiller.
LA CLEF DES CHAMPS
Celà se fait très simplement avec les fonctions adéquates de la layers.library, qui se charge toute seule comme une grande de l’interfaçage avec les sémaphores, nous libérant de tout ce travail. Il est ainsi possible de verrouiller soit un layer particulier, soit une layer_info complète, et donc tous les layers qui en dépendent. Par exemple, Intuition verrouille la layer_info de l’écran concerné lorsque vous déplacez ou agrandissez une fenêtre, ou encore lorsque vous déroulez un menu. Par autre exemple, le Workbench verrouille la layer_info de son écran lorsque vous déplacez une icône.
UnlockLayerlnfo() Déverrouille ladite layer_info
Avec LockLayer(), vous verrouillez un layer donné afin qu’aucune autre tâche ne puisse y effectuer de sortie graphique en même temps que vous. Rappelons que ce verrouillage étant basé sur les sémaphores, votre tâche sera mise en état d’attente si d’aventure le layer concerné était déjà verrouillé par une autre tâche. Si vous désirez verrouiller plusieurs layers dans un même écran mais pas tous, il vous faudra entourer tous les appels à LockLayer() par une paire de LockLayerInfo() et UnlockLayerInfo(), ceci afin d’éviter un blocage intempestif du système.
LockLayers() vous permet de verrouiller en une seule fois tous les layers d’un écran donné, sans toucher à la structure layer_info associée. De fait, une autre tâche peut toujours la modifier, même si les layers eux-mêmes sont protégés.
Enfin, LockLayerInfo() est la plus puissante des trois fonctions de verrouillage, puisque non contente de bloquer tous les layers de l’écran, elle verrouille également la layer_info elle-même. De cette manière, vous êtes sûr que rien d’imprévu ne peut arriver à vos layers pendant que vous les travaillez au corps à corps.
Ces trois fonctions possèdent chacune leur vis-à-vis, qu’il convient d’appeler absolument lorsque le travail désiré a été effectué : n’oubliez pas que d’autres tâches peuvent attendre d’avoir le droit d’accéder au(x) layer(s) verrouillé(s).
Dernier point intéressant à noter, le fait que la graphies.library dispose de deux fonctions similaires à LockLayerQ et UnlockLayer(), s’appellant respectivement LockLayerRom() et UnlockLayerRomO et qui peuvent donc permettre, dans certains cas ma foi assez limités, d’éviter d’avoir à ouvrir la layers.library.
LES REGIONS
Cette fois-ci, tout ce qui pouvait être dit sur les layers l’a été, nous pouvons donc aborder sans plus attendre ce nouveau concept qu’est la Région. La layer s.library est maintenant oubliée, c’est la graphies.library qui se charge de tout (à une exception près, que nous verrons plus loin). Franchement, je n’ai jamais compris pourquoi elle ne se chargeait pas également des layers, mais bon, hein, c’est pas moi qui ai écrit le système (Dieu merci !).
Tout comme les layers, les régions font intervenir la notion de rectangle de clipping. Le principe est pourtant assez simple : alors que les layers servent à limiter les sorties graphiques à une zone particulière d’une BitMap, les régions servent à les limiter à une zone particulière d’un layer. Pourquoi donc se compliquer ainsi la vie ? N’oubliez pas que les layers peuvent se chevaucher entre eux, changer de taille, être déplacés... L’utilité des régions réside donc dans la capacité qu’elles sont de limiter les sorties graphiques aux parties visibles d’un layer donné.
Lorsque plusieurs layers se chevauchent, le système maintient et met à jour une liste des régions perdues, appellée la DamageList. Ainsi, plus tard, lorsque l’application concernée voudra rafraîchir sa fenêtre, le système limitera les sorties graphiques aux régions contenues dans cette liste, accélérant d’autant l’opération. Ceci est tout-à-fait transparent pour l’application, qui se contente de redessiner "bêtement" le layer entier.
La graphies.library met à notre disposition plusieurs routines de création et de gestion des régions au sein d’un layer. Celles-ci
sont résumées dans le tableau ci-dessous, que je vous laisse examiner avant de continuer.
NewRegion() DisposeRegion() ClearRegion()
Crée une nouvelle région dans le layer Libère ladite région Réinitialise une région
AndRectRegion() OrRectRegion() XorRectRegion() ClearRectRegion()
Gestion des régions conjointement avec des Rectangles
AndRegionRegion() OrRegionRegion() XorRegionRegion()
Gestion des régions conjointement avec d'autres régions
Enfin, la layers.library offre une seule fonction de gestions des régions, à savoir InstallClipRegion(), qui installe effectivement la région dans le layer. Tout celà semble un peu bordélique, mais c’est finalement assez logique.
Avec NewRegion(), on alloue dynamiquement et initialise une structure Région que l’on pourra par la suite lier au layer. Cette région est "vierge", c’est-à-dire qu’elle ne permet pas encore de sortie graphique. L’allocation étant dynamique, la fonction DisposeRegion() sert à libérer la mémoire utilisée.
Les fonctions suivantes, AndRectRegion(), OrRectRegion() et XorRectRegion() servent à mettre en place la région. Elles utilisent une structure supplémentaire, connue sous le nom de structure Rectangle, qui définit les coordonnées de la zone de clipping effective Ce n’est qu’une fois l’une de ces trois fonctions appelée que la région sera opérative et pourra être installée dans le layer avec InstallClipRegion(). La différence entre ces trois fonctions tient dans la manière dont elles calculent la zone effectivement visible : AndRectRegion() limite la sortie aux parties du layer visibles à la fois dans le Rectangle et dans la Région ; OrRectRegion() étend la Région aux parties visibles dans le Rectangle ; XorRectRegion() limite la sortie aux parties visibles dans le Rectangle ou dans la Région, mais pas dans les deux. Arrivé ici, un schéma sera peut-être plus clair.
AndRec tReffiont)
Rectangle
Région
Zone de clippinff (visible)
Enfin, ClearRectRegion() supprime un Rectangle donné de la région considérée.
Quant aux trois dernières fonctions, à savoir AndRegionRegion(), OrRegionRegionO et XorRegionRegion(), elles agissent de la même manière que les précédentes, mais jonglent avec deux régions plutôt qu’avec une région et un rectangle.
Finalement, lorsque vous avez décidé et correctement initialisé votre Région, vous l’installez dans le layer avec InstallClipRegion() ~ qui, rappelons-le encore une fois, fait partie de la layers.library - et enfin, toutes les sorties graphiques dans ce layer seront limitées à cette région.
L’EXEMPLE
Le programme qui suit est l’exemple d’utilisation des layers en dehors d’intuition, promis le mois dernier. Pour les régions, c’est un peu plus difficile de réaliser un programme dont l’effet visuel doit évident... C’est pourquoi je vous propose à la place une petite routine, d’ailleurs adaptée de celle fournie dans les RKM Libraries & Devices 1.3, pages 516 et 517, qui permet de clipper une fenêtre Intuition à ses bords (ce qu’intuition ne fait pas elle-même tout seule ; si vous avez déjà essayé de dessiner en dehors d’une fenêtre, vous aurez pu remarquer que les bords font partie intégrande du layer, sauf évidemment dans le cas d’une fenêtre de type GIMMEZEROZERO, mais ça, c’est une autre histoire).
Par Max
Hé, pssiittt...
On a failli oublier de vous rappeler que le 3615 ANT et le 3615 ComRev vous attendent 24 heures sur 24 pour dialoguer en direct, télécharger, jouer...
Avec 1’Espion de la Mémoire, vous avez pu explorer tout le système graphique de Vamiga. ExecMaster va plus loin en vous permettant en plus de modifier à votre guise les structures les plus importantes du noyau multitâche d’Exec.
ExecMas
De fait, si le procédé peut paraître identique, la réalisation est totalement différente. D’abord, le langage machine a été choisi car lorsque que l’on s’amuse avec Exec, il vaut mieux être le plus rapide possible. Ensuite, le fait même de pouvoir modifier les données affichées rendait tout-à-fait caduque la méthode mise en oeuvre dans l’Utilitaire de l’ANT numéro 25.
ExecMaster vous est proposé ici dans sa version 0.7, c’est-à-dire qu’il s’agit quasiment de ce que les professionnels de l’informatique nomment une bêta-version. Le listing est déjà suffisamment conséquent comme ça (jugez Par vous même...), à tel point d’ailleurs que nous avons choisi de le publier en deux fois. Vous trouverez donc ce mois-ci le programme principal, qui vous permettra au moins de voir comment tout celà fonctionne, et le mois prochain, les définitions de l’écran, des fenêtres, des gadgets et des menus utilisés.
PRESENTATION
Le menu principal Affichage propose de visualiser les différentes tâches en service dans le système, les bibliothèques, devices et resources présents en mémoire, les ports publics connus d’Exec, les modules résidents, les listes-mémoires et les interruptions actives. Toutes ces données ne sont pas obligatoirement modifiables, aussi un second menu apparaît-il en fonction de ce qui est en cours de visualisation.
ExecMaster permet de changer la priorité d’une tâche, de la "geler" provisoirement (c’est-à-dire de suspendre son exécution) et de la "réchauffer" (la ré-introduire dans le la liste des tâches candidates à l’utilisation du CPU). Vous pouvez également ouvrir et fermer une bibliothèque particulière, ou éliminer de la mémoire toutes celles qui ne sont pas présentement utilisées. Quant aux ports, vous pouvez les "cacher" (les supprimer de la liste des ports publics, mais sans les détruire) ou bien les "révéler" (les réintroduire dans la liste). Enfin, vous pouvez changer la priorité d’un type de mémoire donné, par exemple pour faire en sorte que le système alloue de la mémoire CHIP en piorité. Tout celà s’effectue à la souris, via les menus adéquats.
PRINCIPE
Dans tous les cas, étant donné que l’on parcourt des listes publiques modifiables par une autre tâche à n’importe quel instant, il convient d’interdire d’abord le multitâche et même carrément les interruptions, de lire les données de la liste en cours et de les copier dans un buffer supplémentaire, pour finalement les afficher après avoir rétablit le multitâche. Une routine, AffList, se charge de tout celà : elle parcourt la liste pointée par a2 jusqu’à sa fin, en appelant à chaque itération la routine pointée par a3 et chargée de mettre en forme les données trouvées. AffList est appelée quasiment par toutes les routines d’affichage, sauf celles qui ne dépendent pas d’une liste (au sens Exec du terme), comme par exemple les interruptions ou les modules résidents.
Geler une tâche est très simple : il suffit de la retirer de sa liste actuelle (.Ready ou Waiting) et de l’insérer dans une autre, interne au programme. Ainsi, Exec ne sait plus que cette tâche existe et elle "s’arrête" tout bonnement de tourner. Le processus inverse est utilisé pour la "réchauffer". Notez qu’ExecMaster refuse de se geler lui-même (c’est la moindre des choses !) Et qu’il ne vérifie pas, en quittant, si des tâches sont encore gelées. Faites donc attention.
Ouvrir et fermer une bibliothèque particulière ne pose aucun problème, on peut peut-être seulement se demander comment la virer de la mémoire si elle n’est pas actuellement utilisée. Très simple : il suffit d’essayer d’allouer un très grosse quantité de mémoire, et le tour est joué ; Exec supprime alors tout ce qui peut l’être : bibliothèques, devices, resources, fontes...
Pour "cacher" et "révéler" un port, on procède de la même manière que pour geler et réchauffer une tâche : on le supprime de la liste des ports publics pour l’insérer dans une autre, interne au programme et inversement. Celà ne détruit pas le port lui- même, même si FindPort() ne peut plus le trouver. Là encore, ExecMaster ne fait aucune vérification quant au nombre de ports encore cachés avant de quitter, donc prudence...
AMELIORATIONS
Beaucoup sont possibles (je vous rappelle que ce n’est là que la version 0.7), comme par exemple terminer définitivement une tâche donnée (gare aux allocations mémoire qui ne seront pas libérées) ou carrément la désassembler depuis son PC actuel... On peut également envoyer des signaux et ou des messages aux ports, lister d’autres structures (input-handlers, vecteurs Reset...) avec toutes les applications qui en découlent. L’imagination est la seule limite aux possibilités d’un tel programme (sans me vanter).
A SIGNALER
... que l’idée d’ExecMaster ne vient pas de moi, mais de l’allemand Werner Günther, auteur du domaine public Xoper dont la version 2.2 est un véritable petit bijou (moins évident à utiliser car entièrement au clavier) que tout programmeur se doit de posséder. D’ailleurs, je vous l’ai glissée quelque part sur la disquette d’accompagnement de ce numéro de l’ANT. Merci qui ?
ExecMaster
v0.7 - 1991, Max pour ANT
opt o+,
, ow-
incdir
"include:"
include
"exec alerts.i"
include
"exec execbase.i"
include
"exec tasks.i"
include
"exec memory.i"
include
"exec résident.i"
include
"intuition intuition.i"
include
"libraries dos.i"
include
"libraries dosextens.i"
include
"exec exec_lib.i"
include
"intuition intuition_lib.i
include
"graphies graphics_lib.i"
include
"libraries dos_lib.i"
include
"misc easystart.i"
. ***** Macros
EXEC MACRO
movea.l $ 4.w,a6 jsr _LVO l(a6)
ENDM
DOS MACRO
movea.l DosBase(a5),a6 jsr _LVO l(a6)
ENDM
INT MACRO
movea.
1 IntBase(a5),a6
lea ports(a5),a0
jsr
_LVO l(a6)
NEWLIST aO
ENDM GFX MACRO
suba.l al, al EXEC FindTask
movea.
1 GfxBase(a5),a6
movea. 1 d0,al
jsr
_LVO l(a6)
moveq 20,d0
ENDM
CALL SetTaskPri
CALL MACRO
sf fini(a5)
jsr
_LVO l(a6)
ENDM
. ************************************
. ************************************
MainLoop:
tst.b fini(a5)
rsreset
bne Exit
Xltem rs.b
0 ; Structure MenuItem étendue
xi_Item rs.b
mi_SIZEOF ; Item standard
movea. 1 window(a5),aO
xi_Func rs.1
1 ; Fonction à exécuter ou NULL
movea. 1 wd__UserPort (aO ), aO
xi_SIZE rs.w
0
EXEC WaitPort
rsreset
AllMsgs movea. 1 window(a5) , aO
Ligne rs.b
0
movea. 1 wd_UserPort (aO ), aO
lg_Num rs.w
1 ; Numéro de la ligne
EXEC GetMsg
lg_Data rs.b
38 ; Données pour RawDoFmt()
tst.l dO
lg_Str rs.b
80 ; Buffer ASCII pour
beq. S MainLoop
RawDoFmt() lg_SIZE rs.b
0
movea .1 dO, al
rsreset
move.1 im_Class(al) d3 move.w im_Code(al),d2
Args rs.1
2
CALL ReplyMsg
Wbmsg rs. 1
1
DosBase rs.1
1
cmpi.1 MENUPICK d3
IntBase rs.l
1
bne. S AllMsgs
GfxBase rs.l
1
screen rs.1
1
DoMenus moveq 0,d0
window rs. 1
1
move.w d2,d0
rport rs. 1
1
lea MainMenuList(pc),aO
tasks rs.b
LHJ3IZE ; Liste des tâches "gelées"
INT ItemAddress
ports rs.b
LH SIZE ; Liste des ports "cachés"
tst.l dO
ligne_y rs.w
1
beq AllMsgs
lignes rs.w
1
movea .1 dO, al
fini rs.w
1
move.1 xi_Func(al),dO
VARSIZE rs.w
0
beq. S .nofune
. ************************************
movea .1 dO, aO
Start lea
VARS(pc),a5
movem. 1 d0-d7 a0-a6, - (sp) ; Sauve les registres
movem. 1
. DO aO,Args(a5)
movea.1 $ 4.w,a6 ; Prépare a6
lea
dosname(pc),al
lea txtbuff,a4 ; et a4
jsr (aO) ; Saute à la routine
moveq
0,d0
movem.1 (sp)+,d0-d7 a0-a6 ; Récupère les
EXEC
OpenLibrary
registres
move.1
dO,DosBase(a5)
beq
NoDos
. Nof une move. W mi_Next Select (al),d2
lea
intname(pc),al
bra. S DoMenus
moveq
0, dO
. ************************************
CALL
OpenLibrary
Exi t move.1 window(a 5),dO
move.1
dO,IntBase(a5)
beq. S .nomenu
beq
Nolnt
movea. 1 d0,a0
lea
gfxname(pc),al
INT ClearMenuStrip
moveq
0, dO
. Nomenu movea. 1 window(a5 ) , aO
CALL
OpenLibrary
INT CloseWindow
move.1
dO,GfxBase(a5)
beq
NoGfx
NoWin movea.1screen(a5),aO
lea
ns(pc),a0
INT CloseScreen
INT
OpenScreen
NoScr movea. 1 GfxBase (a5), al
move.1
dO, screen(a5)
EXEC CloseLibrary
beq
NoScr
lea
nw(pc),a0
NoGfx movea. 1 IntBase (a5 ), al EXEC CloseLibrary
move.1
dO,nw_Screen(aO)
INT
OpenWindow
Nolnt movea. 1 DosBase (a5 ), al
move.1
dO,window(a5)
EXEC CloseLibrary
beq
NoWin
movea.1
dO, aO
NoDos moveq 0,d0 rts
move.1
wd_RPort(aO),rport(a5)
lea
MainMenuList(pc),al
. ************************************
CALL
SetMenuStrip
SetMenu move .1 aO, - ( sp)
lea
tasks(a5),aO
movea. 1 window(a5) ,a0 INT ClearMenuStrip
. ************************************
NewLine addq.w 8,ligne_y(a5) ; Descend d'I ligne de
Ptitre
movea.1
window(a5),a0
move.b
4,ccr ; Positi
lea
MainMenuList(pc),al
rts
move.1
(sp)+,mu_NextMenu(al)
.non
movea.l screen(a5),aO
INT
SetMenuStrip
INT
DisplayBeep
rts
move.b
0,ccr ; Efface
rts
move.1
aO, -(sp)

DoFmt
movem. 1
d0-d3 d7 a0-a3 a6,-(sp)
moveq
0,d0 ; Efface l'écran
moveq
-l,d7
movea.1
rport(a5),al
move.w
lignes(a5),lg_Num(a4)
GFX
SetRast
addq.w
1,lignes(a5)
moveq
3, dO
movea.1
a4, al
movea.1
rport(a5),al
lea
.putch(pc),a2
CALL
SetAPen
lea
lg_Str(a4),a3
EXEC
RawDoFmt
moveq
0,d0 ; Affiche le titre
cmpi .b
80,d7
moveq
20,dl . .
Bis.s *-
.okl
movea.1
rport(a5), al
moveq
80,d7
CALL
Move
.okl
move.1
d7,d0
movea.1
(sp)+, aO
lea
lg_Str(a4),aO
moveq
0, dO
movea.1
rport(a5),al
move. B
(aO)+,dO
GFX
Text
movea.1
rport(a5),al
bsr
NewLine
CALL
Text
lea
lg SIZE(a4),a4
movem. 1
(sp)+,d0-d3 d7 a0-a3 a6
moveq
1 dO
rts
movea.1
rport(a5),al
CALL
SetAPen
.putch
move.b
dO,(a3)+
addq.1
1, d7
moveq
0,d0 ; Positionne le curseur
rts
moveq
30,dl ; pour la 1ère ligne
move.w
dl,ligne_y(a5)
§
movea.l rport(a5),al
CALL
rts
Move
texte
move.w ligne_y(a5),dl moveq 0,d0 movea.l rport(a5),al CALL Move rts
. ************************************
; Affiche la liste pointée par a2 en appelant la ; routine de formatage pointée par a3 AffList move.w $ 4000,$ dff09a .ail movea.1 LH_HEAD(a2),a2 LN_SUCC(a2)
tst. 1 beq. S lea jsr bsr. S bra. S .endlst move.w rts
.endlst
lg_Data(a4),al (a3)
DoFmt .ail
$ c000,$ dff09a
* ***********************************
Vérifie si le noeud pointé par al se trouve dans la liste pointée par aO ChkList move.w $ 4000,$ dff09a
.ail movea.1 LHHEAD(aO),aO
tst.l LN_SUCC(a0)
beq.s .end
cmpa.1 aO,al
bne.s .ail
.end move.w $ c000,$ dff09a
cmpa.l a0,al
rts
. ************************************
; Vérifie si dO est un numéro de ligne valide et si oui, ; renvoie l'adresse du Noeud concerné dans al ChecklD move.1 dO,dl bmi.s .non
cmp.w lignes(a5),d0 bcc.s .non
mulu lg_SIZE,d0 movea.1 lg_Data(a4,dO.1),al
GetNum movem.1 dl-d7 a0-a6,-(sp) lea w3gad(pc),al ori.w LONGINT,gg_Activation(al) bra. S DoGadget
GetStr movem. 1 dl-d7 a0-a6, - (sp) lea w3gad(pc), al andi.w ~LONGINT,gg_Activation(al)
DoGadget:
movea.l gg_SpecialInfo(al),al clr.w si_BufferPos(al) clr.w si_DispPos(al) clr.w si_NumChars(al) clr.l si_LongInt(al) movea.l si_Buffer(al),al clr.b (al)
moveq -l,d4 move.l a0,d2
. Okl movea. 1 window ( a5 ), aO INT ClearMenuStrip lea nw3(pc),a0 move.l screen(a5),nw_Screen(a0) move.1 d2,nw_Title(aO)
CALL OpenWindow move.1 dO,d2 beq . Nowin
.Loop movea.l d2,a0
movea. 1 wd_UserPort ( aO ), aO EXEC WaitPort
.ail movea.l d2,a0
movea. 1 wd_UserPort (aO), aO
EXEC GetMsg
tst.l dO
beq.s .Loop
movea.l d0,al
move.l im_Class(al),d3
CALL ReplyMsg
cmpi.1 GADGETUP, d3 beq. S .gotit cmpi.1 ACTIVEWINDOW,d3 bne.s .ail
.activ lea w3gad(pc),a0 movea.1 d2,al
suba.1
a2,a2
even
INT
ActivateGadget
bra
.ail
tstxt dc.b
"Invalid Added Running
de .b
"Waiting Except Removed
.gotit
move.1
strinfo+si_LongInt(pc), d4
tasktxt de .b
"TaskProc"
even
.close
movea.1
d2, aO
INT
CloseWindow
Pri_tache:
lea
pritxt(pc),aO
.nowin
movea.1
window(a5),aO
bsr
GetNum
lea
ml(pc),al
bsr
ChecklD
CALL
SetMenuStrip
bne. S
. Err
lea
TaskReady(a6),aO
move.1
d4, dO
bsr
ChkList
movem. 1
(sp)+,dl-d7 a0-a6
beq. S
.id_ok
rts
lea
TaskWait(a6),a0
bsr
ChkList
. ************************************
beq. S
.id_ok
Affiche_taches:
lea
tasks(a5),aO
lea
m2(pc), aO
bsr
ChkList
bsr
SetMenu
bne. S
.err
lea
ttitre(pc),aO
.id_ok lea
pritxt2(pc),a0
bsr
Ptitre
bsr
GetNum
clr .w
lignes(a5)
EXEC
SetTaskPri
.err lea
txtbuff,a4
movea.1
$ 4.w, a6
bra
Affiche_tach.es
lea
AffTask(pc),a3
movea.1
ThisTask(a6), a2
pritxt dc.b
"N° de tâche :",0
lea
lg Data(a4),al
even
jsr
(a3 )
pritxt2 de .b
"Priorité : ",0
bsr
DoFmt
even
lea
TaskReady(a6),a2
Gele_tache:
bsr
AffList
lea
geltxt(pc),aO
bsr
GetNum
lea
TaskWait(a6),a2
bsr
ChecklD
bsr
AffList
bne. S
.err
empa.1
ThisTask(a6), al
lea
tasks(a5),a2
beq. S
. Err
bra
AffList
lea
TaskReady(a6),aO
bsr
ChkList
Af fTask lea
tfmt(pc),aO
beq. S
.ok_id
move. 1
a2,(al)
lea
TaskWait(a6), aO
move. 1
TC+LN_NAME(a2),18(al)
bsr
ChkList
bne. S
.okl
bne. S
. Err
move. 1
Defaut,18(al)
.ok_id CALL
Disable
bset
7, TC_STATE (al ) ; Pour Af:
. Okl
move. 1
tasktxt,4(al)
move.1
al,-(sp)
move. 1
Defaut,14(al)
CALL
Remove ; Enlève le noeud
cmpi.b
NT PROCESS,TC+LN TYPE(a2)
lea
tasks(a5),a0
bne. S
.ok2
movea.1
(sp)+,al
move. 1
pr_CLI(a2),d0
CALL
AddHead ; et le met dans :
beq. S
.ok2
CALL
Enable
addq.1
4 4(al)
.err lea
txtbuff,a4
move.1
pr TaskNum(a2), 14 (al)
bra
Af f iche_taches
lsl.l
2, dO
movea.1
d0 aO
geltxt dc.b
"Geler N° :",0
move.1
cli CommandName (aO) ,d0
even
lsl.l
2, dO
movea.1
dO,aO
Chauffe_tache:
tst .b
(aO) +
lea
tasks(a5),a0
beq. S
.okl.1
IFEMPTY
aO,.err
move. 1
aO,18(al)
.okl.l
lea
tfmt2(pc),aO
lea
chautxt(pc),aO
.ok2
moveq
0,d0
bsr
GetNum
move.b
TC_STATE(a2),dO
bsr
ChecklD
bpl. S
.okl.2 ; Si le bit 7 est positionné, bne.s
. Err
moveq
7,d0 ; alors la tâche est "gelée" lea
tasks(a5),aO
.okl.2
lsl .w
3,d0
bsr
ChkList
add.l
tstxt,dO
bne. S
.err
move.1
dO,8(al)
.ok_id CALL
Disable
move.b
TC+LN PRI(a2),dO
lea
tasks(a5),a0
ext .w
dO
move.1
al,-(sp)
move.w
d0 12(al)
CALL
Remove
rts
lea
TaskWait(a6),aO
movea.1
(sp)+,al
ttitre
de .b
.len-*
bclr
7,TC_STATE(al)
dc. b
" N° Adresse Type Etat
Pri Num No" cmpi.b
TS WAIT,TC STATE(al)
. Len
de .b
"m"
beq. S
.okl
even
lea
TaskReady(a6),aO
. Okl CALL
AddTail
tfmt
de .b
"%3d %081x %.4s %-l,1s %4d
%.3s %s",0 CALL
Enable
tfmt2
de .b
"%3d %081x %.4s %-l.7s %4d
%31d %s",0 .err lea
txtbuff,a4
Ami g a N e w s T e c h nNuméro 27 - NOV 91
lea ltitre(pc),aO
bsr Ptitre
clr.w lignes(a5)
movea.l $ 4.w,a6
lea DeviceList(a6),a2
lea AffLibs(pc), a3
bra AffList
Affiche_taches "Réchauffer N°:",0
. ************************************ t
Af f iche__libraries :
lea m3(pc),a0
bsr SetMenu
lea ltitre(pc),aO
bsr Ptitre
clr.w lignes(a5)
m4(pc),aO SetMenu ptitre(pc),aO Ptitre lignes(a5)
movea.l $ 4.w,a6
.okl
.okl.2
. Len
lfmt
lea
libtxt(pc),aO
movea.1
bsr
GetStr
empi.b
lea
strbuf(pc),al
bne. S
moveq
0,d0
move.1
EXEC
OpenLibrary
beq. S
tst .1
dO
lsl.l
bne. S
.ok
movea.1
movea.1
screen(a5), aO
move.1
INT
DisplayBeep
lsl.l
lea
txtbuff, a4
movea.1
bra
Af f iche__l ibraries
tst .b
bne. S
de .b
"Nom library : ", 0
movea.1
even
movea.1
.ok4 . Ok2
. Ok3
ptitre . Len pfmt pftxt
Bit Tâch" %2d %8",0
movea.l d0,al
CloseLibrary txtbuff,a4 Affiche libraries
CALL
lea
bra
move. 1 moveq
$ ffffff,dO 1 dl
EXEC
AllocMem
Cache_port:
lea
txtbuff,a4
lea
portxt(pc),aO
bra
Af f iche_libraries
bsr
GetNum
bsr
ChecklD

bne. S
. Err
Af fiche_devices
;
lea
PortList(a6),aO
suba.1
a0,a0
bsr
ChkList
bsr
SetMenu
bne. S
.err
movea.1 lea lea bra
Af f Libs move. 1 moveq move.b move.w move.b move.w move.b ext. W move.w move.b move.w move.1 bne. S move.1
.okl lea rts
bra
chautxt de. B even
bsr
movea.1 lea lea CALL tst. 1 beq. S
Ferme_lib:
lea
de .b de .b de .b even de .b even
Ouvre lib:
ltitre
.err
.ok
Purge_lib:
$ 4 .w, a6
LibList(a6),a2 AffLibs(pc),a3 AffList
a2, (al)
0,d0
LIB_VERSION+l(a2), dO dO,4(al)
LIB_REVISION+l(a2),dO dO,6(al)
LIB_FLAGS(a2),dO dO
dO, 8(al)
LIB OPENCNT+1(a2),d0 dO,10(al)
LIB+LN_NAME(a2),12(al) . Okl
Defaut,12(al) lfmt(pc),aO
.len-*
" N° Adresse Version Flg Cnt No" "m"
"%3d %081x %3d.%-3d $ %02x %3d %b",0
libtxt(pc),aO GetStr $ 4 .w, a6
LibList(a6),aO strbuf(pc),al FindName dO . Err
lea
lea
bsr
lea
bra
%
Af f Port move. 1 move.1 bne. S move.1 moveq move.b bpl. S moveq lsl .w add.l move.1 move.b andi.w move.w
move.1 move.1 beq. S
Affiche_ports: lea bsr lea bsr clr.w
move.1 bra. S move.1 beq. S move.1 lea rts
de .b
dc. b de .b even
dc. b even
dc. b
dc. b even
PortList(a6), a2 AffPort(pc),a3 AffList
ports(a5),a2 AffList
a2,(al)
MP+LN_NAME(a2),4(al)
.okl
Défaut,4(al)
0,d0
MP_FLAGS(a2),d0
.okl.2
4,d0
3, dO
pftxt,dO
d0,8(al)
MP_SIGBIT(a2) dû
$ ff,dO
dO,12(al)
Defaut,14(al)
MP_SIGTASK(a2),dO
.ok3
d0,a0
NT_PROCESS,TC+LN_TYPE(aO) .ok2
pr_CLI(aO),d0 .ok2 2, dO dO, aO
c1iCommandName(aO),dO 2, dO d0,a0 (aO) +
.ok4
MP_SIGTASK(a2),aO TC+LN_NAME(aO) aO aO,14(al)
.ok3
TC+LN_NAME(aO),dO .ok3
dO,14(al) pfmt(pc),aO
.len-*," N° Adresse"
" Nom Flags
«0 ii
"%3d %081x. %-16.16s %.la
"Signal Softlnt Ignore ' Action Hidden "
CALL
Disable
CALL
Remove
bset
7,MP_FLAGS(al) ; Pour Af f iche_j>ort
lea
MemList(a6),aO
move.1
al,-(sp)
movein. 1
(sp)+,dO al
CALL
Remove
move.b
dO,LN_PRI(al)
lea
ports(a5),aO
CALL
Enqueue
movea.1
(sp)+,al
CALL
Enable
CALL
AddHead
.err
lea
txtbuff,a4
CALL
Enable
bra
Affiche_memoire
lea
txtbuff,a4
bra
Af f i che_port s
memtxt
de .b even
"N° du noeud :",0
de .b
"N° du port :",0
memtxt2 de .b
"Priorité :",0
even
even
Revele_port:
. ************************************
lea
ports(a5),aO
Affiche_resources:
IFEMPTY
aO,.err
suba.1
a0,a0
lea
portxt2(pc),aO
bsr
SetMenu
bsr
GetNum
lea
ltitre(pc),a0
bsr
ChecklD
bsr
Ptitre
bne. S
. Err
clr.w
lignes(a5)
lea
ports(a5),aO
bsr
ChkList
movea.1
$ 4 .w, a6
bne. S
. Err
lea
ResourceList(a6),a2
CALL
Disable
lea
AffLibs(pc),a3
move.1
al,-(sp)
bra
AffList
lea
ports(a5),aO
CALL
Remove
. ************************************
lea
PortList(a6),aO
Affiche_modules
;
movea.1
(sp)+,al
suba.1
aO, aO
bclr
7,MP_FLAGS(al)
bsr
SetMenu
CALL
AddTail
lea
otitre(pc),aO
. Err lea
txtbuff,a4
bsr
Ptitre
bra
Af f i che_port s
clr.w
lignes(a5)
portxt2 de .b
"Montrer N° :",0
movea.1
$ 4.w,a6
even
movea.1
ResModules(a6),a2
Af fMods lea
lg Data(a4),al
• ************************************
. Next move.1
(a2)+,d0
Affiche_memoire
;
beq. S
.ret
lea
m5 (pc),aO
bgt. S
.1
bsr
SetMenu
bclr
31,dO
lea
mtitre(pc),aO
movea.1
dO, a2
bsr
Ptitre
bra. S
.next
clr.w
lignes(a5)
. 1 movea.1
d0,a0
move.1
aO,(al)
movea.1
$ 4 .w, a6
move.b
RT PRI (aO),dO
lea
MemList(a6),a2
ext .w
dO
lea
AffMem(pc),a3
move.w
d0,4(al)
bra
Af fList
move.b
RT VERSION(aO),dO
ext. W
dO
AffMem move.l
a2,(al)
move.w
dO,6(al)
move.1
MH_LOWER(a2),4(al)
move.b
RT TYPE(aO), dO
move.1
MH_UPPER(a2),8(al)
andi.w
15,dO
move.1
MH_FREE(a2),12(al)
lsl .w
2,d0
move.w
MH_ATTRIBUTES(a2),16(al)
move.1
ottab(pc,dO.w),8(al)
move.b
MH+LN PRI(a2),dO
move.1
RT_INIT(aO),12(al)
ext. W
dO
move.1
RT_NAME(aO),16(al)
move.w
dO,18(al)
bne. S
.okl
move.1
MH+LN_NAME(a2),20(al)
move.1
Defaut,16(al)
bne. S
. Ok
.okl lea
ofmt(pc),aO
move.1
Défaut,20(al)
bsr
DoFmt
.ok lea
mfmt (pc),aO
bra. S
AffMods
rts
.ret rts
mtitre
de .b
.len-*," N° Node
otitre
dc. b
.len-*," N° "
de .b
"Lower Upper Free
Attr "
de .b
"Adresse Pri Ver Type Init
de .b
"Pri No"
. Len
de .b
"m"
. Len
de .b
"m"
even
even
ofmt
de .b
"%3d %081x %3d %3d %-8s %081x %s",0
mfmt
de .b
"%3d %081x %081x %081x "
even
de .b
%81d %2d %3d %s",0
ottab
de. 1
Défaut,otxtl,0,otxt2,0,0,0,0
even
otxtl
de. 1 de .b
otxt3,otxt4,0,0,0,0,0,0 "Tâche",0
Pri_memoire;
otxt2
de .b
"Device",0
lea
memtxt(pc),aO
otxt3
de .b
"Resource",0
bsr
GetNum
otxt4
de .b
"Library",0
bsr
ChecklD
even
bne.s .err
lea memtxt2(pc),aO
bsr GetNum
movem.l dO al,-(sp)
CALL Disable
Lorsque l’on veut tracer des droites au blitter, la recherche de l’octant dans lequel se trouve la droite est souvent problématique, surtout lorsque les points définissant la droite sont calculés.
On peut toujours utiliser la routine de Frédéric Mazué, parue dans TANT N° 14, mais je vous propose d’étudier la façon dont est conçu le code de l’octant pour mettre au point une autre routine un peu plus rapide : on n’a plus à piocher dans un table, ce qui réclame beaucoup de cycles de bus (l’instruction move.w d(pc),an en réclame 24 avec un 68000!). La composition du code de l’octant apparaît comme étant assez compliquée, mais en fait elle est régie suivant une loi simple.
Soient (xl,yl) et (x2,y2) les coordonnées des point définissant la droite. Posons DX=IX1-X2I et DY=IY1-Y2I et étudions le tableau suivant.
CODE EN
Yl PAR
XI PAR
DX PAR
BINAIRE
RAPPORT À Y2
RAPPORT A X2
RAPPORT A ]
000
Y1 Y2
X1 X2
DX DY
100
Yl Y2
X1 X2
DX>DY
010
Y1 Y2
X1>X2
DX DY
101
Y1 Y2
Xl>X2
DX>DY
001
Y1>Y2
X1 X2
DX DY
110
Y1>Y2
X1 X2
y DX>DY
011
Yl»Y2
X1>X2
DX DY
111
Yl>Y2
X1>X2
DX>DY
DY
L’analyse des deux premières lignes nous montre que si DX est supérieur à DY, alors le troisième bit du code de l’octant est positionné. La ligne 3 semble nos montrer que le second bit corespond à XI par rapport à X2, mais la ligne 4 fout toute cette jolie théorie par terre. Par contre, ces deux lignes confirment l’hypothèse faite sur le troisième bit. Les quatre dernières lignes nous montrent que cette hypothèse est valable.
Nous codons donc pour l’instant l’octant sous la forme Dxx, où D est positionné selon DX et DY. Ce qui nous amène à séparer le précédent tableau en deux (c’est un très bon moyen pour y voir clair).
LE BON OCTANT
petit et le plus grand de DX et DY.
Enfin, voici un extrait de source mettant tout ceci en application. On peut le mettre à la place de la routine Recherche_octant du programme de TANT N°14.
Note : la routine Recherche_octant que je vous propose a été modifiée pour être compatible avec le programme de Frédéric Mazué. Les changements sont indiqués en commentaire avec les anciennes valeurs.
; trouve l'octant et le renvoie dans DO ;D3 contient Pdeltat et D2 Gdelta ;D0 à D3 sont modifies ; L'octant est deja decale de deux sur ;la gauche (on a plus qu'a faire un or.w
Recherche_octant Move. W XI,DO Move. W Y1,Dl Move.w X2,D2 Move. W Y2, D3 DO,. . )
Sub.w Bpl. S Neg.w Moveq
D1,D3 Yl_inf_Y2 D3
;si Yl est plus petit ;DY>0
;A=1 (cf article),si l'on veut utiliser ;a la place de celle de l'ANT N°14 mettre
4, Dl cette routine
Bra.s Test_X 1 dans Dl Yl_inf_Y2
Moveq 0,D1 Test_X
;si Yl Y2 alors A=0 X2 ;Idem pour X
;B=l,on peut mettre 1 dans DO pour utiliser routine dans le programme de l'ANT N°14
Sub.w DO,D2
Bpl.s Xl_inf_
Neg.w D2 Moveq 4,DO
cette
Bra. S Test_D
Xl_inf_X2
Moveq 0,D0 Test_D
Cmp. W D3, D2
Bpl.S DY_sup_DX Lsl.b 1,Dl Or.b Dl,DO Or.b 16,DO met cette
;1AB dans DO,mettre Or.b 4,DO si l'on
routinne dans le Programme de l'ANT N°14
Move.b DO,octant modification pour assurer D2,GDelta ;la compatibilitee,ajoutez D3,PDelta ;ces trois lignes pour utiliser ;le programme de l'ANT N°14
Move.w Move.w Rts DY_sup_DX Exg.w Lsl Or.b Move.b Move.w Move.w Rts
D3,D2 1, DO Dl, DO DO,octant D2,GDelta D3,PDelta
;OBA dans DO ?cf ci dessus
tracer de droite au blitter,programme de l'ANT 14 la routine recherche_octant a ete modiefièe
library** equ -408 equ -414 equ -198 equ -210 equ -150 equ -156 lybrary* * equ -456 equ -462 library** equ -198 equ -66
$ 58
$ 48
$ 4C
$ 50
$ 54
$ 60
$ 62
$ 64
$ 66
$ 40
$ 42
$ 44
$ 46
$ 00
$ 70
* *exec. OldOpenLibrary CloseLibrary AlloocMem FreeMem SuperState UserState **graphies
OwnBlitter DisownBlitter **intuition OpenScreen CloseScreen
* *CHIPS**
BLTSIZE
BLTCPTH
BLTBPTH
BLTAPTH
BLTDPTH
BLTCMOD
BLIBMOD
BLTAMOD
BLTDMOD
BLTCONO
BLTCON1
BLTAFWM
BLTALWM
BLTDDAT
BLTCDAT
equ
equ
equ
equ
equ
equ
equ
equ
equ
equ
equ
equ
equ
equ
equ
Et que voit-on ? Tout simplement que l’octant peut être codé sous la forme 1AB ou OBA ! Et comment le voit-on ? Très simple.
Pour A d’abord. Dans le tableau de gauche, on constate que sur les deux premières lignes le premier bit est à 0 alors qu’il est mis pour les lignes 3 et 4, ce qui correspondrait à A=1 si Y1>Y2 et A=0 si Y1 Y2. En se ruant sur l’autre tableau, on s’aperçoit que le premier bit ne joue plus ce rôle et Ton regarde, à tout hasard, si le second ne corespond pas... Et là, ô miracle, il corespond. Tout s’enchaîne alors et l’on s’aperçoit que le bit restant évolue aussi selon la loi magique :
B=1 si X1>X2 et B=0 si Xl X2.
Avec un peu de recul, on décide plus généralement que le code de l’octant est de la forme XYZ avec X=F(DX,DY), Y=G(Pdelta) et Z=H(Gdelta), où Pdelta et Gdelta sont respectivement le plus
en supposant DX>DY
en
supposant DX DY
code bin
Yl, Y2
XI, X2
1 aode bin
Yl, Y2
XI, X2
000
Y1 Y2
X1 X2
100
Y1 Y2
X1 X2
010
Yl Y2
X1>X2
101
Yl Y2
Xl>X2
001
Y1>Y2
X1 X2
110
Y1>Y2
X1 X2
011
Y1>Y2
X1>X2
111 :
Yl>Y2
X1>X2
A mi g a N e w s T e c hwviNuméro 27 - NOV 91
8
BLTBDAT
equ $ 72
Moveq
4, Dl
BLTADAT
equ $ 74
decalage
DMACON
equ $ 96
Bra. S
Test_X
DMACONR
equ $ 02
dans Dl Yl_inf_Y2
Début
Moveq
0, Dl
Move.1
$ 4,A6
Test_X
Lea
graf_name,Al
Sub.w
D0,D2
Jsr
OldOpenLibrary(A6)
Bpl. S
Xl_inf X2
Move.1
D0,graf_base
Neg.w
D2
Lea
int_name, Al
Moveq
4, D0 ;B:
Jsr
OldOpenLibrary(A6)
le decalage
Move.1
D0,int_base
Bra. S
Test_D v
Move.1
int_base,A6
BLTCON1
Lea
écran,A0
Xl_inf_X2
Jsr
OpenScreen(A6)
Moveq
0, D0
Move.1
D0,canal écran
Test_D
lea
$ dff000,A5
Cmp. W
D2,D3
Bsr
Blitter_Busy
Bpl. S
DY_sup DX
Move.1
graf_base,A6
Lsl.b
1, Dl
Jsr
OwnBlitter(A6)
Or.b
Dl, D0
Bsr
Recherche__octant
Or.b
16,D0
;A=1 (cf article),si l'on veut éviter le ;lors de la préparation de BLTCON1 mettre 4
; si Yl Y2 alors A=0
Bsr adresse_depart Move.1 DO,BLTCPTH(A5) Move.1 DO,BLTDPTH(A5)
:_X2 ;Idem pour X
TestJD ;vers la gauche lors de la préparation de
;1AB dans DO,si on a fait les modifications lors de
Bsr Jsr
souris Lea Btst Bne. S Move.1 Move.1 Jsr
Move.1 Move.1 Jsr
Move.1 Jsr Rts
Blitter_Busy
Btst 14,DMACONR(A5)
Bne.s Blitter_Busy Rts
Re c he rche_oc t ant Move. W XI, DO Move. W Yl,Dl Move.w X2,D2 Move.w Y2,D3 Sub.w D1,D3 Bpl. S Yl_inf_Y2 Neg.w D3
Dc.l titre_ecran De . 1 0 Dc.l 0 titre_ecran
Dc.b 'Little STORM Blitter demo',0 Even octant dc.b 0 Even
0
150 300 100
XI
Yl
X2
Y2
De .w De .w De .w De .w Gdelta Dc.w 0 Pdelta Dc.w 0 end
; trouve l'octant et le renvoie dans D0 ;D3 contient Pdeltat et D2 Gdelta ;D0 à D3 sont modifies
; si Yl est plus petit ; DY>0
;la préparation de Dl et DO alors mettre or.b 16,DO au lieu de or.b 4,DO
Move.w
40,BLTCMOD(A5)
Move.b
D0,octant modification pour assurer
Move.w
40,BLTDMOD(A5)
Move.w
D3,PDelta ;la compatibilitee,enlevez
Clr.l
D0
Move.w
D2,GDelta ;ces trois ligne et traitez
Clr.l
Dl
Rts
,*le résultats directement
Clr.l
D2
DY_sup_DX
Clr.l
D6
Exg.w
D3,D2
Clr.l
D7
Lsl
1, D0
Move.w
Pdelta,D0
Or.b
Dl,D0 ;0BA dans D0
Move.w
Gdelta,Dl
Move.b
D0,octant modification pour assurer
Muls
4, D0
Move.w
D3,PDelta ;la compatibilitee,enlevez
Move.w
D0,BLIBMOD(A5)
Move.w
D2,GDelta ces trois ligne et traitez
Move.w
D0, D2
Rts
le résultats directement
Muls
2 , Dl
adresse, départ
Sub.w
Dl, D0
Move.w
XI, D0
Roxl.w
7, D7
Ext. 1
DO
Move.1
D0,BLTAPTH(A5)
Divs
8, D0
Move. W
D2,D0
And. 1
$ ffff,D0
Muls
2, Dl
Move.w
Yl, Dl
Sub.w
Dl, D0
Muls
40,Dl
Move.w
D0,BLTAMOD(A5)
Add.l
Dl, D0
Move. W
$ 8000,BLTADAT(A5)
Move.1
canal_ecran,Al
Move. W
$ ffff,BLTBDAT(A5)
Move.1
$ c0(Al),Dl
Move. W
$ ffff,BLTAFWM(A5)
Add.l Dl
,D0
Move. W
$ ffff,BLTALWM(A5)
Rts
Move.w
XI, D0
image De.1 0
And.w
$ 000f,D0
canal_ecran
DC. l 0
Ror .w
4, D0
int_base De.
1 0
Or .w
D0,D6
graf_base De
.1’ 0
Or .w
D0,D7
int_name De.
B 'intuition.library',0
Or .w
$ 0bca,D6
Even
Move. W
D6,BLTCONO(A5)
graf_name De
• b 'graphies.library',0
Move.b
octant,D0
Even
Ext .w
D0
écran
Roi .w
2, D0
De .w 0
Bset
0, D0
De. W 0
Or .w
D0, D7
Dc.w 320
Bset
1, D7
De.w 200
Move.w
D7,BLTCONl(A5)
Dc.w 2
Move.w
Gdelta,D0
Dc.b 0
add.w
$ 01,D0
Dc.b 1
Muls
64,D0
Dc.w 2
Add.w
00002,D0
De .w 15
Move.w
D0,BLTSIZE(A5)
Dc.l 0
Blitter_Busy DisownBlitter(A6)
$ bfe001,A5 6, A5) souris int_base, A6 canal_ecran,AO CloseScreen(A6) $ 04,A6 int_base,Al CloseLibrary(A6) graf_base,Al CloseLibrary(A6)
On ne voit pas très bien à priori à quoi des interruptions logicielles peuvent bien servir... Alors qu’il est souvent primordial de pouvoir réagir au quart de millionième de seconde près au VBL ou à la fin d’une transmission série, un petit délai (dû à un grand nombre de tâches tournant concuremment) dans un protocole de communication entre deux processus n’est que très rarement pénalisant. En fait, c’est justement là leur utilité : permettre de synchroniser le plus justement possible deux tâches entre elles.
Le principe est très simpe : étant donné que les messages sont chaînés les uns à la suite des autres dans un port, un message particulier peut mettre un certain temps avant d’être traité par la tâche réceptrice. Or, dans certains cas, il est primordial que ce message soit immédiatement pris en compte, quitte à ce que les autres patientent un peu, bien au chaud qu’ils sont dans leur liste d’attente. Pour ce faire, on décide de suspendre provisoirement le système multitâche - grâce à une interruption, donc - et de gérer tout de suite l’événement intervenu. On le voit, les interruptions logicielles agissent exactement comme les interruptions matérielles, à ceci près qu’elles sont générées non pas par le hardware, mais par le software (en fait, elles utilisent le niveau d’interruption 1, bit 2 de INTENA).
* Initialisation de l'interruption *
SoftInt.is_Node.ln_Type = NTINTERRUPT;
Softint.is_Node.ln Pri = 0; * par exemple *
SoftInt.is_Code = (VOID (*)())SoftCode;
Softlnt.is_Data = (APTR)SoftData;
* Initialisation du port *
SoftlntPort.mp_Node.ln_Type = NT MSGPORT;
SoftlntPort.mp_Node.ln_Name = "Softlnt port"; SoftlntPort.mp_Flags = PA_SOFTINT;
SoftlntPort.mp_SigTask = (struct Task *)&SoftInt;
AddPort(&SoftIntPort);
Ensuite, chaque message envoyé sur ce port déclenchera l’interruption logicielle concernée. Notez qu’en assembleur, il existe un champ supplémentaire dans la structure MsgPort, baptisé MP_SOFTINT, qui permet de différencier à l’écriture du programme un port d’interruption d’un port de message. Sa valeur est égale à celle de MP_SIGTASK.
EMPLOI AVEC LES DEVICES
Comme les interruptions matérielles, les interruptions logicielles ne peuvent appeler qu’un nombre réduit de fonctions du système. Une particularité est à noter concernant les devices : normalement, il est interdit d’utiliser DoIO() et BeginIO() dans une routine d’interruption, mais le timer.device et l’audio.device font exception à cette règle. Celà permet par exemple de relancer directement une requête de l’un de ces devices depuis l’interruption, sans que le programme principal n’ait besoin d’intervenir.
MISE EN OEUVRE
Une interruption logicielle s’initialise exactement de la même manière qu’une interruption matérielle, au moyen d’une structure Interrupt correctement remplie. Attention ici à bien spécifier dans le noeud, le type NT_INTERRUPT et non NT_SOFTINT, qui est un flag interne à Exec. Seules cinq priorités sont possibles : -32, - 16, 0, +16 et +32 ; toute autre valeur est illégale. Les champs is_Code et is_Data sont initialisés comme pour une interruption matérielle (cf. Le mois dernier).
Une fois délenchée, l’interruption logicielle s’exécute dans un environnement identique à celui des interruptions matérielles : mode superviseur, retour au handler d’Exec par l’instruction RTS et protection obligatoire des registres A2-A4 et D2-D7. Si elle survient en cours de fonctionnement "normal" du système (mode multitâche), l’interruption est exécutée immédiatement ; sinon, si elle est déclenchée au cours d’une autre interruption logicielle, elle devra attendre que celle-ci se termine ; enfin, si elle est délenchée au cours d’une interruption matérielle, elle devra attendre que tous les serveurs éventuels restant à exécuter aient fini leur travail. Ceci permet par exemple à une interruption de haut-niveau de se décharger d’une tâche ingrate et gourmande en temps-machine sur une autre interruption de priorité moindre.
Il existe deux moyens de délencher une interruption logicielle : soit en appelant la fonction Cause(), soit en postant un message dans un port de type PA_SOFTINT.
Dans le premier cas, on transmet à Cause() l’adresse de la structure Interrupt concernée et l’interruption se déclenche suivant les conditions ennoncées plus haut.
Dans le second cas, on initialie un MsgPort comme illustré ci- dessous :
struct MsgPort SoftlntPort = 0}; struct Interrupt Softlnt = 0};
L’EXEMPLE
Comme le veut la tradition, nous allons terminer cette étude des interruptions logicielles par un petit exemple. En l’occurence, il ne s’agit ni plus ni moins que d’un petit compteur de 1 2 secondes. On aurait certes pu le programmer d’une autre manière, mais c’est la seule idée de programme qui me soit passé par la tête...
; Exemple (tout-à-fait inutile) d'interruption logicielle.
; L'interruption compte les 1 2 secondes...
; Le programme principal attend simplement un CTRL C puis ; affiche le résultat du comptage.
; S.
Schreiber
- 171190
incdir
"include:"
include
11 exec interrupt s . I11
include
"devices timer.i"
include
"libraries dos.i"
include
"exec exec_lib.i"
include
"intuition intuition_lib.i
include
"libraries dos_lib.i"
OFF
EQU 0
; Compteur OFF
ON
EQU 1
; Compteur ON
STOP
EQU 2
; Compteur arrêté
DELAIEQU 50000; Mb micro-secondes (1 2 seconde)
• ************************************

Startlea dosname(pc), al; Ouvre la dos.library moveq 0,dO
p
0
sous
CALLEXEC OpenLibrary move.l dO _DOSBase
beq NoDos
CALLDOS Output; Pour communiquer...
move.1 dO,stdout
move.1 dO,dl; Salut !
Move.1 text,d2
moveq 11en,d3 CALLDOS Write
lea tireq(pc),al; Ouvre le timer.device
move.b NT_MESSAGE,IO+MP+LN_TYPE(al)
move.1 SoftintPort,IO+MN_REPLYPORT(al)
lea tiname(pc),a0
moveq UNIT_MICROHZ,dO
moveq 0,dl
CALLEXEC OpenDevice
tst .bdO
bne NoTimer
cmpi.b ON,iflag; On s'arrête ?
Beq.sbegintr ; Pas encore, relance la requête move.b STOP,iflag; Oui, prévient Main
rts
; Lancement d'une requête timer pour DELAI microsecondes .
ATTENTION :
Ca ne marche qu'avec le timer.device ou 1'audio.device
On ne peut pas appeler les autres devices depuis une interruption, même logicielle !
Begintr lea tireq(pc),al
move.w TR_ADDREQUEST,IO_COMMAND(al)
move.1 DELAI,IOTVTIME+TV_MICRO(al)
BEGINIO rts
lea SoftlntPort(pc),al CALLEXEC AddPort
• ************************************ 9
; Variables
dos.library fenêtre CLI compteur
buffer pour RawDoFmt
bsr begintr ; Lance la 1ère requête...
* ****** Programme principal : attente de CTRL C (!)
Main move.1 SIGBREAKF_CTRL_C,dO
CALLEXEC Wait
* ***** C'est tout !!!
Move.1 counter(pc),longout
move.b OFF,iflag; Demande à l'interruption de
Exit cmpi.b STOP,iflag; s'arrêter et attend qu'elle
bne.sExit ; le fasse
lea nbfmt(pc),aO; Affiche le compteur
lea counter(pc),al
lea putche(pc),a2
lea charout(pc),a3
CALLEXEC RawDoFmt
lea SoftlntPort(pc),al CALLEXEC RemPort
lea tireq(pc),al; Ferme le timer.device CALLEXEC CloseDevice
NoTimer movea.l _DOSBase(pc),al; Ferme la dos.library
CALLEXEC CloseLibrary
NoDos moveq 0, dO rts
; Retour au CLI
* ***********************************
9
putche movem.l d0-d3 a6,-(sp); J'avais pas envie de bufferiser !
Move.b dO,(a3)
move.l stdout(pc),dl
move.1 a3, d2
moveq 1,d3
CALLDOS Write
movem.l (sp)+,d0-d3 a6
rts
_DOSBase de.1 0
stdout de. 1 0
counter dc.l 0
longout dc.l 0
charout de. B 0
iflagdc.b ON ; flag d'arrêt
; MsgPort de type PA_SOFTINT SoftlntPort:
de.1 0,0 ; ln_Succ, ln_Pred
de. B NT MSGPORT,0 ; ln_Type, ln_Pri de. 1 pName ; InJName
dc. b PA_SOFTINT,0; mp_Flags, mp_SigBit
dc. l Softint ; mp_SigTask
dcb.bLH_SIZE ; mp_MsgList
; structure Interrupt
Softlnt dc.l 0,0 ; ln_Succ, ln_Pred
dc. b NT_INTERRUPT,0; ln_Type, ln_Pri
dc. l siName ; ln_Name
dc. l 0,SoftCode; is_Data, is_Code
tireqdcb.bIOTV_SIZE ; IORequest pour le timer.device
dosname DOSNAME
tiname TIMERNAME
pName dc.b "Horloge. Port ", 0
siName dc.b "Horloge.interrupt", 0
text dc.b "L'interruption logicielle compte les demi-"
dc. b "secondes.10,"Pressez CTRL C pour arrêter.",10 tien EQU 1-text
nbfmtdc.b "Résultat du comptage : %ld",10,0 even
* ***********************************
9
END
Par Stéphane Sehreihei
série dont le but est de vous initier à l’utilisation de programmes compatibles Unix et de vous faire découvrir un certain nombre d’utilitaires du Domaine Public qui de par leur qualité, valent bon nombre de produits du commerce.
• caractère permet d’indiquer le caractère séparateur de champs.
• programme indique le nom du programme Awk à exécuter.
• indique la fin des options. Ceci est très utile pour autoriser les noms des arguments des programmes sources à commencer par le caractère
• fichier correspond à la liste des fichiers à traiter.
LANGAGE OU PAS LANGAGE ?
Nous allons commencer ce mois-ci par Gawk (abréviation de GNU Awk), un langage de traitement de fichiers ASCII. C’est l’aboutissement de la réécriture de l’utilitaire Unix Awk dans le cadre du projet GNU (d’ou sont nom de Gawk), projet visant à améliorer les fonctionnalités et la portabilité des commandes Unix sur d’autres systèmes d’exploitation, tout en restant compatible avec les anciennes.
Le langage Awk a été écrit à l’origine par trois célèbres acteurs du monde Unix, à savoir Aho, Weinberger et Kernighan (vous savez, un des papas du C). Gawk est compatible avec la version System V release 4 d’Unix.
FONCTIONS ET SYNTAXE
La documentation Unix officielle décrit la fonctionnalité de Awk (et par extension de Gawk) comme une commande permettant de réaliser des recherches sur des lignes spécifiques d’un fichier ASCII et d’effectuer des traitements en fonction du résultat de ces recherches.
Pour expliquer un peu plus cette définition, je dirais que Gawk est un logiciel de traitement des chaînes de caractères d’un fichier ASCII, plus puissant que grep (1), dans la mesure où il autorise la réalisation de traitements sur ces chaînes de caractères là où grep se contente de les visualiser.
Les différentes fonctionnalités de Gawk sont :
• Traitement de données numériques.
• Utilisation de variables et de tableaux unidimensionnels.
• Contrôle des flots de données d’entrée.
• La sélection et utilisation de patterns (2) de recherche.
• Besoin d’aucun compilateur d’aucune sorte pour l’analyse des traitements à effectuer.
Gawk lit chaque fichier d’entrée dans l’ordre indiqué lors du passage de la commande (cf. Description de la syntaxe ci-dessous). Si le nom du fichier est ’-’ où s’il n’existe aucun nom de fichier alors Gawk lit l’entrée standard (typiquement l’entrée clavier).
Voyons de manière rapide comment cela fonctionne.
La commande Gawk travaille ligne par ligne. Pour chaque ligne, elle cherche à détecter la correspondance entre la ligne et la liste des patterns définies par l’utilisateur. Quand elle trouve l’occurence d’une pattern dans la ligne, elle exécute l’action associée à cette pattern et renvoie le résultat sur la sortie standard (par défaut l’écran).
La syntaxe de la commande telle qu’elle est décrite dans le manuel est la suivante :
Gawk [-F caractère] [-f Programme] [ ] [fichier...]
Gawk ressemble plus à un véritable langage qu’à un simple utilitaire dans la mesure où l’on est amené à programmer des actions précises devant survenir si certaines conditions sont réunies, et ce à l’aide d’un langage structuré basé sur le couple pattern actions. Pour nous en persuader, voyons plus en détails le mécanisme de traitement de Gawk et la manière dont on peut le programmer.
Gawk commence par lire et contrôler la syntaxe de toutes les paires "pattern actions" du programme. Si aucune erreur de syntaxe n’est détectée, il transforme le programme en une série d’instructions internes (aucun résultat de cette pseudo compilation ne sort de la commande). A ce moment, le traitement ligne par ligne des fichiers spécifiés peut commencer.
Gawk lit une ligne du fichier d’entrée et la compare à chaque pattern du programme, effectuant l’action associée dans le cas où le test de comparaison est vrai. Il passe ensuite à la prochaine ligne, et continue ainsi jusqu’à la dernière du dernier fichier d’entrée.
La commande Gawk considère chaque ligne du fichier d’entrée comme une suite de champs séparés par un caractère particulier et se terminant par une fin de ligne. Par défaut, le caractère servant de séparateur entre deux champs est le caractère Espace>. Il est toutefois possible de le changer par celui de votre choix.
En effet, ce séparateur est contenu dans une variable spéciale FS (Field Separator). Pour changer de séparateur il vous suffit d’affecter à FS autre chose que le blanc ’ ’. Ainsi, l’instruction Gawk
fs =
définit le point comme séparateur.
Un séparateur peut également être une chaîne de caractères avec ou sans pattern(s) de sélection. Chaque champ de la ligne est stocké à la lecture dans une variable. Pour accéder à un champ particulier, il faut donc lire la variable correspondante. Ce sont les variables $ 1 pour le premier champ, $ 2 pour le deuxième et ainsi de suite jusques $ 99, qui est le maximum autorisé. A noter que pour accéder à l’ensemble des champs on utilisera la variable $ 0. Par exemple, l’affectation de ’2’ au troisième champ d’une ligne deviendra :
$ 3 = '2'
Parlons maintenant plus en détails de la syntaxe du langage. Comme je vous l’ai dit, l’élément de base du langage est la séquence pattern actions. Il est toutefois possible de se passer de l’un de ces deux éléments : s’il n’y a pas de pattern, on effectue l’action sur toutes les lignes du fichier, et s’il n’y a pas d’action, Gawk se contente d’écrire la ligne complète sur la sortie standard.
Une action au sens de Gawk est une suite d’instructions régies par les mêmes règles syntaxiques que le C. Voici un détail des principaux ordres de Gawk.
If (expression) instruction [instruction] while (expression) instruction [instruction] for (expressionl; expression2; expression3) break
print [expression]
printf [format] [expression]
next
delete Array[expression] (détruit un tableau d'éléments) exit[expression] (sortie immédiate du programme)
LES VARIABLES
Pour être complet sur la description de Gawk, il nous faut voir maintenant les variables et la manière dont elles sont gérées.
Les variables de Gawk sont purements dynamiques. C’est-à-dire qu’elles n’existent qu’à partir de leur première assignation. Elles ne sont pas non plus typées dans le sens ou elle prennent le type de ce qu’on leur affecte. Les valeurs qu’elles contiennent peuvent être réelles, entières ou de type chaîne de caractères. Gawk supporte les tableaux unidimensionnels seulement, mais il est très simple de simuler plusieurs dimensions par un habile décalage d’indice. Gawk possède également bon nombre de variables réservées qu’il initialise et assigne dynamiquement de manière transparente pour l’utilisateur. Passons en revue quelques une de ces variables réservées du langage qui, vous allez pouvoir en juger, sont très utiles.
• FS : caractère indiquant la séparation de deux champs, par défaut il s’agit du caractère espace>.
• NF : nombre de champs dans la ligne courante (abréviation anglaise de Number of Fields).
• NR : numéro de la ligne courante (abréviation anglaise de Number of Records).
• FNR : numéro de la ligne courante dans le fichier courant.
PETITS EXEMPLES SIMPLES
Pour terminer cette étude du langage Gawk, nous allons voir quelques exemples des fonctionnalités et de la puissance de traitement du langage.
Exemple 1 : ce premier exemple va permettre la visualisation de toutes les lignes d’un fichier nommé toto, dont la taille est supérieure à 72 caractères. Cet exemple est des plus simples et ne nécessite même pas d’écrire un programme complet ; on passera directement la commande sous la forme :
Gawk 'length > 72' toto
La fonction length, lorsqu’elle ne possède pas de paramètres, renvoie la longueur de la ligne d’entrée.
Exemple 2 : cette fois on complique un peu. Nous allons visualiser toute les lignes du fichier toto comprises entre deux mots-clefs ’start’ et ’stop’. La commande à envoyer à Gawk pour faire le traitement est donc la suivante :
Gawk ' start , stop ' toto
Par H err Doktor Vtm GhitenSthnmelïmDoï
L’indication d’une chaîne de caractères se fait en la limitant par deux slashes.
Exemple 3 : nous allons maintenant réaliser notre premier vrai programme, que nous appelerons somme.awk. Il traite un fichier toto en additionnant tous les nombres correspondant à la deuxième colonne de chaque ligne du fichier.
Voici le fichier d’exemple.
121 12 14.5
126 14 18.5
12 14 125
le programme à réaliser est donc le suivant :

somme += $ 2
}
END

print " somme : ", somme; print " moyenne ; ", somme NR;
}
La première action ajoute la valeur du second champ de chaque ligne à la variable somme. Le mot clé END situé avant la deuxième action, indique qu’elle ne devra être exécutée qu’après que toutes les lignes de tous les fichiers d’entrée aient été traitées par la première. Enfin, NR est la variable réservée contenant le nombre total de lignes lues.
Exemple 4 : pour notre dernier exemple, nous allons inverser les colonnes du fichier toto, c’est-à-dire inverser les champs de chaque ligne.
Le programme est très simple, le voici : awk ' print $ 3, $ 2, $ 1 >' titi
Voilà pour les exemples, il ne nous reste plus qu’à savoir où trouver le logiciel.
OU TROUVER Gawk ?
Gawk est disponible, vous vous en doutez bien, dans le domaine public. La version que j’ai testée se trouve sur la disquette Fish numéro 406. Outre le programme proprement dit, vous trouverez également une documentation (en anglais) dans le plus pur style "man" d’Unix, ainsi qu’un certain nombre de fichiers textes expliquant le travail des auteurs. Pour ceux qui voudraient en savoir plus sur Awk, je vous conseille la "bible" de ce langage : "the Awk Programming Language", écrit par les trois auteurs d’Awk eux-mêmes et édité par Bell Téléphoné Laboratories, Incorporated.
En conclusion, je vous recommande fortement d’étudier ce petit langage de traitement de fichiers ASCII qu’est Gawk. Le mois prochain, nous continuerons notre exploration du monde Unix avec Flex, la version Amiga du célèbre pré-processeur C, très utile pour générer (entre autres choses) des analyseurs syntaxiques, j’ai nommé Lex. A bientôt donc, si vous le voulez bien !
Le mois dernier, je vous avais promis le scrolling le plus rapide possible. Voici qui est prometteur, mais beaucoup d’entre vous se sont peut-être dit que ce n’était là qu’un moyen malhonnête d’attirer l’attention du lecteur...
SCRQLLING "KICK-OFF"
Je viens de me rendre compte qu’il est possible que certains croient que je parle de la technique très répandue dans le domaine des démos, qui consiste à créer un gros bitplan et à faire bouger l’écran dessus... Cette pratique est non seulement très simple à programmer, mais aussi très rapide en temps d’exécution (en fait, plus rapide que la technique dite Kick-Off), mais les contraintes mémoire qu’elle entraîne la rendent inutilisable dans un jeu digne de ce nom.
Le gros avantage du scrolling que je vais vous décrire, provient du fait qu’il permet de ne gérer que les nouveaux blocs de 16 x 16 pixels et donc, si l’on n’affiche que le strict nécessaire, on ne peut pas faire moins (à l’exception de ne rien faire, et pour cela je ne connais que la technique du gros bitplan).
Après cette introduction, je vais vous expliquer plus précisément comment programmer un tel scrolling et quelles sont ses limites.
LE PRINCIPE
Tout d’abord, le scrolling horizontal. L’écran doit être défini à la taille voulue, en ajoutant un modulo de 2 octets permettant d’utiliser le scrolling hard (registre BPLCON1). Pour procéder au scrolling, on calcule la direction en comparant la position actuelle (variables ori_x, ori_y) à l’ancienne (variables old_ori_x, old_ori_y). Si le scrolling va vers la droite, on affiche une nouvelle colonne de blocs à droite de l'écran et s’il va vers la gauche, on l’affiche devinez où ? Hé bien oui, à gauche, bien joué, vous reviendrez en deuxième semaine.
Pour voir le scrolling bouger, on incrémente les pointeurs bitplans pour la droite et on les décrémente pour la gauche, mais uniquement quand cela est nécessaire, c’est-à-dire tous les 16 pixels. Le spectateur a ainsi l’impression que l’écran tout entier bouge dans la direction voulue. Comme vous l’avez sûrement deviné, on peut aller très loin dans la mémoire comme cela et c’est en effet le seul défault de cette technique : il n’est pas infini en horizontal. Par exemple, pour un scrolling sur 80 écrans de large, il faut prévoir 80 lignes suplémentaires en bas de la page. Ce défault n’est pas vraiment excessif, compte tenu des avantages. De plus, avec le double-buffering qui est nécessaire dans la majorité des jeux ou autres aplications, nous ne sommes pas forcés d’avoir deux fois ce supplément. En effet, quand une page va descendre, l’autre aussi, donc si on espace un peu les 2 pages pour ne pas qu’elles se chevauchent au moment des changements de direction, le supplément de ligne n’est que de 80 pour les deux pages.
Passons maintenant au scrolling vertical, qui est lui plus efficace car infini. Il est en fait la partie forte de la technique grâce à la manière originale dont il est géré. Il a besoin pour pouvoir s’exprimer pleinement, d’une hauteur de playfield égale à celle de l’écran plus 16 pixels. Tout se fait grâce au Copper : quand le scroll descend, on fait descendre le pointeur des bitplans sur la page. Apres avoir descendu 16 pixels, la page n’est plus assez grande pour descendre encore de cette manière. Quand le scrolling est dans cette position, on remarque que l’on ne se sert pas des 16 premiers pixels de la page, donc la ruse consiste à afficher la nouvelle ligne de blocs à cet endroit. Pour donner l’illusion au spectateur que le scrolling continue normalement, on interrompt au moment opportun le balayage-écran avec le Copper pour changer les pointeurs qui montrent la zone contenant les nouveaux blocs.
Dans cette posture, on peut encore descendre de 16 pixels. Arrivé à cette nouvelle limite, on affiche encore une nouvelle ligne en bas de l’écran, donc en fait sur le haut de la page, entre les lignes 16 et 32, et ainsi de suite pour le reste de la descente.
Quand le premier pointeur arrive en bas de la page, on le fait remonter tout en haut et on peut continuer comme cela très longtemps. Pour faire monter le scroll, le procédé est identique, à l’exception du pointeur bitplan qui monte et de la nouvelle ligne de blocs qui est affichée en haut de l’écran.
On détecte dans un premier temps la direction de la même manière que dans le scrolling horizontal pour savoir si on affiche une ligne de blocs en haut ou en bas de l’écran.
REALISATION
Ceux qui ont tout suivi se sont très probablement aperçus que le scrolling vertical empêchait l’utilisation du scrolling horizontal tel qu’il a été décrit plus haut. Pour avoir un scrolling multidirectionnel, il faut obtenir une parfaite compatibilité entre les deux. Pour ce faire, on va modifier le scrolling horizontal et plus précisément la routine qui affiche les nouvelles colonnes : on fait en sorte que le premier bloc de la colonne s’affiche à l’endroit que montre le premier pointeur bitplan. On affiche chaque bloc en dessous de l’autre, en testant à chaque fois si Ton arrive en bas de la page ; si oui, on affiche les prochains blocs en haut de la page. De cette façon, on peut avoir un scrolling tout-à- fait intéressant, car il permet un déplacement de 16 pixels en X et en Y simultanément, le tout en cinquante rasterlines pour un écran de 320 x 256 en 4 bitplans.
Dans le cas de l’utilisation du double-buffering, le pas maximum descend à 8 pixels en X et en Y.
Voila, c’est fini ; le mois prochain vous aurez très probablement droit à quelque chose plus proche de la démo que du jeu, comme par exemple une routine de plasma ou un ciel étoilé multidirectionnel.
La phrase du mois : "si vous n'affrontez pas vos terreurs en face, elles vous grimperont dans le dos".
* * LISTING DE JEROME ETIENNE POUR L'ANT
* * sur le scroll KICK OFF
OPT C-
incdir
7include:'
include
7exec exec_lib.i7
include
7hardware custom.i
custom
$ dff000
execbase -
4
BPL_X
320
BPL_Y =
192
BPL_DEPTH =
4
MODULO
2
BPL_WIDTH =
BPL_X 8 +MODULO
BPL_SIZE =
BPL_WIDTH*BPL_Y
SCR_SIZE
BPL_SIZE*BPL_DEPTH
MAP_WIDTH =
35
MAP_HEIGHT=
35
BLK_WIDTH =
16
BLK_HEIGHT=
16
SCROLLSPEED
8
vsync: macro .wait_vsync @:
move.1
vposr(a5),d0
and.l $ lff00,d0 cmp.l $ 11000,d0 bne.s.wait_vsync @ endm
wait bit : macro
btst 14,dmaconr(a5)
* flag d'erreur desactive "graphies.library",0
¦
. Loop_wait_blt @:
btst 14,dmaconr(a5)
bne.s .loop_wait_blt @ endm
MOD: MACRO , 2 modulo
. MODULO_GO_ON G : emp.w 2,
bit.s .MODULO_IS_FINISH @ sub.w 2, bra.s .MODULO_GO_ON G .MODULO_IS_FINISH @ :
ENDM
* chiffre a moduler 2 base du
* ************* programme principal move.l (execbase).w,a6 lea custom,a5 CALLEXEC Forbid
move.w $ 03e0,dmacon(a5)* ail dma off except disk move.w (BPL_DEPTH 12)+ $ 200,bplconO(a5) clr.w bplconl(a5) clr.w bplcon2(a5)
move.w BPL_WIDTH*(BPL_DEPTH-1),bpllmod(a5) move.w B P L_WIDTH *(BPL_DEPTH-1),bpl2mod(a5) move.w $ 2981,diwstrt(a5) * move.w $ e9c0,diwstop(a5) move.w $ 0030,ddfstrt(a5) move.w $ 00d0,ddfstop(a5) bsr build_coplist
move.l coplist_adr,copllc(a5) * >run my coplist clr.w copjmpl(a5) *
move.w $ 83c0,dmacon(a5) * dma blit,cop & bpl move.w $ f00,color+15*2(a5) move.w $ 0f0,color+l*2(a5) move.w $ 00f,color+6*2(a5)
bsr INIT_FIRST_SCR main_loop: vsync
move.w -1,color(a5) bsr INIT_SCR_PT_ADR bsr SCROLL bsr build_coplist
bsr CHANGE_DIRECTION_WITH_KEYBOARD tst.w ORI_X
bge.s .ORI_X_NE_SORT_PAS_A_GAUCHE clr.w ORI_X . ORI_X_NE_SORT_PAS_A_GAUCHE
cmp.w MAP_WIDTH*16-BPL_X-1,ORI_X ble.s .ORI_X_NE_SORT_PAS_A_DROITE move.w MAP_WIDTH*16-BPL_X-1,ORI_X . ORI_X_NE_SORT_PAS_A_DROITE tst.w ORI_Y
bge.s .ORI_Y_NE_SORT_PAS_EN_HAUT clr.w ORI_Y . ORI_Y_NE_SORT_PAS_EN_HAUT
emp.w MAP_HEIGHT*16-BPL_Y-1,ORI_Y ble.s .ORI_Y_NE_SORT_PAS_EN_BAS move.w MAP_HEIGHT *16-BPL_Y-1,ORI_Y . ORI_Y_NE_SORT_PAS_EN_BAS clr.w color(a5) move.b $ bfec01,d0* not dO ror.b l,d0 cmp.b $ 45,d0 *
beq init_end * btst 6,$ bfe001 bne main_loop
* ******* înît end ***********
* réactivation de l'old coplist init_end:
wait_blt
lea GfxName (pc), al * nom de la library ds al moveq 0,d0 * version 0 (the last)
CALLEXEC OpenLibrary * lib graphique ouverte
move.l d0,a4 * adr de graphiebase ds a4
move.l 38(a4),copllc(a5)* chargement de l'adr de clr.w copjmpl(a5) * l'old coplist et lancement
move.w $ 83e0,dmacon(a5)* activation des canaux dma CALLEXEC Permit * multi switching autorise
* ****************
* >ecran 320*192
* >avec un mot en plus
* pour le shift
* >capture the key wich is pressed
* and put is code RAW in dO
sort si on press sur esc
fin: moveq 0,d0 rts
GfxName: dc.b even
CHANGE DIRECTION WITH KEYBOARD:
move.b
$ bfecOl,dO *
not
dO * >capture the key pressed
ror.b
l,d0 * and put is code RAW in dO
ext .w
dO
lea
TAB_DELTA_X_Y_ACOORDING_TO_KEYBOARD,a 0
.TRY_ANOTHER
. KEY:
tst. W
(aO)
bit. S
.TST KEY FINISH
emp .w
(aO),d0
bne. S
.NOT_THIS_KEY
move.w
2(aO),d0
add.w
dO,ORI_X
move.w
4(aO),dO
add.w
dO,ORI_Y
bra. S
.TST_KEY_FINISH
.NOT_THIS_KEY:
addq.1
6, aO
bra. S
.TRY_ANOTHER_KEY
.TST_KEY_FINISH:
rts
S SET
SCROLL_SPEED
TAB_DELTA_X_
Y_ACOORDING_TO_KEYBOARD:
dc. w $ 3f,s,-s,$ 3e,0,-s,$ 3d,-s,-s,$ 2d,-
s,0,$ 2f,s,0,
$ ld,-s,s
dc. w $ le,0,s,$ lf,s,s
de. W -1
INIT_SCR_PT_
ADR : move.1LOG_SCR_ADR,a 0
move.w
ORI X,dO
and.w
$ fff0,dO
lsr .w
3, dO
lea
(aO,dO.w),aO
move.1
aO,SCR_PT_ADR
rts
SCROLL:
move.w ORI_Y,dO
move.w
BPL Y+BLK HEIGHT,dl
MOD
d0,dl
move.w
dO,START FIRST PART
neg. W
dO
add.w
BPL_Y+BLK_HEIGHT,dO
emp .w
BPL_Y,d0
ble. S
.FIRST_PART_SIZE_NOT_BIGGER_THAN_SCREEN
move.w
BPL_Y,d0
.FIRST_PART_
SIZE_NOT_BIGGER_THAN_SCREEN
move.w
dO,FIRST_PART_SIZE
move.w
ORI_Y,dO
add.w
BPL_Y,dO
move.w
BPL_Y+BLK_HEIGHT,dl
MOD
dO, dl
move.w
dO,END_SECOND_PART
lea
MAP,aO
move.w
ORI X,dO
lsr .w
4, dO
lea
(aO,dO.w),aO
move.w
ORI_Y,dO
lsr .w
4, dO
mulu
MAP_WIDTH,dO
add.l
dO, aO
move.1
aO,MAP_PT
WAIT_BLT
move.w
$ 0 9 f 0,BLTCONO(a5)
clr. W
BLTCONl(a5)
move.w
-1,BLTAFWM(a5)
move.w
-1,BLTALWM(a5)
move.w
BPL_WIDTH-2,BLTDMOD(a5)
clr .w
BLTAMOD(a5)
* maintenant
on affiche les blk pour le scroll en x
move.w
ORI_X,dO
cmp.w
OLD_ORI_X,dO
ble. S
.DONT_SCROLL_TOWARD_RIGHT
move.w
OLD_ORI_X,dO
add.w
BPL X-l,dO
and.w
$ fff0,dO
move.w
ORI_X,dl
add.w
BPL X-l,dl
and.w
$ fff0,dl
emp .w
dO, dl
beq.s .DONT_SCROLL_TOWARD_RIGHT
move.1 SCR_PT_ADR aO
move.w STARTFIRST_PART,dO
and.w $ fff0,dO
mulu BPL_WIDTH*BPL_DEPTH,dO
add.l dO,aO
lea BPL_WIDTH-MODULO(aO),aO
move.1 MAP_PT,al add.l BPL_X 16,al bsr AFF_A_COLUMM_OF_BLK
.DONT_SCROLL_TOWARD_RIGHT: move.w ORI_X,dO cmp.w OLD_ORI_X,dO bge.S .DONT_SCROLL_TOWARD_LEFT move.w OLD_ORI_X,dO and.w $ fff0,dO move.w ORI_X,d1 and.w $ fff0,dl cmp.w d0,dl
beq. S .DONT_SCROLL_TOWARD_LEFT move.1 SCR_PT_ADR,aO move.w START_F1RST_PART,d0 and.w $ fff0,d0 mulu BPL_WIDTH*BPL_DEPTH,dO add.l d0,a0 move.1 MAP_PT,al bsr AFF_A_COLUMM_OF_BLK .DONT_SCROLL_TOWARD_LEFT:
* maintenant on affiche les blk pour le scroll en y move.w ORI_Y,dO cmp.w OLD_ORI_Y,dO ble.S .DONT_SCROLL_TOWARD_DOWN move.w OLD_ORI_Y dO add.w BPL_Y-1,dO and.w $ fff0,dO move.w ORI_Y,dl add.w BPL_Y-1,dl and.w $ fff0,dl cmp.w d0,dl
beq.S .DONT_SCROLL_TOWARD_DOWN
move.1 SCR_PT_ADR aO
move.w END_SECOND_PART,dO
and.w $ fffO,dO
mulu BPL_WIDTH*BPL_DEPTH dO
add.l d0,a0
move.1 MAP_PT,a1
add.l (BPL_Y 16)*MAP_WIDTH,al bsr AFF_A_LINE_OF_BLK .DONT_SCROLL_TOWARD_DOWN: move.w ORI_Y,dO cmp.w OLD_ORI_Y dO bge.S .DONT_SCROLL_TOWARD_UP move.w OLD_ORI_Y dO and.w $ fff0,d0 move.w ORI_Y,dl and.w $ fff0,dl cmp.w d0,dl
beq.S .DONT_SCROLL_TOWARD_UP move.1 SCR_PT_ADR, aO move.w START_F1RST_PART,dO and.w $ fff0,dO mulu BPL_WIDTH*BPL_DEPTH,dO add.l d0,a0 move.1 MAP_PT, a1 bsr AFF_A_LINE_OF_BLK .DONT_SCROLL_TOWARD_UP:
move.w ORI_X OLD_ORI_X move.w ORI_Y,OLD_ORI_Y rts
ORI_X:
ds. W
1
* coor du coin en haut a
gauche
ORI_Y:
ds .w
1
*
OLD_ORI_X:
ds .w
1
OLD_ORI_Y:
ds .w
1
START_F I RS T
_PART:
ds .w
1
END_SECOND_
PART:
ds .w
1
FIRST_PART
SIZE:
ds. W
1
MAP_PT:
ds. 11
AFF_A_COLUMM_OF_BLK: * IN: a0= adr ou on va poser les
blk
* al= adr de la map ou on va chercher quel blk afficher
* OUT: rien
lea TAB_ADR_BLK_GFX,a2
moveq 0,d2 move.w BPL_Y 16-1+1,dO .LOOP_AFF_EACH_BLK:
move.b (al) dl
lea MAP_WIDTH(al),al
ext. W dl
lsl.w 2,dl
move.l (a2,dl.w),dl
WAIT_BLT
move.l aO,BLTDPT(a5) move.l dl,BLTAPT(a5) move . W BLK_HEIGHT*BPL_DEPTH*64+BLK_WIDTH 16,BLTSIZE(a5) lea BLK__HEIGHT*BPL_WIDTH*BPL_DEPTH ( aO ) , aO
tst.w d2
bne.s .SECOND_PART_ALREADY_BEGIN move.l SCR_PT_ADR,a3 add. 1 (BPL_Y+BLK_HEIGHT-1)*BPL_WIDTH*BPL_DEPTH,a3 cmp.1 a3, aO
ble.s .FIRST_PART_NOT_TOTALLY_PRINT sub.1 (BPL_Y+BLK_HEIGHT)*BPL_WIDTH*BPL_DEPTH,aO
moveq -l,d2 .SECOND_PART_ALREADY_BEGIN .FIRST_PART_NOT_TOTALLY_PRINT:
dbf dO,.LOOP_AFF_EACH_BLK
rts
END_AFF_A_COLUMM_OF_BLK
AFF_A_LINE_OF_BLK:
* *****************************************************
* IN: a0= adr ou on va poser les blk
* al= adr de la map ou on va chercher quel blk
afficher
* OUT: rien
lea TAB_ADR_BLK_GFX,a2
moveq BPL_WIDTH 2-l,dO .LOOP_AFF_EACH_BLK:
move.b (al)+,dl ext.w dl lsl.w 2,dl move.l (a2 dl.w) dl WAIT_BLT
move.1 aO,BLTDPT(a5) move.l dl,BLTAPT(a5) move . W BLK_HEIGHT*BPL_DEPTH*64 +BLK_WIDTH 16,BLTSIZE(a5) addq.l BLK_WIDTH 8,aO dbf dO,.LOOP_AFF_EACH_BLK rts
END_AFF_A_LINE_OF_BLK
INIT_FIRST_SCR:
* *******************************************************
clr.w ORI_X clr.w ORI_Y clr.w OLD_ORI_X clr.w OLD_ORI_Y
* on init les registres blitter qui resteront ainsi durant toute la routine
WAIT_BLT
move.w $ 09f0,BLTCONO(a5) clr.w BLTCONl(a5) move.w -1,BLTAFWM(a5) move.w -1,BLTALWM(a5) move.w BPL_WIDTH-2,BLTDMOD(a5) clr.w BLTAMOD(a5)
* maintenant on affiche le premier écran
move.1 LOG_SCR_ADR,a 0 move.l PHY_SCR_ADR,a3 lea MAP,al lea TAB_ADR_BLK_GFX,a2 moveq BPL_Y 16+1-1,d2 .LOOP_AFF_EACH_LINE:
move.w BPL_WIDTH 2-l,dO . LOOP_.AFF_EACH_BLK :
move.b (al)+,dl ext.w dl lsl.w 2,dl move.l (a2,dl.w),dl WAIT_BLT
move.l aO,BLTDPT(a5)
move.l dl,BLTAPT(a5)
move.w 16*BPL_DEPTH*64+1, BLTSIZE(a5) addq.l 2,a0 WAIT_BLT
move.1 a3,BLTDPT(a5) move.l dl,BLTAPT(a5)
move.w 16*BPL_DEPTH* 64 + 1,BLTSIZE(a5) addq.l 2,a3
dbf dO,.LOOP_AFF_EACH_BLK
lea 16*BPL_WIDTH*BPL_DEPTH-BPL_WIDTH(aO),aO
lea 16*BPL_WIDTH*BPL_DEPTH-BPL_WIDTH(a3),a3
lea MAP_WIDTH-BPL_WIDTH 2(al),al dbf d2,.LOOP_AFF_EACH_LINE rts
ENDINITFIRSTSCR
build_coplist:
move.1 COPLIST_ADR(pc),a 0 move.w ORIX,dO and.w $ 000f,dO neg.w dO add.w $ f,dO move.w dO,dl lsl.w 4,d0 or.w dl,dO move.w BPLCONl,(a0)+ move.w dO,(aO)+
move.l SCR_PT_ADR,d2 move.w START_FIRST_PART,dO mulu BPL_WIDTH*BPL_DEPTH,dO add.1 dO,d2 moveq BPL_DEPTH-1,dO move.w bplpt,dl .loop_init_BPL_in_clist move.w dl,(aO)+ addq.l 2,dl swap d2 move.w d2,(aO)+ swap d2 move.w dl, (aO) + addq.l 2,dl move.w d2,(aO)+ add.1 BPL_WIDTH,d2
dbf dO,.loop_init_BPL_in_clist
moveq $ 29,dO add.w FIRSTPARTSIZE,dO lsl.w 8,d0 or.w $ 0001,dO move.w dO,(aO)+ move.w $ fffe,(a0)+ move.l SCRPTADR,d2 moveq BPL_DEPTH-1,dO move.w bplpt,dl . Loop_init;_BPL_in_clistl move.w dl,(aO)+ addq.l 2,dl swap d2 move.w d2,(aO)+ swap d2
move.w dl,(aO)+ addq.l 2,dl move.w d2,(aO)+ add.1 BPL_WIDTH,d2 dbf dO,.loop_init_BPL_in_clistl
move.l $ fffffffe,(aO)+* montre la fin de la clist rts
END_bui1d_cop1ist
TAB_ADR_BLK_GFX: de . 1 BLK0_GFX,BLK1_GFX,BLK2_GFX
SCR_PT_ADR: ds. 1 1
LOG_SCR_ADR: de . 1 ecranl
PHY_SCR_ADR: de . 1 ecran2
COPLIST_ADR: de . 1 coplist
MAP : dc.b 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
dc. b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
dc. b 0,0,0,1,1,1,0,0,0,1,0,0,0,0,0,1,0,1
dc. b 1,1,1,1,1,1,0,0,0,0,0,1,1,0,0,0,0
dc. b 0,0,1,0,0,0,1,0,0,1,1,0,0,0,0,1,0,0
dc. b 0,0,1,0,0,0,0,0,0,0,1,1,1,1,0,0,0
dc. b 0,0,1,0,0,0,1,0,0,1,0,1,0,0,0,1,0,0
de .b
0,0
1
0
0
0
0
0
0
0
0
1
1
0
0
0
0
de .b
0,0
1
1
1
1
1
0
0
1
0
0
1
0
0
1
0,0
de .b
0,0
1
0
0
0
0
0
0
0
0
1
1
0
0
0
0
de .b
0,0
1
0
0
0
1
0
0
1
0
0
0
1
0
1
0,0
de .b
0,0
1
0
0
0
0
0
0
0
0
0
0
0
0
0
0
de .b
0,0
1
0
0
0
1
0
0
1
0
0
0
0
1
1
0,0
de .b
0,0
1
0
0
0
0
0
0
0
0
1
1
0
0
0
0
de .b
0,0
1
0
0
0
1
0
0
1
0
0
0
0
0
1
0,0
de .b
0,0
1
0
0
0
0
0
0
0
0
1
1
0
0
0
0
de .b
0,0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0,0
de .b
0,0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
de .b
0,0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0,0
de .b
0,0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
de .b
0,0
1
1
1
1
0
0
0
0
0
0
1
0
0
0
1,0
de .b
0,0
0
0
1
0
0
0
1
1
0
1
0
0
0
1
0
de .b
0,0
1
0
0
0
0
1
0
0
1
0
1
0
0
0
1,0
de .b
0,0
0
1
0
1
0
1
0
0
0
1
1
0
1
1
0
de .b
0,0
1
1
1
0
0
1
0
0
1
0
1
0
0
0
1,0
de .b
1,1
0
1
0
1
0
0
1
0
0
1
0
1
0
1
0
de .b
0,0
1
0
0
0
0
1
0
0
1
0
1
0
0
0
1,0
de .b
0,0
0
1
1
1
0
0
0
1
0
1
0
0
0
1
0
de .b
0,0
1
0
0
0
0
0
1
1
0
0
1
1
1
0
1,1
de .b
1,0
0
1
0
1
0
1
1
0
0
1
0
0
0
1
0
de .b
0,0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0,0
de .b
0,0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
de .b
0,0
0
0
0
0
0
0
0
0
1
1
0
0
1
0
1,0
de .b
1,1
1
0
1
1
1
0
0
0
0
0
0
0
0
0
0
de .b
0,0
0
0
0
0
0
0
0
1
0
0
1
0
1
0
1,0
de .b
0,1
0
0
1
0
0
1
0
0
0
0
0
0
0
0
0
de .b
0,0
0
0
0
0
0
0
0
1
0
0
0
0
1
1
1,0
de .b
0,1
0
0
1
1
1
0
0
0
0
0
0
0
0
0
0
de .b
0,0
0
0
0
0
0
0
0
1
0
0
1
0
1
0
1,0
de .b
0,1
0
0
1
0
0
0
0
0
0
0
0
0
0
0
0
de .b
0,0
0
0
0
0
0
0
0
0
1
1
0
0
1
0
1,0
de .b
1,1
1
0
1
0
0
0
0
0
0
0
0
0
0
0
0
de. B
1,0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0,0
de .b
0,0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1
de .b
0,0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0,0
de .b
0,0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
de .b
0,0
1
1
1
1
0
0
0
0
0
0
1
0
0
0
1,0
de .b
0,0
0
0
1
0
0
0
1
1
0
1
0
0
0
1
0
de .b
0,0
1
0
0
0
0
1
0
0
1
0
1
0
0
0
1,0
de .b
0,0
0
1
0
1
0
1
0
0
0
1
1
0
1
1
0
de .b
0,0
1
1
1
0
0
1
0
0
1
0
1
0
0
0
1,0
de .b
2,2
0
1
0
1
0
0
1
0
0
1
0
1
0
1
0
de .b
0,0
1
0
0
0
0
1
0
0
1
0
1
0
0
0
1,0
de .b
0,0
0
1
1
1
0
0
0
1
0
1
0
0
0
1
0
de .b
0,0
1
0
0
0
0
0
1
1
0
0
1
1
1
0
1,1
de .b
1,0
0
1
0
1
0
1
1
0
0
1
0
0
0
1
0
de .b
0,0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0,0
de .b
0,0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
de .b
0,0
0
0
0
0
0
0
0
0
1
1
0
0
1
0
1,0
de .b
1,1
1
0
1
1
1
0
0
0
0
0
0
0
0
0
0
de .b
0,0
0
0
0
0
0
0
0
1
0
0
1
0
1
0
1,0
de .b
0,1
0
0
1
0
0
1
0
0
0
0
0
0
0
0
0
de .b
0,0
0
0
0
0
0
0
0
1
0
0
0
0
1
1
1,0
de .b
0,1
0
0
1
1
1
0
0
0
0
0
0
0
0
0
0
de .b
0,0
0
0
0
0
0
0
0
1
0
0
1
0
1
0
1,0
de .b
0,1
0
0
1
0
0
0
0
0
0
0
0
0
0
0
0
de. B
0,0
0
0
0
0
0
0
0
0
1
1
0
0
1
0
1,0
de .b
1,1
1
0
1
0
0
0
0
0
0
0
0
0
0
0
0
de .b
1,0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0,0
de .b
0,0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1
sectionGFX,DATA_C BLK0_GFX : REPT 16
dc. w -1,-1,-1,-1 ENDR
BLK1_GFX : REPT 16
de . W -1,0,0,0
ENDR
BLK2_GFX : REPT 16
dc. w 0,-1,-1,0 ENDR
sectionZONECHIP,BSS_C ds. 1 1
ecranl: ds.1 (scr_size*2) 4
ecran2: ds.1 (scr_size*2) 4
coplist: ds. 1 1000*4 * taiile inconnue donc on
prévoit gros end
Par Jerôme Etienne
MEMOIRE DU MO '
UBYTE cmd[128), c, *ptr, *adr;
UliONG blocs[32}, tailles [32} , taille; SHORT bloc, nbjblocs =0, i;
BOOLi fini = F ALSE; struct MemChunk *mc;
for (i = 0; i 32; i + + )
blocs[i] = tailles[i} = -IL;
while ('.fini)
puts(belp);
Tjpper=$ 9o0 8 lx, mh->mh Upper,
mh-
) ;
Le premier commence par réserver un buffer de 16 Ko et lui attribue un MemHeader particulier. Ainsi, on peut user de ce buffer exactement comme s'il s’agissait de la mémoire principale de l’ordinateur, à ceci près que seules les fonctions AllocateO et Deallocate() sont utilisables. Ce programme est interactif, c’est-à-dire que c’est vous qui décidez des opérations à suivre (allocation ou libération d’un bloc quelconque). Le programme affiche à chaque fois l’état du MemHeader, les différents MemChunks qui peuvent résulter de la fragmentation du buffer, ainsi que l’adresse et la taille des différents blocs alloués.
L’utilisation en est très simple, bien qu’entièrement au clavier. Vous entrez une lettre de commande suivi d’un nombre x. La lettre peut être A, ce qui demande d’allouer x octets dans le buffer, ou bien L, ce qui demander de libérer le bloc numéro x. ou enfin Q, ce qui met fin au programme. Faites attention à ne bien entrer que des nombres décimaux, aucune vérification particulière n’étant faite à ce niveau.
Pour illustrer nos nouvelles connaissances sur la gestion de la mémoire par Exec, je vous propose deux petits programmes C pour le prix d'un seul.
For (i = 0, me = mh->mh_First; me; me = mc- mc_Next)
printf("MemChunk %2d, Next = $ 3&081x, Bytes=$ %081x n"
i++, mc->mcJNext, mc->mc_Bytes);
printf(" n"); for (i = 0; i nbjblocs; i++)
printf("Bloc %2d, adresse: $ %081x, taille: $ '3&081x n" ,
i, blocs(i), tailles[i});
printf("MemHeader ;"); printf (" Lower=$ '3&081x, Free=$ 9&081x n n" ,
mh->mh Lower,
mh_Free);
printf(" n[AnlLx|Q) > gets(ptr = cmd);
include stdio.h> ttinclude stdlib.h> include string.h> include exec types.h>
include exec mémory.h>
ifdef LATTICE include proto exec.h> endif
define IsDigit(c) ((c) >= define BLOCSIZE 0x4000
0' ScSc (c) = '9')
UBYTE help[]=" fAn=Allouer n octets, "
"Lx=Libérer bloc n°x, "
"Q=Quitter n";
void main(void);
void memdemo(struct MemHeader *mh) ;
void main(void)

struct MemHeader *mh; struct MemChunk *mc;
if (mh = (struct MemHeader *)AllocMem(sizeof(*mh), MEMF_CLEAR))
if (me = (struct MemChunk *)AllocMem(BLOCSIZE, 0)) mh->mh_Node.ln_Type = NT MEMORY;
mh->mh Node.ln Name = "MemDemo.chunck";
while (c = *ptr++)
. Switch (c 1 0x20)
( case 'q': * Quitter *
f ini = TRUE ; break;
case 'a': * Allouer *
c = *ptr++;
while (1IsDigit(c)) c = *ptr++; taille = c - '0';
c = *ptr++; while (IsDigit(c))
taille = (taille * 10) + (c - '0'); c = *ptr++;
}
* ptr = ' 0';
if (nbjblocs 32)
. If (taille)
adr = Allocate(mh, taille); if (adr)
printf("Allocation de %ld octetsXn",
taille)
blocs[nbjblocs) = (ULONG)adr; tailles(nbjblocs) = taille; nb_blocs++;
else puts("L'allocation a échoué 1");
}
else puts("Taille incorrecte.");
= me;
= (APTR)mc;
= (APTR)((ULONG)mc + BLOCSIZE) = BLOCSIZE;
= NULL;
= BLOCSIZE;
mem demo(mh); FreeMem((APTR)mc, BLOCSIZE);
}
FreeMem((APTR)mh, sizeof(*mh));
}
exit(0);
void memdemo(struct MemHeader *mh)
else puts("Nombre maximum de bloc atteint break;
mh->mh_First mh->mh Lower mh->mhUpper mh->mh Free
mc->mc Next mc->mc_Bytes
case '1'; * Libérer *
case 'f'; c = *ptr++;
while (1IsDigit(c)) c = *ptr++; bloc = c - '0';
c = *ptr++; while (IsDigit(c))
( bloc = (bloc * 10) + (c - '0'); c = *ptr++;
}
* ptr = ' 0';
if (bloc nbjblocs)
if (blocs(bloc) 1= -IL)
A m i g a N e w .v T e c liVilANmnéro 2 7 - NOV 91
JDE (II)
printf ( "Libération du bloc n°%>ld n n",
P
?
Bloc);
Deallocate(mh, (APTR)blocs[bloc], tailles[bloc]);
for (i = bloc; i 32; i++)
blocs[i] = blocs[i+1];
tailles[i] = tailles[i + 1] ;
}
- -nbblocs;
}
else put s (11 Bloc non réservé.");
}
else puts("Numéro de bloc invalide."); break;
}
}
}
}
ChipMemFirst
Ce tout petit programme est un petit truc pour vous permettre de modifier la priorité d'allocation de la mémoire Chip. Il se contente de changer la priorité de la liste CHIP dans la structure ExecBase. Ainsi, et sous réserve qu'il y en ait suffisamment, ce sera en priorité de la mémoire Chip qu' AllocMem( ) et consoeurs alloueront. Cela peut être utile par exemple, pour charger une ancienne démo qui refuse de tourner correctement avec une extension de mémoire.
Par défaut, la nouvelle priorité est de 10, ce qui devrait être amplement suffisant dans la plupart des cas (Exec l'initialise à -10, la Fast Ram étant à 0). Mais il peut arriver que certaines extensions, notament 32 bits sur les cartes 68030, aient des priorités supérieures. C'est pourquoi il est possible de préciser en paramètre sur la ligne de commande, la nouvelle priorité à appliquer à la mémoire Chip.
include stdio.h>
include stdlib.h>
include string.h>
include exec types.h>
include exec mémory.h>
include exec execbase.h>
ifdef LATTICE include proto exec.h>
endif
extern struct ExecBase *SysBase;
void maintint argc, char **argv)

int newpri = 10; struct MemHeader *mh;
if (argc > 1)
newpri = atoi(argv[1]);
Disable();
if (mh = FindName(&SysBase->MemList, "Chip Memory")) Remove((struct Node *)mh); mh->mh_Node.ln_Pri = newpri;
Enqueue(&SysBase->MemList, (struct Node *)mh);
}
Enable();
ExecMaster (suite de la page 17)
r
Affiche
_interr:
suba.1
aO, aO
bsr
SetMenu
lea
ititre(pc),aO
bsr
Ptitre
clr. W
lignes(a5)
movea.1
$ 4 .w, a6
lea
IntVects(a6), a6
move.1
intypes,d3
move.w
%1110001111111000,dl
moveq
15,d2
Afflnts btst
d2, dl
beq. S
. Serv
.handl
lea
lg Data(a4),al
move.1
a6,(al)
move.1
IV_CODE(a6),4(al)
beq. S
.nxtint
move.1
IV_NODE(a6),dO
beq. S
.nxtint
movea.1
dO, aO
move.b
LN PRI(aO),d0
ext. W
dO
move.w
dO,8(al)
move.w
'H',10(al)
move.1
d3,12(al)
move.1
LN_NAME (aO ) , 16 (al)
bne. S
. Okl
move.1
Defaut,16(al)
. Okl
lea
ifmt(pc),aO
bsr
DoFmt
bra. S
.nxtint
. Serv
move.1
IVDATA(a 6),a2
lea
AffServ(pc),a3
bsr
AffList
.nxtint addq.1
8,d3
lea
IV_SIZE(a6),a6
dbra
d2,Afflnts
rts
AffServ move.l
a2,(al)
move.1
IV_CODE(a6),4(al)
move.b
LN PRI(a2),dO
ext. W
dO
move.w
dO,8(al)
move.w
'S',10(al)
move.1
d3,12(al)
move.1
LN_NAME(a2),16(al)
bne. S
.ok2
move.1
Défaut,16(al)
. Ok2
lea
ifmt (pc),aO
rts
ititre
de .b
.len-*
de .b
" N° Node Code Pri Type
. Len
de .b
"m"
even
ifmt
de .b
"%3d %081x %081x %3d %c %-l .7s %s",
even
intypes de .b
"TBE DSKBLK SOFTINT PORTS
de .b
"COPER VERTB BLIT AUD0
de .b
"AUDI AUD2 AUD3 RBF
de .b
"DSKSYNC EXTER INTEN NIM
even

Affiche
_Quitter

st
fini(a5)
rts

VARS
dcb.b
VARSIZE
dosname de .b
"dos.library",0
intname de .b
"intuition.library",0
gfxname de .b
"graphies.library",0,0
Défaut
de .b
"--------" 0,0
No"

include "em_windows.s" include "em_menus.s"

section BUFFER,BSS
txtbuff ds.b lg_SIZE*100 ; ça doit être assez sinon boum ! END
Par Max
Par Philippe Vautrin
Une petite lettre plus que sympathique est parvenue à la rédaction d Amiga Revue, celle d’un jeune roumain qui, se sentant isolé, recherche des contacts de programmation.
Séquence émotion.
Cher Monsieur, je suis un jeune roumain passionné d'informatique. Très heureux possesseur d'un ordinateur Amiga 1000, j’avais commencé d’étudier le langage A-BASIC de cet ordinateur. Seulement avec cet A-BASIC, et sans avoir accès à d'autres programmes, je me sens un peu désorienté. Mais le problème est que je n'ai pas la possibilité de communiquer et d'échanger des idées avec d’autres possesseurs d’ordinateur.
Pour ça je veux, bien sûr si possible, entrer en contact avec d’autres possesseurs d'Amiga qui pourraient m'initier à la technique de programmation en Assembleur et en C, et dans la création graphique. Mon adresse est
Câlin Butiu
Str Giurgiu NR 15 AP 35 1900 Timisoara Roumanie
Avec mes amitiés, merci.
Cette lettre nous est parvenue telle quelle, c’est-à-dire dans un français presque parfait (à quelques fôtes d’aurthografe près). Nous espérons très sincèrement que vous serez nombreux à répondre à Câlin et qu’il pourra rapidement progresser sur l’Amiga.
Zi g Herr Doktor... Shaitan et moi-même, fervants lecteurs d'Amiga NewsTech, avons un problème en C : comment générer un nombre aléatoire sans rentrer dans une prise de tête mathématicale, simplement en utilisant les fonctions contenues dans notre Lattice
5. 0... Notre mémoire se souvient de ces temps reculés, à l’heure de nos premiers balbutiements informatiques, où une simple commande A = RND(I) suffisait pour obtenir, d'une manière réellement aléatoire, une valeur entre 0 et 0,99. Mens c'était Page de la micro-magnon et du Basic. Aujourd'hui, c'est C et les micro-sapiens que nous sommes appellent à l'aide : qu'est-ce donc que ces fonctions rand(), drand()... qui renvoient inlassablement la même suite de nombres ?
Cyrille Giquello (MAD), Issy les Moulineaux
Désolé si ce n’est pas le Doktor qui répond, mais il est en train de se la couler douce sur un bateau, quelque part au large des côtes françaises, là où y'a du soleil même en septembre... On prend ses vacances quand on peut.
D’abord, cette bonne vieille instruction Basic RND() ne renvoyait pas un nombre si aléatoire que ça, mais plutôt pioché "au harsard" dans une table... Et encore, l’expression "au hasard" est tout-à-fait subjective, puisqu'une suite de calculs autant mathématiques que précis donnait l’indice du nombre dans la table...
D'ailleurs, les fonctions rand() et drand() agissent exactement de la même manière, à ceci près qu’il ne s’agit plus de piocher dans une table, mais de calculer directement le nouveau nombre aléatoire. Je ne rentrerai pas dans les détails de la formule, qui peut varier d’un compilateur C à un autre, mais l’avantage d’une telle méthode est simplement de permettre à un programmeur, pour des raisons de débogage. De re-créer la même suite de "nombres aléatoires" autant de fois qu'il le désire.
Ces deux fonctions agissent de la même manière : une valeur de départ, baptisée "graine" (seed en anglais), sert de base à la génération de la suite de nombres. Si cette graine ne change pas entre plusieurs exécutions du programme, la suite non plus.
Il est fort heureusement possible de modifier cette graine, en appelant la fonction srand(new_seed), ce qui correspond en gros à ce bon vieux RANDOMIZE du Basic. Si la nouvelle graine est une constante, la suite "aléatoire" ne le sera bien sûr pas vraiment... Mais il est possible de lui donner une valeur (presque) quelconque avec le petit bout de code suivant :
USHORT seed = *(USHORT *)0xdff006; srand(seed);
rand() renvoie un entier de type USHORT. Drand() un réel de type DOUBLE, etc. Chaque bibliothèque du Lattice possède sa propre fonction rand(), en fonction du type des données manipulées. Consultez votre documentation.
Un dernier point sur les nombres aléatoires avec le Lattice : Frédéric Mazué avait mis en avant et corrigé un bug des fonctions drand48() et erand48()... Ca se passait dans TANT numéro 22 du mois de mai 1991.
Bonjour à tous. Si vous êtes en manque d'imagination pour créer de nouvelles rubriques dans TANT, je me fais un plaisir de vous soumettre quelques idées :
• Infographie : 20, dessin vectoriel, 3D, Ray Tracing...
• Programmation : création d'un langage (interpréteur, compilateur, linker)...
• Algorythmie (votre confrère ST Magazine disposait d'une excellence série sur ce sujet).
• Mathématiques scientifiques et financières.
• etc, etc.
Vous le voyez, ce sont là des sujets généraux, plutôt que bien arrêtés sur le système d'exploitation (Intuition, Exec). Il y a matière à beaucoup d'articles, qui seront sans aucun doute appréciés de n om h re ux lecte u rs.
Laurent Cacléac, Rennes
Oui. Ce sont là de bonnes idées, mais nous pensons qu’avant d'aborder des sujets aussi généraux, il faut d’abord savoir programmer la machine elle-même ! Celà dit, nous sommes conscients du fait qu’on ne vas pas pouvoir vous parler éternellement des fenêtres, des Bobs ou des interruptions... C’est pourquoi nous préparons d'ores et déjà de nouvelles rubriques, parmi lesquelles la télématique, la compression de données, des algorythmes divers et variés, les micro-processeurs 68020 et
68030... Comme disait le prophète, "chaque chose en son temps et les vaches seront bien gardées".
Bonjour à toute la rédaction. Cette lettre s’adresse plus spécialement à votre spécialiste de l'assembleur, Max : j’ai besoin d’un petit conseil sur la meilleure façon de trier des données en assembleur. Je sais que tu as déjà publié un article sur le sujet dans un ancien numéro de TANT, mais celà ne concernait que le "tri à bulles", qui est loin d’être assez rapide dans mon cas. Pourrais-tu expliquer d’autres méthodes plus rapides, par exempte le Quick-Sort ou bien le Shell- Sort ? Je ne les connais que de nom mais on m’a certifié que c’étaient les méthodes les plus rapides.
Stéphane Rouget, Paris
Excellente idée, je me mets au travail imméditament. J'espère pouvoir vous proposer de telles routines d'ici deux ou trois mois. Désolé ne pas pouvoir en parler dans ces pages, mais celà prendrait vraiment trop de place.
Max
Salut à toute l'équipe de TANT. Je vous écris pour vous signaler que je ne connais absolument rien au C et qu 'il y en a beaucoup trop dans- vôtre journal (qui est pourtant sensass !). J'espérais trouver plus de programmes en assembleur et notamment des exemples de programmation de démos ou des effets bien particuliers. Ca ne m'empêche pas de renouveller mon abonnement, mais si vous pouviez faire un petit effort...
TLP
Messieurs, j’apprécie beaucoup votre revue, bien que les trois-quarts soient incompréhensibles pour moi... Le langage C et l’assembleur me sont parfaitement inconnus et je ne suis que plutôt médiocre en Basic G FA. J'espérais, à traver votre revue, pouvoir progresser dans ce tangage et, même si je dois reconnaître apprendre beaucoup sur le système d'exploitation de l'Amiga, je suis plutôt déçu. Avez-vous l'intention prochaine de réparer cette injustice ?
AMOS (suite de la page 5)
Alain Souillant, Blois
Bravo pour votre magazine, qui est de loin ce qui se fait de mieux en la matière. On pourra regretter une PAO timide, quelques fautes d'orthographe ou de français qui arrivent encore à se glisser de ci et de là (beaucoup moins qu'au début en tout cas !), mais le contenu est îout-à-fait ce que j'en attendais. Programmeur professionnel en Turbo C et Turbo Pascal (sur un IBM AT 386...), je voulais seulement vous dire que l'Amiga NewsTech soutient largement la comparaison avec des revues similaires sur PC mais vendues en kiosque, à un prix équivalent et parfois plus cher. Continuez sur cette voie et un jour, qui sait, peut-être pourrons-nous trouver Amiga NewsTech dans les rayons de notre libraire habtuel. C'est en tout cas tout le mal que je vous souhaite. Au fait, une dernière chose : c'est très gentil à vous et fort intelligent de fournir des langages du domaine public tel que le Pascal ou le Fortran... Mais si vous n'en parlez pas dans le magazine, la disquette ira vite se faire oublier au fond d'un tiroir...
Eric Pochant, Epinay
Ces trois lettres ne sont qu'un tout petit échantillon représentatif du courrier que nous recevons... J'aimerais une fois de plus rappeler qu’il est difficile de contenter tout le monde, même avec la meilleure volonté.
Deux rubriques sont entièrement consacrées à la programmation en mode "démo", c’est-à-dire sans utiliser le système d'exploitation. Ce sont DemoMakers et Hardware. Pour cette dernière, Loïc Far préfère aborder en premier les sujets qui ne l'ont jamais (ou très peu) été ailleurs, comme vous avez certainement pu vous en rendre compte : CIAs, drives, clavier... Copper et Blitter suivront, mais en leur temps. De plus, Transactor peut vous permettre de vous exprimer sur un sujet particulier de votre choix ; c’est votre rubrique, à vous de la remplir !
Le Basic (qu'il soit Amiga- ou GFA-) ne fait effectivement pas partie des langages dont on parle régulièrement dans l'ANT. 11 y a plusieurs raisons à cela : d’abord, il faut reconnaître que ce n'est pas une franche réussite (FAmiga-Basic est lent et lourd, le GFA- Basic est bogué jusqu'à la moelle). Ensuite, les passionnés préfèrent l'assembleur et les pros, le C. Reste donc l'AMOS, auquel une rubrique est consacrée, signée (est-il réellement besoin de le rappeler ?) Par l'auteur du langage lui-même, j'ai nommé François Lionet.
Enfin, pour expliquer l’orientation réellement "système" de l’ANT, il faut vous avouer que nous avons toujours considéré que l'Amiga est un ordinateur par trop extraordinaire pour le confiner au rôle de "console de jeu" ou de "machine à démos", aussi étonnantes soient-elles. Nous soutenons donc la programmation propre, multitâche, bref, "Amiga friendly". Ce qui ne nous empêchera pas de développer à la première occasion l’autre côté de la chose.
Je voudrais juste signaler à Loïc Far que son article "Les Disque Chouettes" sur les lecteur de disquettes était peut-être compréhensible par quelqu'un ayant déjà quelques notions sur le sujet, mais pour celui qui espérait pouvoir programmer un trackload avec... Le format MFM est abordé avec beaucoup trop de rapidité, sans parler réellement du procédé de décodage (et il n'est même pas fait mention du codage). Serait-il donc possible que monsieur Far y apporte quelque complément ?
Un lecteur au hasard parmi beaucoup d'autres
Hé, hé, hé... Je n'ai passé cette lettre que pour embêter l’ami Loïc, qui a d'ores et déjà réalisé le complément demandé. Tournez vite la page et voyez par vous-mêmes.
Procédure TOPERAND
If SYNT : Pop Proc : End If CHAR
If CHAR$ = "("
Inc NPAR : TEVALUE[NPAR]
Else
P=Instr(FUN$ , " "+CHAR$ )
If P
Repeat
For N=1 To 10
B$ =Mid$ (FUN$ ,P+N+l,1)
Exit If B$ ="}",2
Exit If B$ oMid$ (EV$ , PEV+N-1,1)
Next
P=Instr(FUN$ ," "+CHAR$ ,P+l)
Until P=0 If P
E=P+N+2 : PILE$ (PPILE)=+Mid$ (FUN$ , E, 4 ) : Inc PPILE PEV=PEV+N-1
NP=Val(Mid$ (FUN$ ,E+4,1))
If NP
OPAR=NPAR For N=1 To NP
TEVALUE[NPAR] : PILE$ (PPILE)="_END" : Inc PPILE If N NP
CHAR:If CHAR$ SYNT=-1 : Pop Proc: End If End If Next
If NPARoOPAR-1 : SYNT=-1 : Pop Proc : End If NPAR=OPAR End If Else
SYNT=-1 : Pop Proc End If Else
If(CHAR$ ="-") or(CHAR$ >="0" and(CHAR$ ="9"))
P=PEV-1 Repeat
Inc P : A$ =Mid$ (EV$ ,P,1)
If Upper$ (A$ )="E"
Inc P : A$ =Mid$ (EV$ P 1)
If A$ ="+" or(A$ ="-")
Inc P : A$ =Mid$ (EV$ P 1)
End If End If
Until(A$ >" ") and(A$ >".") and((A$ "0") or(A$ >"9"))
PILE$ (PPILE)="CONST" : PILE (PPILE)=Val(Mid$ (EV$ ,PEV-1,P-PEV+1))
Inc PPILE : PEV=P
Else
SYNT=-1
End If
End If
End If
End Proc
Procédure CHAR If PEV>LEV
CHAR$ =" " : PEV=10000
Else
Repeat : CHAR$ =Mid$ (EV$ ,PEV,1) : Inc PEV : Until CHAR$ >" "
End If
End Proc
' PROCEDURES DE CALCUL Procédure EVALUE
PPILE=-1 : EVALIT End Proc[PP (PP)]
r
Procédure EVALIT Repeat
Inc PPILE : Gosub PILE$ (PPILE)
Until E Pop Proc
_END: E=1 : Return
_CONST: Inc PP : PP (PP)=PILE (PPILE) : Return
PLUS :
Dec PP :
: PP (PP)=PP (PP)+PP (PP+1)
: Return
MINS :
Dec PP :
: PP (PP)=PP (PP)-PP (PP+1)
: Return
DIVD :
Dec PP :
: PP (PP)=PP (PP) PP (PP+1)
: Return
_TIME:
Dec PP :
: PP (PP)=PP (PP)*PP (PP+1)
: Return
POWR :
Dec PP :
: PP (PP)=PP (PP)APP (PP+1)
: Return
VARS :
EVALIT :
: EVALIT
Dec PP :
: A=PP (PP) : B=PP (PP+1) :
PP (PP)=VAR (A, B)
Return
SINU :
EVALIT :
: PP (PP)=Sin(PP (PP)) :
: Return
_COSI:
EVALIT :
: PP (PP)=Cos(PP (PP)) :
: Return
_TANG:
EVALIT :
: PP (PP)=Tan(PP (PP)) :
: Return
_PIPI:
Inc PP :
: PP (PP)=Pi : Return
_SQRR:
EVALIT :
: PP (PP)=Sqr(PP (PP)) :
: Return
_LOGG:
EVALIT :
: PP (PP)=Log(PP (PP)) :
: Return
LNNN :
EVALIT :
: PP (PP)=Ln(PP (PP)) :
Return
ABSS :
EVALIT :
: PP (PP)=Abs(PP (PP)) :
: Return
EXPP :
EVALIT :
: PP (PP)=Exp(PP (PP)) :
: Return
INTT :
EVALIT :
: PP (PP)=Int(PP (PP)) :
: Return
_RNDD :
EVALIT :
: PP (PP)=Rnd(PP (PP)) :
: Return
End Proc
Par François Lionet
¦b
Par Ok et Cancel
101
Suite à quelques courriers plutôt critiques, j’ai décidé unilatéralement de reporter au mois prochain la fin de mon article sur le clavier, pour revenir aux lecteurs de disquettes.
N
IUTUTUT !
Quelques abonnés ont en effet été déçus de ne pas trouver dans cet article la routine promise de délai hard (à grands coups de CIA) permettant de temporiser le déplacement de la tête de lecture de piste à piste. D'autres, plus mesquins, auraient souhaité trouver plus d’informations d’une part sur le codage MFM, d’autre part sur l’utilisation du Blitter pour le décodage des données.
A tous ceux-là, je tiens d'abord à adresser mes excuses les plus plates, à défaut des plus sincères, et à rappeler ce que j’avais alors dit dans : décoder le format MFM au Blitter relève à mon avis de l’hérésie. Celà revient à se limiter volontairement à charger des données en Chip-Ram, alors qu’en utilisant le 68000, on peut utiliser un buffer de piste se trouvant n'importe où en mémoire. De plus, du fait du délai nécessaire au moteur pas à pas pour déplacer la tête de lecture écriture, le temps gagné serait tout de suite perdu à attendre que celle-ci soit effectivement positionnée sur la piste suivante. Sans oublier que le Blitter peut toujours être utilisé pour autre chose pendant ce temps-là, par exemple pour une animation graphique pendant le chargement. Enfin, il faut bien reconnaître ce qui est, le décodage MFM est beaucoup plus facile à réaliser au 68000 !
Celà dit, voici un tout petit bout de programme assembleur qui initialise le timer A du CIA-B et compte exactement tous les 3 millisecondes, soit le temps mimimum recommandé par les ingénieurs de chez Commodore-Amiga. Cet exemple ne fonctionne pas tel quel, et il vous faudra l'insérer correctement dans le programme du numéro 25.
; Interdiction de l'interruption CIA-B
move.w $ 2000,$ dff09a ; INTENA, bit 13
; Initialisation du timer A, mode one-shot, 3 ms
move.l $ bfe001,a0 move.b $ e00(a0),d0 andi.b $ c0,d0 ;
; Adresse du CIA-B ; Lit le registre CRA Masque les bits inutilisés ; Mode one-shot ; Ecrit le registre
; 3ms = 2128 en PAL ; (2148 en NTSC)
; Test le registre ICR
ori.b $ 8,d0 move.b d0,$ e00(a0)
; Programme le délai : 3 ms move.b $ 28,$ 400(aO) move.b $ 21,$ 500(aO)
; Attend l'expiration du délai wait btst.b 0,$ d00(a0) beq.s wait ; On pourrait ici faire autre chose !
; Reprogramme le timer et boucle
bset.b 0,$ e00(a0) ; Le timer redémarre
bra.s wait
Voilà, c’était vraiment pas grand chose, et je m’en veux de ne pas l’avoir tout de suite fait dans le programme concerné.
MFM
Vous les aviez demandées, voici quelques informations complémentaires sur le format MFM et sur la manière dont l’Amiga enregistre ses pistes et secteurs sur la disquette.
D’abord, le format MFM lui-même. Rappelons que ce format est destiné à éviter toute erreur de synchronisation lors de la lecture et de l’écriture des données, en insérant un bit de syncho entre chaque bit de donnée. Qui plus est, on code d’abord les bits impairs puis ensuite les bits pairs, dans deux buffers consécutifs en mémoire.
Donc, pour décoder un bloc de données, il suffit d'utiliser la routine suivante :
?
DO = nombre de mots longs à décoder - 1 aO pointe les bits impairs al pointe les bits pairs a2 pointe le buffer de décodage
Décodé move.1
(a0)+,dl
move.1
(al)+,d2
andi.1
$ 55555555,dl
andi.1
$ 55555555,d2
lsl.l
1, dl
or. 1
d2,dl
move.1
dl,(a2)+
dbra
dO,Décodé
rts
Le codage est plus beaucoup plus délicat et ne sera pas abordé maintenant. Si vraiment vous désirez savoir comment procéder, écrivez-moi des tonnes de lettres, j’adore me faire injurier.
J’avoue humblement ne m’être jamais attaqué à ce côté du
problème mais qui sait, avec un peu de bonne volonté, on arrive à tout.
Un mot de synchronisation particulier est placé devant chaque secteur, qui permet au DMA d'en reconnaître exactement le début. Ce mot est une valeur MFM bidon, c’est-à-dire qui ne correspond à aucun codage possible (de cette manière, on est sûr qu’il s’agit bien d’une marque de syncho et non d'une donnée du secteur).
AmigaDOS utilise le mot $ 4489, mais n'importe quel autre mot
"bidon" ferait tout aussi bien l’affaire.
LES PISTES
Une piste contient 11 secteurs constitués d'une zone d’informations diverses de 32 octets suivis d’une zone de données de 512 octets. La piste elle-même est précédée d'un GAP d'environ 700 octets. Je dis environ parce que sa taille peut varier suivant la position de la piste (près du centre ou au contraire de l'extérieur) sur la disquette. Le GAP lui-même ne sert strictement à rien pour le DOS, mais permet d’éviter, à l’écriture de la piste, que les dernières données écrites n’écrasent les premières (n’oubliez pas qu'une piste est circulaire !). Si on additionne tout ceci, on obtient une longueur de 700 + (2 * (11 * (32 + 512))) = 12668 octets MFM par piste.
Voici résumé dans un tableau le format exact d'un secteur codé.
Offset
Valeur
Signification
0
$ aaaa
Premier mot du secteur
2
$ aaaa
Second mot du secteur
4
$ 4489
Premier mot de synchro
6
$ 4489
Second mot de synchro
8
xxxxx
ID secteur (bits impairs)
12
xxxxx
ID secteur (bits pairs)
16
xxxxx
Données AmigaDOS (bits impairs)
32
xxxxx
Données AmigaDOS (bits pairs)
48
xxxxx
Checksum de l'entête (bits
impairs)
52
xxxxx
Checksum de l'entête (bits
pairs)
56
xxxxx
Checksum des données (bits
impairs)
60
xxxxx
Checksum des données (bits pairs)
64
xxxxx
Données (bits impairs)
576
xxxxx
Données (bits pairs)
Comme on le voit, si l’on veut décoder tout ça, il faut procéder en quatre étapes : d’abord l’ID secteur, ensuite le checksum de l'entête, puis le checksum des données, et enfin les données elles- mêmes. La routine MFMUncode du programme de l’ANT 25 gérait tout celà et vérifiait en même temps la validité des deux checksums.
Voilà, je pense que le point a été fait et que le goût de trop peu du précédent article aura bel et bien disparu. Sur ce, je vous donne rendez-vous au mois prochain pour la suite de l’étude du clavier. Asta la vista !
Par Loïc Far
1
* ***********************************
monospace;">$ 4489 Premier mot de synchro
6
$ 4489
Second mot de synchro
8
xxxxx
ID secteur (bits impairs)
12
xxxxx
ID secteur (bits pairs)
16
xxxxx
Données AmigaDOS (bits impairs)
32
xxxxx
Données AmigaDOS (bits pairs)
48
xxxxx
Checksum de l'entête (bits
impairs)
52
xxxxx
Checksum de l'entête (bits
pairs)
56
xxxxx
Checksum des données (bits
impairs)
60
xxxxx
Checksum des données (bits pairs)
64
xxxxx
Données (bits impairs)
576
xxxxx
Données (bits pairs)
Comme on le voit, si l’on veut décoder tout ça, il faut procéder en quatre étapes : d’abord l’ID secteur, ensuite le checksum de l'entête, puis le checksum des données, et enfin les données elles- mêmes. La routine MFMUncode du programme de l’ANT 25 gérait tout celà et vérifiait en même temps la validité des deux checksums.
Voilà, je pense que le point a été fait et que le goût de trop peu du précédent article aura bel et bien disparu. Sur ce, je vous donne rendez-vous au mois prochain pour la suite de l’étude du clavier. Asta la vista !
Par Loïc Far
1
* ***********************************

Click image to download PDF

AMIGA NEWS TECH numero 27 (11-1991)

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


Thanks for you help to extend Amigaland.com !
frdanlenfideelhuitjanoplptroruessvtr

Connexion

Pub+

55.2% 
9.5% 
3.9% 
3.3% 
3.1% 
2.6% 
2.2% 
1.3% 
1% 
0.9% 

Today: 126
Yesterday: 88
This Week: 731
Last Week: 645
This Month: 1925
Last Month: 2867
Total: 77313

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