Sponsors

FacebookTwitterGoogle Bookmarks

Qui ne connait pas aujourd'hui la fameuse arp.library et son non moins célèbre sélecteur de fichiers ? En tous cas cette bibliothèque de fonctions a été utilisée par de nombreux développeurs et dans des langages tels que le C, l’assembleur, le Modula II, l’Amiga BASIC, Arexx et aussi le HiSoft BASIC. A cette liste manquait le GFA-Basic. Cela est certainement dû à la relative nouveauté de ce langage qui a beaucoup de peine à s’imposer sur Amiga. Cependant ce langage possède toutes les commandes nécessaires à l’interfaçage de la bibliothèque ARP. L’utilisation de l’ARP en GFA-Basic apporte-t-elle grand chose à ce langage. Oui, si l’on considère que celle-ci offre de nombreuses fonctions dont la reconnaissance des caractères génériques, l’évaluation d’une ligne de commande et bien sûr son fameux sélecteur de fichiers. Mais, me direz-vous le GFA-Basic possède son propre sélecteur de fichiers et il est inutile d’appeler celui de l’ARP. Je répondrai que le sélecteur de l’ARP est avantageux à plus d’un titre. Sa première qualité vient de sa renommée et de sa présence dans de nombreux programmes, qui font que ce sélecteur est connu de beaucoup d’utilisateurs d’Amiga. En second lieu, celui-ci est âgé de plusieurs années : il descend du vieux sélecteur de Charlie Fheath visible par exemple dans le programme PerfectSound, qui l’a constamment amélioré depuis. De cette ancienneté découlent naturellement efficacité, rapidité et facilité d’emploi. On peut affirmer sans crainte qu’il s’agit du sélecteur de fichiers le plus efficace jamais créé sur Amiga (celui d’AZ est un concurrent valable à qui j’attribuerai la palme de l’esthétisme). Efficace car le programmeur peut y ajouter ses propres gadgets et redéfinir la fenêtre d’affichage. Mon but n’est pas de critiquer le sélecteur du GFA-Basic, qui j’en suis sûr se bonifiera avec le temps. Le premier sélecteur de Charlie Fheath était loin d’être convivial, mais pour l’époque il était exceptionnel car la plupart des logiciels n’offraient pas encore de liste de fichiers (chargez un fichier avec NotePad, vous comprendrez). Non, le seul défaut grave de ce sélecteur est un bug faisant que 48 octets ne sont pas rendus au système à la fin de son appel. Ceci a entraîné de nombreuses erreurs "aléatoires” dans plusieurs programmes particulièrement gros. C'est une raison suffisante pour utiliser la arp.library. La version utilisée ici de l'ARP est la 1.3, numéro 39.1. Il est absolument garanti que le programme ci-dessous ne fonctionnera pas avec une version inférieure. Et j’ose espérer qu’il fonctionnera avec les versions postérieures. Le fichier arp.library doit être contenu dans le répertoire LIBS; et son numéro de version peut être obtenu avec la commande du CLI : Version arp.library Au cas où vous n’auriez pas ce fichier sous la main, je vous conseillerai premièrement de faire le tour de vos disquettes, un grand nombre de logiciels utilisant la arp.library (qui est domaine public, je le rappelle), il existe une chance non négligeable pour que vous retrouviez cè fichier dans le répertoire LIBS: de l’une d’entre elles. Sinon, les moyens possibles de se la procurer sont les disquettes Fish, ou d’autres collections du domaine public, et aussi la plupart des serveurs proposant du téléchargement sur Amiga.

Click image to download PDF

AMIGA NEWS TECH numero 13 (06-1990)

Document sans nom N°13 JUIN 90
Le blitter (suite)
Frédéric Mazué
Création et animation sprite sous IRQ.
LANGAGE
?... Dominique Geno
Le 3D se complique un peu..
Pascal Amiable
(Croquis accompagnant le 3D C)
AMIGA DOS
éïsl
Plan de
«If
projection
(Comment utiliser le disque RAD ?) Dominique Lorre
Direction
de projection
Plan de
Fig I.a Projection parallèle.
|Gfa ba5c| ¦1 1
WÊÊ nU ihiiMt K i wi
Fig III axonowé trique
(ARP et GFA) Dominique Lorre
Centre de
projection.
ASSEMBLEUR 68000
Figl.b Projection perspective.
Jouer une musique SoundTracker en GFA.
Max
ILS ONT CHOISI MAD...
ET VOUS?? ©48.78.11.65
MAD
42, RUE LAMARTINE
75009 PARIS
LE 3D SE COMPLIQUE UN PEU OU..
PROJETONS DANS LA JOIE ET LA BONNE HUMEUR ( ie 11
Chers lecteurs et lectrices, je vous souhaite le bonjour. Dans ce troisième volet des aventures au cœur même de la troisième dimension, nous allons étudier les différents moyens permettant de transformer un objet tridimensionnel en une image bidimensionnelle pour l’affichage.
Ce passage de l’espace au plan s’effectue à l'aide d'une transformation appelée PROJECTION. Le mois dernier, je vous ai proposé un petit programme contenant une fonction transformeQ contenant des équations de projection. Cette projection, comme bien d’autres, vous sera expliquée en détail dans cet article.
PRINCIPE
La projection consiste donc à simuler, en deux dimensions des objets en trois dimensions tels qu’on les perçoit avec nos yeux.
Toute projection de l’espace sur le plan peut être représentée par une matrice homogène 4x4 (ne vous affolez pas je vous explique tout de suite). Un point P (3D) est projeté en un point P’ (2D) en multipliant ses coordonnées homogènes [X,Y,Z,W] par la matrice de projection Mp.
P = Mp.P soit
X’
MxxMxyMxzMxw
X
Y’
Myx Myy Myz Myw
Y
I
Mzx Mzy Mzz Mzw
Z
W
Mwx Mwy Mwz Mww
W
Cette matrice de projection est un outil permettant de transformer un vecteur en un autre. Cette représentation simple équivaut au système développé ci-dessous.
X' = Mxx.X + Mxy.Y + Mxz.Z + Mxw.W
Y' = Myx.X + Myy.Y + Myz.Z + Myw.W
Z' = Mzx.X + Mzy.Y + Mzz.Z + Mzw.W
W' = Mwx.X + Mwy.Y + Mwz.Z + Mww.W
C’est déjà plus simple comme cela non?
Vous aurez sans doute remarqué que le vecteur résultant de la multiplication est un vecteur 3D alors qu’une projection est une transformation 3D -> 2D. C’est certes vrai, mais la matrice de projection est telle que le terme Z’ vaut zéro à la fin du calcul, ce qui signifie que le point résultant P’ est ramené dans un plan homogène avec pour coordonnées (X’ Y’ W’), Z’ valant zéro.
CLASSIFICATION DES PROJECTIONS
Une projection est définie par une surface de projection et un centre de projection. La surface de projection est généralement choisie plane (ce n’est nullement une obligation et certains domaines techniques nécessitent l’utilisation de projections non planes). Chaque point de l’objet étant projeté suivant des rayons rectilignes issus d’un centre de projection, on appelle ce type de projection “projection géométrique plane”. Elle représente l’avantage qu’un segment de droite se transforme en un segment de droite, ce qui ne nécessite donc que le calcul des projections de ses extrémités.
On peut décomposer les projections géométriques planes en deux catégories,
- Les projections parallèles.
- Les projections perspectives.
Les projections parallèles ( Fig La) sont caractérisées par un centre de projection ramené à l’infini. Ce qui signifie que les rayons de projections sont parallèles à une direction de projection. De cette propriété découle une conservation de certaines distances (ces projections sont souvent utilisées dans le dessin technique pour cette raison).
Pour les projections perspectives (Fig l.b), le centre de projection est placé à une distance finie du plan de projection. Il s'agit de la position de l’œil de l’observateur.
Cette projection restitue un effet de profondeur, augmentant l'effet de réalisme. On utilise souvent la perspective dans la création d’images réalistes.
La figure ci-après (Fig II) représente la classification des projections planes que nous allons étudier. Comme vous pouvez le remarquer il en existe un certain nombre.
Bien maintenant que nous avons bien regarder ce schéma, nous allons étudier chaque projection en détail.
LES PROJECTIONS PARALLELES
1. Projections axonométriques.
Les projections axonométriques représentent le premier sous-groupe des projections parallèles. On définit un plan quelconque de l’espace comme plan de projection et on projette chaque point sur ce plan suivant la normale au plan (Fig III).
Il existe trois types de projections axonométriques particulières, les projections orthographiques, les projections dimétriques et la projection isométrique. Nous allons commencer par étudier les projections orthographiques qui vont nous servir à décrire à la fois la projection axono- métrique générale et les deux autres types particuliers,
2. Les projections orthographiques.
Les projections orthographiques représentent les projections axonométriques les plus simples. Il s’agit simplement de supprimer une des coordonnées pour permettre le passage de la 3D à la 2D. On obtient respectivement les vues de face (suppression de X) de côté (suppression de Y) et de dessus (suppression de Z).
Sous forme matriciel on représente
la vue de face par une matrice
0100
X' = Y
Mf =
0010
soit Y' = Z
0000
Z’ = 0
0001
W1 = W
la vue de côté par une matrice
1000
X’ = X
Me =
0010
soit Y’ = Z
0000
Z’ = 0
0001
W’ = W
la vue de dessus par une matrice
1000
X’
= X
Md =
0100
soit Y’
= Y
0000
Z’
= 0
0001
W
= W
3. Les projections axonométriques générales.
Les projections axonométriques générales permettent de choisir librement l'angle d’observation d’une scène en définissant une direction de projection quelconque. Cette projection consiste à exprimer les coordonnées des objets de la scène dans un repère associé au plan de projection et ensuite à effectuer une projection orthographique en vue de face pour éliminer la coordonnée X et obtenir ainsi une image 2D.
La direction de vision est donnée par deux points :
Le point 0’ représentant la position de l'observateur.
Un point P quelconque visé par l’observateur et appartenant au plan de projection.
La matrice de représentation de l’axonométrie générale est le produit de 5 matrices :
- Une translation T de vecteur [-Tx,-Ty,-Tz,1] dont les composantes en
[X,Y,Z,W] représentent le vecteur 00’(Tx,Ty,Tz,1] entre l’Origine de ia scène et la Position de l’observateur.
M
- Une rotation d’axe vertical Z : Rz(-phi).
- Une rotation d’axe Y : Ry(-teta).
- Une rotation d’axe X : Rx(-alpha).
- Une projection Orthographique selon X : Mf.
Maxo = Mf.Rx.Ry.Rz.T
soit après un calcul long et fastidieux, Maxo vaut :
A B C -Ty
Maxo D E F -Tz 0 0 0
0 0 1
avec :
A = -cos(alpha).sin(phi)+ sin(alpha).sin(teta).cos(phi)
B = cos(alpha).cos(phi)+sin(alpha).sin(teta).sin(phi)
C = sin(alpha).cos(teta)
D = sin(alpha).sin(phi)+cos(alpha).sin(teta).cos(phi)
E = -sin(alpha).cos(phi)+cos(alpha).sin(teta).sin(phi)
F = cos(alpha).cos(teta)
Soit en développé :
X’ = A.X + B.Y + C.Z -Ty.W Y’ = D.X + E.Y + F.Z -Tz.W W’ = W
et bien sûr on a Z’ = 0 ce qui est tout à fait normal car on élimine une dimension.
4. Les projections dimétriques.
Les projections dimétriques sont caractérisées par le fait que deux des trois vecteurs unités du repère 3D seront de même longueur après projection. Il s'agit d’un cas particulier de la projection axonométrique générale.
La matrice de projection dimétrique Mdim est la même que la matrice de projection axonométrique générale, mais ii existe une contrainte sur les angles teta et phi.
En effet pour que deux des trois vecteurs unités soient de même longueur après projection il faut introduire une contrainte sur phi, une fois que l’on connaît teta.
La contrainte est la suivante:
soit A = SQR((sinA2(teta)) (1 -sinA2(teta))).
On a phi = ARCSIN(A). Où ARCSINO est la fonction inverse de sinus (elle doit se trouver dans toute bonne bibliothèque de fonctions qui se respecte). NDLR : Mathrans.library dans ie répertoire lips d'une disquette Workbench.
_
PROJECTIONS GEOMETRIQUES PLANES
5. La projection isométrique.
Autre cas particulier de projection axonométrique, la projection isométrique. La projection isométrique est telle que la longueur des trois vecteurs unitaires est conservée par le passage 3D en 2D.
La matrice de projection isométrique Miso est égale à la multiplication de la matrice de projection orthographique en vue de face (Mf) et de trois rotations particulières R(x,0) angle = 0 degré, R(y,35.624) angle =
35. 624 degrés et R(z,45) angle = 45degrés.
Miso = Mf.R(x,0).R(y,35.624).R(z,45)
d’ou Miso vaut après calcul:
- SQR(2) 2 SQR(2) 2 0 -Ty
Miso = -(SQR(3).SQR(2)) 6 -(SQR(3).SQR(2)) 6 (SQR(3).SQR(2)) 3-Tz 0 0 0 0
0 0 0 1
Où la fonction SQR(x) signifie racine carrée de x.
6. Les projections obliques.
Dans une projection oblique, les plans parallèles au pian de projection X=0 sont projetés en vraie grandeur (Fig IV).
Les paramètres sont un angle alpha représentatif de la projection du troisième axe et un facteur de réduction pour cet axe.
La matrice de cette projection vaut alors.
I. cos(alpha) 1 0
l. sin(alpha) 0 1
Mobli =
On notera deux projections obliques particulières:
La projection cavalière : alpha = 45 degrés, I = SQR(2).
La projection cabinet : alpha = 45 degrés, I - SQR(2) 2.
La projection cavalière reporte en vraie grandeur les segments parallèles à X alors que la projection cabinet les reporte en demi-longueur.
Bien, nous avons maintenant fait le tour des projections parallèles.
On discute, on discute et avec tout cela il ne reste plus de place pour ce mois. Donc il vous faudra attendre le mois prochain pour voir apparaître les projections perspectives. Et nous terminerons en apothéose cette étude des projections par la caméra virtuelle qui vous permettra de représenter à peu près tous les effets du 3D.
PETIT PRELIMINAIRE
BLITTER 4e PARTIE
Avant de rentrer dans le vif du sujet, il est peut-être nécessaire de rappeler le détail des bits du registre BLTCON1 pour ceux qui auraient manqué les épisodes précédents.
BLTCON1
Offset $ 42 registre 16 bits
bit N° Nom
Fonction
15 BSH3 14 BSH2 13 BSH1 12 BSH0
ces quatre bits ont été expliqués dans le numéro précédent à propos des scrollings horizontaux
11-5
ces sept bits ne sont pas utilisés
4 EFE 3 IFE 2 FCI 1 DESC 0 LINE
Exclusive Fill Enable Inclusive Fill Enable Fill Carry In mode descending mode tracé de ligne
LE VIF DU SUJET
Nous avons vu précédemment que le blitter travaille avec des fenêtres “rectangulaires”. Sachant cela, il sera possible de remplir n’importe quelle surface, à condition que celle-ci soit inscrite dans la fenêtre qui sera définie dans le registre BLTSIZE.
Comment le blitter procède-t-il pour remplir une surface ?
Comme d’habitude, il travaille ligne par ligne, mais cette fois il commencera par la ligne la plus en bas de la fenêtre et traitera cette ligne de
droite à gauche, et ainsi de suite jusqu’à la ligne la plus haute.
Du point de vue de la programmation, cela signifie que, dans ce cas, le blitter travaille par décrémentation d'adresses. Cela implique que pour un remplissage de surface, il est OBLIGATOIRE d'activer le bit DESC. De même les registres BLTxPTH devront pointer sur les derniers mots des zones source et cible.
Voyons maintenant comment le blitter rempli une ligne :
- Hypothèse de départ, le bit FCI n’est pas mis.
- Soit l’exemple de ligne à remplir :
1 1 1 1 .
où un point correspond à un point écran éteint et un 1 correspond à un point écran allumé et ceci dans une fenêtre de deux mots de large.
Le blitter examine le point le plus à droite: ce point est éteint, le blitter passe au point immédiatement à gauche qui lui aussi est éteint et ainsi de suite jusqu’à ce qu’il rencontre le premier point allumé. A ce moment, le blitter inverse le bit FCI. Dans ce cas précis ie blitter met donc le bit FCI à 1. Le blitter continue son examen sur la gauche et à ce moment de deux choses l’une :
- le point examiné est éteint : dans ce cas le blitter écrit le bit FCI à l’emplacement correspondant au point. Le point s’allume.
- le point examiné est allumé: dans ce cas, comme précédémment, le blitter inverse le bit FCI, puis continue son examen à gauche.
Après travail du blitter la ligne de l’exemple devient donc :
1111111 1111111 .
Maintenant que vous avez tout compris il est possible de résumer très simplement :
Aujourd'hui nous nous intéressons aux remplissages de surfaces par le blitter. Pas de difficultés particulières, c’est même très simple pour une fois.
Le blitter examine chaque point de droite à gauche. Chaque point éteint est “rempli” par le bit FCI. A chaque point allumé rencontré le bit FCI est inversé.
L’hypothèse de départ était FCI = 0. Mais il est parfaitement possible d'initialiser ce bit à 1 dès le départ.
Observons ce qui se passe à partir du même exemple :
le premier point est éteint, donc le blitter y place le bit FCI. Celui-ci étant initialisé à 1 le point s’allume, et ainsi de suite jusqu’à ce que le blitter rencontre un point allumé. A ce moment le bit FCI sera inversé (et donc dans ce cas annulé) et ainsi de suite.
Le résultat sera donc :
111111 111111 111111111
Autrement dit nous avons colorié l'image en “négatif”.
ATTENTION AU PIEGE !
Tout ceci c’est bien joli, mais il y a quand même un traquenard.
Pour que tout se passe bien, il faut que chaque ligne de l'image à colorier contienne UN NOMBRE PAIR de points allumés.
Sinon 11 1 par exemple
ne sera pas rempli comme ceci 11111111
mais comme cela 1111111111111111
En clair (si j’ose dire) cela s’appelle une grosse tache !
Nous en avons presque fini. Il nous reste à savoir qu'il existe en fait deux modes de remplissage.
- Le mode IFE ce qui signifie Inclusive Fill Enable ou mode de remplissage inclusif. Ce mode vous le connaissez déjà car les exemples précédents sous-entendaient qu’il était utilisé. Vous savez tout, n’en parlons plus.
- Le mode EFE ce qui signifie Exclusive Fill Enable ou mode de remplissage exclusif. Que se passe-t-il avec ce mode ? Très simple : nous avons vu que lorsque le blitter rencontre un point allumé, il inverse le bit FCI. Si cette inversion met le bit à 1 rien ne change. Par contre si l'inversion met le bit à 0, alors le point responsable de l’inversion sera éteint. Pour être plus clair, le premier exemple sera rempli en mode EFE
comme suit :......111111......111111 .soit deux points de moins
qu’en mode IFE.
ET MAINTENANT LE PROGRAMME
Remarquez qu'au label data les données de notre image (cf. Articles précédents) ont été modifiées. Presque tous les bits sont nuls et il ne subsiste plus qu’un “squelette” qui sera notre motif à remplir,
Comme d’habitude le programme ouvre les librairies nécéssaires puis un écran intuition à un bitplane.
Premièrement, le programme copie on ne peut plus simplement le squelette dans le bitplane de l'écran puis il en effectue le remplissage,
Deuxièmement on ruse : le squelette est installé en mode descending et EN MEME TEMPS on active un mode de remplissage dans BLTCON1 (dans ce cas le mode IFE. Oh miracle, notre motif est rempli en même temps qu’il est installé et le blitter a travaillé AUSSI VITE que pour une copie de données simple. Décidément ce blitter est génial !
Troisièmement on reprend la ruse du deuxièmement mais avec le mode EFE et on constate comme prévu que le blitter a supprimé une colonne de points à gauche du motif et une colonne de points à droite dans le trou du motif.
* ** exec.library ***
OidOpenLibrary = -408 CloseLibrary =-414
Permit =-138
Forbid = -132
AllocMem = -198
FreeMem =-210
* ** graphics.iibrary ***
OwnBlitter = -456
DisownBlitter = -462
* ** intuition, library ***
CloseScreen = -66
OpenScreen =-198
c>
C>[> début
move.1 $ 04,a6
lea grai_name,a1 jsr 01d0penLibrary(a6) move.l d0,graf_base
lea int_name,a1 jsr 01dOpenLibrary(a6) move.l d0,int_base
move.l (fin_data-data),d0 move.l $ 10002,d1 jsr AllocMem(a6) move.l d0, image
move.l (fin data-data) 2,d0
CHIP_RAM
subq $ 01 ,d0
move.l image,a0
lea data,a1
loop
move.w (a1 )+,(a0)+ dbra d0,loop
move.l int_base,a6 lea écran,a0 jsr OpenScreen(a6) move.l d0,canaLecran
move.l graf_base,a6 jsr OwnBlitter(a6)
lea $ dff000,a0 bsr Blitter_Busy
Mise en place du “squelette”
move.l image, BLTAPTH(a0) move.l canal_ecran,a1 move.l $ c0(a1 ),a1
BLTSEE
= $ 58
BLTCPTH
= $ 48
BLTBPTH
= $ 4C
BLTAPTH
= $ 50
BLTDPTH
= $ 54
BLTCMOD
= $ 60
BLIBMOD
= $ 62
BLTAMOD
= $ 64
BLTDMOD
= $ 66
BLTCON0
= $ 40
BLTC0N1
= $ 42
BLTAFWM
= $ 44
BLTALWM
= $ 46
DMACON
= $ 96
DMACONR
= $ 02 C>C>
; graphics.iibrary ouverte
Quatrièmement, toujours avec ruse, en mode IFE, mais au départ le bit FCI est initialisé à 1 : on a un remplissage en négatif.
Enfin, après un click de la souris, le programme ferme écran et librairies et rend la main.
Tout est dit à propos des remplissages de surfaces et je vous donne rendez-vous le mois prochain pour un gros morceau, le tracé de droites.
Frédéric MAZUE
Remplissage de surface avec le BLUTER
[> *** CHIPS *'
;intuition.library ouverte ; réservation de CHIPJRAM
; transfert des données en
; écran à un bitplane ouvert
; blitter réservé ; blitter prêt ?
Adda.l (40*20),a 1 adda.l 10,a1
; nombre de ligne par ; nombre d'octet par ligne ; centre de l'écran approximatif
move.l a1 ,BLTDPTH(a0)
move.w %0000100111110000,BLTCON0(a0)
move.w %0000000000000000,BLTCON1 (a0)
move.w %1000000001000000,DMACON(a0)
move.w $ 00,BLTAMOD(a0)
move.w (20-9)*2,BLTDMOD(a0)
move.w $ ffff,BLTAFWM(a0)
move.w $ ffff,BLTAL,WM(a0)
move.w (20*64+9), BLTSEE(a0)
bsr Blitter_Busy
bsr tempo
Remplissage du “squelette”
move.l canal_ecran,a1 move.l $ c0(a1 ),a1
adda.l 10, a1
adda.l (40*20),a1
adda.l ((19*40)+16),a1 ; pointer sur le dernier
; mot du squelette
move.l a1 ,BLTDPTH(a0) move.l a1 ,BLTAPTH(a0) move.w %0000100111110000,BLTCON0(a0) move.w %0000000000001010,BLTCON1 (a0) move.w %1000000001000000,DMACON(a0) move.w (20-9)*2,BLTAMOD(a0) move.w (20-9)*2,BLTDMOD(a0) move.w $ ffff,BLTAFWM(a0) move.w $ ffff,BLTALWM(a0)
move.w (20*64+9), BLTSEE(a0)
bsr Blitter_Busy
bsr tempo
Mise en place du “squelette” et remplissage en même temps
move.l image,d0 add.l ((20*9‘2)-2),d0
move.l d0,BLTAPTH(a0)
; il faut pointer sur le ; dernier mot de donnée ; mode DESC oblige
move.l canal_ecran,a1 move.l $ c0(a1 ),a1
adda.l (140*20),a1 adda.l 10, a1
adda.l ((19*40)+16), a1
; il faut également pointer ; sur le dernier mot de la ; zone cible
move.l a1 ,BLTDPTH(a0)
move.w %0000100111110000,BLTCON0(a0)
move.w %0000000000001010,BLTCON1 (a0)
move.w %1000000001000000,DMACON(a0)
move.w $ 00,BLTAMOD(a0)
move.w (20-9)*2,BLTDMOD(ct0)
move.w $ ffff,BLTAFWM(a0)
move.w $ ffff,BLTALWM(a0)
move.w (20*64+9), BLTSEE(a0)
bsr Blitter_Busy
bsr tempo
Mise en place et remplissage en mode exclusif
move.1 image,d0 add.l ((20*9*2)-2),d0
; pointer sur le dernier mot ; de la source
move.l canai_ecran,a1 move.l Sc0(a1),a1 adda.l (190*20), a1 adda.1 10,a1
adda.1 ((19*40)+16),a1 ; pointer sur le dernier
; mot de la cible
move.l a1 ,BLTDPTH(a0)
move.w %0000100111110000,BLTCON0(a0)
move.w %0000002M0 10010,BLTCON1 (a0) ; mode exclusif
move.w %1000000001000000,DMACON(a0)
move.w $ 00,BLTAMOD(a0)
move.w (20-9)*2,BLTDMOD(a0)
move.w $ ffi,BLTÀFWM(a0)
move.w $ ffff,BLTALWM(a0)
move.w (20*64+9), BLTSIZE(a0)
bsrBKtterJBusy
bsr tempo
Mise en place et remplissage en “négatif’
move.l imaae,d0 addl ((20*9*21-2), d0
; pointer sur le dernier ; mot de la source
move.1 d0,BLTAPIH(a0)
move.l canal_ecxan,a1 move.l Sc0(a1 ),a1 adda.1 10,a1 adda.1 (290*20), a1
adda.1 ((19*40)+16),a1
; pointer sur le dernier ; mot de la cible
move.l a1 ,BLTDPTH(a0) move.w %0000100111110000,BLTCON0(a0) move.w %0000000000001110,BLTCON1 (a® move.w %1000000001000000,DMACON(ct0) move.w S00,BLTAMOD(a0) move.w (20-9)*2,BLTDMOD(a0) move.w $ ffî;BLTAFWM(a0) move.w $ ffff,BLTALWM(a0)
; fillcanymis
move.w (20*64+9),BLTSIZE(a0)
bsrBlitterJBusÿ jsr DisownBlitter(a6)
souris
; "squelette" à remplir
lea Sbfe001 ,a5 btst 6,(a5) bne souris
move.l int_base,a6 move.l canal_ecran,a0 jsr CloseScreen(a6)
stop
move.l
move.l image,a1
move.1 (fin_data-data),d0
;srFreeMem(a6)
move.1 int_base,a1 jsr CloseLibrary(a6)
move.1 graf_base,a1 jsr CloseLibrary(a6)
rts
Blitter_Busy
0,$ 0000,$ 0000,$ 0000,$ 0000 0,$ 0000,$ 0000,$ 0000,$ 02
btst 14,DMACONR(a0) bne 31itter_Busy rts
; s J
- Æ b ‘
iEigraËf -
; petite temporisation
tempo
movem.l d0-d1 ,-(sp) moveq $ 05,d1 move.w Sffi,d0
bouclejempo dbra d0,bouclejempo dbra d1 bouclejempo
movem.1 (sp)+,d0-d1 rts
b-
imaae
r:
dc. 10
canal écran
dc. 10
int_base
dc. 10
graf base
dc. 10
int.name
dc. b 'intuition.library',0 even
graf_name
dc. b 'graphics.lbrary',1
even
écran
dc. w 0
dc. w 0
dc. w 320
dc. w 200
dc. w 1
dc. b
dc. b
dc. w
dc. w
1
2
15
dc. l 0
de.! Titre_ecran
dc. 10
dc. 10
titre.ecran
dc. b blitter demo',0 even
data
dc. w S802
dc. w $ 802
dc. w $ 802
dc. w $ 802
dc. w $ 802
dc. w $ 802
dc. w $ 802
dc. w $ 802
dc. w $ 802
dc. w $ 802
dc. w $ 802
dc. w $ 82K
dc. w $ 802
dc. w $ 80(
dc. w $ 82X
dc. w $ 802
dc. w $ 802
dc. w $ 80!
Dc.w $ 80!
Dc.w $ 80!
Fin data
CREATION et ANIMATION DE SPRITES sous IRQ
En fait, E et H fixent la position du coin supérieur gauche dusprite.
Le bit de contrôle d'attache permet, s’il est à 1, de combiner 2 sprites de 4 couleurs pour en faire 1 de 16 couleurs : 15 couleurs plus celle du fond. Pour simplifier, supposons que les sprites sont indépendants donc que le bit At est à 0.
Exemple de calcul des mots de contrôle, pour un sprite de 8lignes que l'on veut dessiner aux coordonnées x = 180 et y = 160
H = 180 = $ B4 =
E = 160 = i
L = 168 =
Soit MOT 1 = % 10100000 01011010 = $ A05A
MOT 2 = % 10101000 00000000 = $ A800
La structure commencera par : dc.w
il est possible d’utiliser cette petite routine qui fait le travail pour vous :
place m_sprite: ; Conditions Entrée : A1 = adresse de la
structure du sprite D0 = position horizontale ( x = 180 )
D1 = position verticale ( y = 160 )
D2 = nombre de lignes du sprite ( 8 )
Conditions Sortie : mots de contrôle calculés et placés dans la structure du sprite
Isr.w 1 ,d0 bcs s1 bclr 0,3(a1) bra suil s1 :
bset 0,3(a1) suil:
move.b d0,1(a1) move.b d1,(a1) btst 8,d1 bne s2 bclr 2,3(a1) bra sui2 s2:
bset 2,3(a1) sui2:
add.l d2,d1 move.b d1,2(a1) btst 8,d1 bne s3 bclr 1,3(a1) bra sui3 s3:
bset 1,3(a1)
sui3:
rts
7 6 5 4 3 2 1 0 H8 H7 H6 H5 H4 H3 H2 H1
6 5 4
3 2 1 E8 L8
Pour appeler cette routine dans l’exemple précédent :
lea structure_sprite,a1 move.l 180,d0 move.l 160,d1 move.l 8,d2 jsr placem_sprite
D’après ce qui précède, il faut :
1) définir le dessin, c’est-à-dire préciser les pixels qui seront visibles, avec quelle couleur et à quel endroit ils seront affichés. Ceci fait partie de la “structure” du sprite.
2) prévenir le copper qu’il doit gérer ce sprite, c’est-à-dire activer le DMA-copper après lui avoir indiqué l’adresse des datas qui forment le sprite : l’adresse de sa structure. Ceci est réalisé en deux étapes :
- On redéfinit une copper-liste (rappelons qu’il s’agit des instructions que doit éxecuter le copper au cours du balayage de l’écran ).
- On remplace la copper-liste du système par la nouvelle.
STRUCTURE D’UN SPRITE
1 ) La position et la hauteur du sprite :
La structure doit commencer par 2 mots de contrôle (à calculer..). Ces
2 mots de contrôle indiquent au DMA-copper la position horizontale et verticale de la 1re ligne du sprite et celle de la dernière ligne + 1 (ce qui indiquera au copper le nombre de lignes du sprite).
Il faut rappeler qu’une ligne du sprite aura une épaisseur de 1 pixel (un point lumineux). Sa position sur i'axe horizontal peut varier en haute ou en basse résolution de 130 à 460 : sur toute la largeur visible de l'écran. Sa position sur l'axe vertical peut varier de 38 à 300 : sur toute la hauteur visible de l'écran , Ces valeurs sont données à quelques pixels près ; il est possible de les vérifier ou de les affiner en changeant les coordonnées du sprite dans le programme-exemple. Précisons que si la position du sprite est en dehors de l'écran, le système ne le dessine pas, sans nous envoyer pour autant un de ces GURUS dont il a le secret... L’origine de l'écran visible est en haut à gauche et correspond à la colonne 130 (x = 130) et à la ligne 38 (y = 38). Dans tous les cas, les positions horizontales et verticales peuvent être supérieures à 255, et sont donc codées sur 9 bits, cela donne une disposition d’enfer :
Mot de contrôle N° 1 :
Bit N°
151413 1211 109 correspondance E7 E6 E5 E4 E3 E2 E1
Avec les convensions suivantes :
H = Position horizontale de la première ligne du sprite ( x )
E = Position verticale de la première ligne du sprite ( y )
At = bit de contrôle d'attache
L = Position verticale de la dernière ligne + 1 ( ou L = E + nombre de lignes du sprite )
E5 sera par exemple le bit 5 de la valeur désignant la position horizontale de la première ligne, codée en base 2 : si y = 160,
E = 160 = $ A0 = %0 1 0 1 0 0 0 0 0 donc E5 = 1
E8E7 E6 E5 E4 E3 E2 E1 E0
Un sprite ( « lutin » en anglais ) est une image gérée par le COPPER. Cette image préparée par le programmeur est reproduite à l’écran par ie copper. Cette reproduction n’est pas permanente et elle doit être refaite à chaque balayage de l'écran par le faisceau d’électrons.
Que doit faire le programmeur?
Mot de contrôle N° 2 :
Bit N°
15 14 13 12 11 139 8 7
correspondance
L7 L6 L5 L4 L3 L2 L1 L0 At
CREER UN SPRITE of partie)
H0
0
1
0
1
1
0
1
0
0
H8
H7
H6
H5
H4
H3
H2
H1
H0
0
1
0
1
0
0
0
0
0
E8
E7
E6
E5
E4
E3
E2
E1
E0
0
1
0
1
0
1
0
0
0
L8
L7
L6
L5
L4
L3
L2
L1
L0
; traitement du bit H0 ; 1"> ligne
; traitement du bit E8
; calcule dern. Ligne + 1 placer le bit L8
f?ÉsiÉIS®F
1DHC3B BBHB
LE DESSIN DU SPRITE
On place dans la structure des mots de données dont chaque bit indique comment le pixel correspondant doit être visible. D’un point de vue pratique, ces mots seront définis bit par bit donc en binaire (précédés du signe %). Plusieurs paramètres entrent en jeu :
- la largeur du sprite est obligatoirement de 16 pixels, ce qui ferait un mot de 16 bits pour une ligne du sprite. Mais ceci ne permettrait que 2 couleurs : un pixel serait allumé (bit à 1) ou éteint (bit à 0, et il aurait alors la couleur du fond).
- pour éviter cela, chaque pixel est codé avec 2 bits, ce qui lui autorise 4 couleurs : 3 couleurs différentes ou celle du fond (transparent). Ceci oblige à utiliser 2 mots de 16 bits pour faire une ligne du sprite (16 pixels horizontaux).
- la hauteur du sprite, son nombre de lignes, est laissée à l'appréciation du programmeur...
- le DMA-copper, dans sa grande générosité, autorise 8 sprites qu’il arrive à gérer en même temps. Ces sprites sont donc numérotés de ...0 à 7 ! Il y a tout de même une limitation du point de vue des couleurs : les sprites 0 et 1 auront les mêmes couleurs, les sprites 2 et 3 auront les mêmes couleurs, etc.
N° du sprite
0 et 1
2 et 3
4 et 5
6 et 7
Voici les correspondances entre bits et couleurs pour chaque sprite :
Bits choisis
Registres couleurs
1® mot
2e mot
- > couleur du pixel
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx>
0
0
transparent
0
1
coul 17
1
0
coul 18
1
1
coul 19
0
0
transparent
0
1
coul 21
1
0
coul 22
1
1
coul 23
0
0
transparent
0
1
coul 25
1
0
coul 26
1
1
coul 27
0
0
transparent
0
1
coul 29
1
0
coul 30
!
1
coul 31
exemple :
1 ligne d’un sprite en assembleur donc 2 mots de 16 bits pour 16 pixels
dc. w %0011000000000000,%0101111111111111 ; pour le sprite N° 1
De gauche à droite : 1 pixel transparent ( couleur du fond )
+ 1 pixel de couleur 17
+ 1 pixel de couleur 18
+ 1 pixel de couleur 19
+ 12 pixels de couleur 17
Rappel : les registres couleurs commencent en $ DFF180 = coul 0
puis $ DFF182 = coul 1
Chaque couleur est un mélange des 3 primaires : Rouge, Vert, Bleu. Chaque primaire peut prendre 16 intensités différentes, elle est donc codée sur 4 bits.
Donc 12 bits sont nécessaires pour définir une couleur :
Bit N° 15 14 13 12 11 1098 7654 32 10
corresp. Rien ! Rouge vert bleu
exemple : rouge = % 0000 1111 0000 0000 = $ 0F00
En assembleur, cela sera fait pour la couleur 1 par : move.w $ F00, $ DFF182 placée au début du programme. Les couleurs peuvent aussi être modifiées (pendant le balayage de l’écran par le faisceau d’électrons) par une instruction MOVE placée dans la copperliste. Dans cet exemple : dc.w $ 182,$ F00 ou bien dc.w colorl
LA FIN DE LA STRUCTURE :
La structure doit se terminer par 2 mots nuls : dc.w 0,0
Exemple d’une structure sprite complète : ce sprite aurait une hauteur de 8 pixels, il représenterait un petit rectangle. L'intérieur et les bords du rectangle auraient des couleurs différentes.
St ru et u re_s p rite :
; mots de contrôle ; (voir le calcul ; plus haut )
; 8 lignes
dc. w $ A05A,$ A800
dc. w %0011111111111111 ,c
dc. w %0011111111111111,
dc. w %0011000000000011 ,%0000111111111100
dc. w %0011000000000011 ,%0000111111111100
dc. w %0011000000000011 ,%0000111111111100
dc. w %0011000000000011 ,%0000111111111100
dc. w %0011111111111111,
dc. w %0011111111111111,'
dc. w
fin des données
GESTION DU COPPER
1 ) Préparation de la copper-liste :
Les addresses des structures seront envoyées dans les registres $ DFF 120 et suite :
$ DFF120 = SPR0PTH = Mot fort de l’adresse (CHIP) de la structure sprite 0
$ DFF122 = SPR0PTL = Mot faible de l’adresse de la structuresprite 0 $ DFF124 = SPR1PTH = Mot fort de l’adresse (CHIP) de la structure sprite 1
$ DFF122 = SPR1PTL = Mot faible de l’adresse de la structuresprite 1
rem: SPRxPTH(L) signifie SPRite N°x PoinTer High (ou Low ), c’est là que le système stocke l’adresse de la structure d’un sprite. Ces labels peuvent être utilisés par un assembleur digne de ce nom qui supporte les fichiers “include”.
Ces pointeurs sont modifiés par le système au cours du balayage de l’écran, et doivent être restaurés avant chaque balayage, d’où la nécessité de les réinitialiser dans une copper-liste personnelle .
On définit dans cette copper-liste la taille de l’écran utilisé avec DIWS- TRT et DIWSTOP, ainsi que les cycles DMA alloués au DMA bitplane avec DDFSTRT et DDFSTOP. Rentrer dans le détail nous entraîneraient trop loin ; les valeurs utilisées dans le programme-exemple sont conseillées, elles définissent l’écran le plus large possible quand on utilise 8 sprites en basse résolution (LOW-RES). On définit aussi dans cette copper-liste l’adresse du bitplane, le mode de résolution, etc. avec les registres BPLCON et BPLMOD. On définit finalement les couleurs qui seront utilisées et, nous y arrivons, les addresses des structures des sprites. Même les sprites inutilisés doivent être redéfinis, on leur attribuera alors l’adresse d’une structure vide (comportant 0,0 comme mots de contrôle). Tout ceci est réalisé dans la copper-liste par des instructions “MOVE ..."Supposons par exemple que la structure du sprite 1 soit à l’adresse $ 20000, on placerait dans la copper-liste :
dc. w SPR1 PTH,$ 0002 ; mot fort (high) de l’adresse
dc. w SPR1 PTL,$ 0000 ; mot faible (low) de l’adresse ; (Cela correspond en assembleur à :
; MOVE.W $ 0002,$ DFF124 ; $ DFF000 + SPR1PTH MOVE.W $ 0000,$ DFF126 I + SPR1PTL )
Mais tout n’est pas aussi simple, car les addresses des structures dépendent de la localisation du programme, et il faut alors prévoir une routine qui prépare la copperliste ... Ceci est encore compliqué si l’on utilise un assembleur qui ne sait pas imposer de chargement en CHIP memory : il faut donc réserver de la mémoire en CHIP, y recopier les données (copper-liste, bitplane,structures des sprites), et ajuster enfin la copper-liste ... (voir l’exemple depuis “run:” jusqu’à “dbra d1, relog3” ). Les heureux utilisateurs de DEVPAC savent qu’il suffit d’ajouter une instruction “section bidon,CODE_C” avant les données pour que celles- ci soient chargées en CHIP memory .
Dans l’exemple choisi, cela ne poserait pas de problème car 208 est une ligne-écran située en-dessous de 169. Pour schématiser le déroulement des opérations du copper :
POSITION du RASTER faisceau d’électrons
variable ligne 160 ligne 161 ligne 162 ligne 163 ligne 164 ligne 165 ligne 166 ligne 167 ligne 168
I0000 ; du 1er “sous-sprite”
; OFFSETS à partir de SDFF000
dmaconr copl le copjmpl dmacon
spr0pth spr0ptl sprlpth sprlptl spr2pth spr2ptl spr3pth spr3ptl
; OFFSETS dans EXEC.LIBRARY
; 8 lignes qui le ; définissent
dc. w %0011000000000011 ,%Ê
dc. w %0011000000000011 ,%6
dc. w %0011111111111111.c
dc. w %0011111111111111,*
dc. w $ D05A,$ D200
dc. w
dc. w %t
dc. w 0,0
11111111111111
11111111111111
Forbid Permit
OldOpenLibrary CloseLibrary AllocMem FreeMem run:
move.l 40*312,d0 move.l $ 10002,d1 move.l 4,a6 jsr allocmem(a6) beq fini
move.l d0,bplane1
move.l 28*2, d0 move.l $ 10002,d1 move.l 4,a6 jsr allocmem(a6)
=-132
=-138
=-414
=-198
=-210
Il ) Installation de la copper-list
Quand la structure et la copper-liste sont prêtes, il faut INITIALISER le DMA-copper. Il suffit de désactiver le DMA-copper :
move.w $ 0080,$ DFF096 ; DMACON
de remplacer la copper-liste du système par la nôtre :
move.l chiplist,$ DFF080 ; COP1LCH et COP1LCL
clr.w $ DFF088 ; COPJMP1 = redémarrage copperl
et de lancer le DMA copper et le DMA bitplane dont la participation est nécessaire car l’écran a été redéfini :
move.w $ 8380,$ DFF096 ; DMACON -> DMA copper ; et DMA bitplane activés
Le sprite devient alors visible à l'écran ...
PROGRAMME SUIVANT
Le programme suivant dessine un “S” avec 1 sprite ...
Pour vous entraîner, essayez de dessiner plusieurs sprites et peut-être même de créer un “super-sprite” en collant côte à côte 8 sprites: le résultat aura alors 128 pixels de large...
COMPLEMENT SUR LES STRUCTURES
Il vous semble inutile de finir une structure par 2 mots nuls, étant donné que les mots de contrôle indiquent au copper le nombre de lignes de celle-ci. Il y a à cela une raison très simple : une structure peut contenir plusieurs sprites qui sont “chaînés” les uns aux autres. L’ensemble correspondra pour le copper au sprite 0 par exemple. Pour ajouter un sprite à la chaîne, on remplace les mots de fin ( 0,0 ) par les mots de contrôle du sprite supplémentaire, suivis de ses datas et de 2 mots de fin nuls. La nouvelle structure ressemblerait à ceci :
structure_sprite0 :
dc. w $ A05A,$ A800 ; mots de contrôle
dc. w %0011111111111111 ,%000000
dc. w %0011111111111111 ,%000000
dc. w %0011000000000011 ,%000011111 1 1 11100
dc. w %0011000000000011 ,%0000111111111100 0111111111100 0111111111100
; mots de contrôle ; du 2e "sous-sprite"
; et 2 lignes qui le ; définissent ; mots de fin de la ; structure
Les mots de contrôle du 2e sous-sprite sont calculés comme précédemment ce qui le définit comme un sprite de 2 lignes de hauteur placé en x = 180 et en y = 208. Comme ces 2 sous-sprites sont gérés par le copper sous le nom de “sprite 0” il serait plus juste de dire que le copper peut gérer 8 structures...
Est-ce à dire que l’on peut définir autant de sprites que l'on veut ? Hélas, non ! Les limitations qui existent sont dûes au fonctionnement du copper et à la taille de l’écran .
En effet le DMA copper, quand il gère cette structure, commence par lire la 1re ligne (logique I). Il voit qu'il s'agit de $ A05A et de $ A800, il va donc attendre la ligne 160 avant de commencer son travail en ce qui concerne cette structure. Arrivé à la ligne 160, il attend la colonne 180 et reproduit la 1re ligne du 1er sous-sprite à l’écran. A la ligne suivante (161 ), il reproduit la 2e ligne du 1er sous-sprite, etc.
Il arrive donc à la ligne 168 quand il lit et interprète les 2 mots de contrôles $ D05A et $ D200. Il ne pourra commencer de reproduire la ligne du 2e sous-sprite qu'à partir de la ligne 169. Conclusion : le 2e sous-sprite ne sera dessiné que si sa première ligne commence au-delà de la ligne-écran 169.
LIGNE de la STRUCTURE interprétée
dc. w $ A05A,$ A800 ligne de datas N° 1 ligne de datas N° 2 ligne de datas N° 3 ligne de datas N° 4 ligne de datas N° 5 ligne de datas N° 6 ligne de datas N° 7 ligne de datas N° 8
dc. w $ D05A,$ D200
ligne 169 à 207 , rien pour cette structure
ligne de datas N° 1 ligne 208
ligne de datas N° 2 ligne 209
dc. w ....ligne 210
Si on ajoute un 3e sous-sprite , il ne pourra pas être placé avant la ligne 211 ! Par contre, il n’y a pas de limitation en ce qui concerne sa position sur la ligne 211 ...
Sachant que ce qui vient d’être dit est valable pour les 8 structures, il est possible de dessiner et d’animer une vingtaine de sprites ou plus... Dans la limite où ils tiennent sur l’écran, ce qui étend considérablement les possibilités de scrolling !
Le mois prochain : comment animer les sprites sous IRQ .
D. GENOT
NDLR : Enfin une copper-list relogeable. Bravo à l'auteur et que d'autres en tirent profit pour programmer l'Amiga correctement.
Ce programme a été écrit sous DEVPAC2 pour Commodore_Revue Il fonctionnera tel quel sous K-SEKA
EQU
$ 002
W spr4pth
EQU
$ 130
EQU
$ 080
spr4ptl
EQU
$ 132
EQU
$ 088
sprôpth
EQU
$ 134
EQU
$ 096
sprôptl
EQU
$ 136
spr6pth
EQU
$ 138
EQU
$ 120
spr6ptl
EQU
$ 13a
EQU
$ 122
spr7pth
EQU
$ 13c
EQU
$ 124
spr7ptl
EQU
$ 13e
EQU
$ 126
colorQ
EQU
$ 180
EQU
$ 128
colorl 7
EQU
$ 1a2
EQU
$ 12a
colorl 8
EQU
$ 1 a4
EQU
$ 12c
colorl 9
EQU
$ 1a6
EQU
$ 12e »
; 312 lignes de 40 octets en LOW-RES ; en CHIP avec effacement
; réserve mémoire pour bitplane ; si impossible !
; 4 mots + 12 lignes de 2 mots ; en CHIP avec effacement
; réserve mémoire pour structure sprite0
H
move.b (a0)+,(a1)+ dbra d0,relogl
move.l struct0,a1 move.l 180,d0 move.l 160,d1 move.l 12,d2 jsr placem_sprite
beq fin2 ; si impossible !
Move.l d0struct0
move.l (fin_structure-structure_sprite0),d0 ; longueur
subq.l 1,d0
move.l struct0,a1
move.l structure_sprite00,a0
relogl :
move.l (fin_clist-clist),d0 move.l $ 10002,d1 move.l 4,a6 jsr allocmem(a6) beq fin3
move.l d0,chiplist move.l (f in_cl ist-clist), d0 subq.l 1,d0 move.l clist,a0 move.l chiplist.al
relog2:
move.b (a0)+,(a1)+ dbra d0,relog2
move.l bplanel ,d0 move.l chiplist,a0 add.l (b1h-clist),a0 move.w d0,4(a0) ¦" swap d0 move.w d0,(a0) move.l struct0,d0 " move.l chiplist,a0 " add.l (im0h-cllst),a0 move.w d0,4(a0) swap d0 move.w d0,(a0) move.l 6,d1
move.l (structure_vide-structure_sprite0).d0 ; adresse add.l struct0,d0 J‘ '* "!J‘
re!og3 : addq.l 8,a0 move.w d0,4(a0) swapd0 move.w d0,(a0) swapd0 dbra d1 ,relog3
move.l 4,a6 jsr Forbid(a6)
lea $ dff000,a5 move.w dmaconr(a5),d0 move.w d0,dmasauv
move.w $ 0080,dmacon(a5) move.l chiplist.copl Ic(a5)
clr.w copjmpl (a5) move.w $ 8380,dmacon(a5)
bsr souris
move.l gfxname,a1 moveq 0,d0 move.l 4,a6 jsr OldOpenLibrary(a6) move.l d0,_gfxbase move.l d0,a4 move.l 38(a4),cop1 Ic(a5) recopie structure en CHIP
adresse datas sprite0 en CHIP x = 180 y = 160 long sprite
calcule et place mots de contrôle
taille copper-liste en CHIP avec effacement
; réserve mémoire pour copper-liste ; si impossible !
; longueur copper-liste
; recopie copper-liste en CHIP
; prépare copper-liste en CHIP ; adresse bitplane low
; adresse bitplane high ; adresse structure en chip
; placée dans chip-copper-liste: low
. Et high ; 7 sprites restants
de la struct vide
; placée en chip-copper-liste : low ; et high
; initialise les 7 autres sprites ; sur une structure vide en chip
; supprime le multi-tâches
sauve la valeur du DMA contrôleur
désactive le DMA copper remplace anc. Copperliste par la nouvelle
0 dans le compteur interne du copper c’est parti ! DMA bitplane et DMA copper activés
attend bouton gauche de la souris
; ouvre graphics.library ; startlist(Â4) -> restaure
; anc. Copper-liste
clr.w copjmpl (a5)
; 0 dans le compteur interne du copper
move.w dmasauv,d0
add.w $ 8000, d0
; restaure le DMA et le réactive
move.w d0,dmacon(a5)
move.l _gfxbase,a1
move.l 4,a6
jsr CloseLibrary(a6)
; ferme graphics.library
move.l 4,a6
jsr Permit(a6)
; remet multi-tâches
move.l (fin_clist-ciist),d0
; taille copper-liste
move.l chiplist.al
; adresse
move.l 4,a6
jsr freemem(a6)
; libère mémoire copper-liste
fin3:
move.l 28*2,d0
; taille
move.l struct0,a1
; adresse
move.l 4,a6
jsr freemem(a6)
; libère mémoire structure sprite0
fin2:
move.l 40*312,d0
; taille
move.l bplanel ,a1
; adresse
move.l 4,a6
jsr freemem(a6)
; libère mémoire bitplane
fini:
rts
souris:
btst 6,$ bfe001
bne souris
rts
placem_sprite:
; CE : A1 = sprite_data ; D2 = nbre lignes
; D0 = pos hor et D1 = pos vert
; CS : mots de contrôle placés
| Isr.w 1 ,d0
bcs s1
bclr 0,3(a1)
bra suil
s1:
bset 0,3(a1)
suil:
move.b d0,1(a1)
move.b d1,(a1)
; 1re ligne
btst 8,d1
bne s2
bclr 2,3(a1)
bra sui2
s2:
bset 2,3(a1)
sui2:
add.l d2,d1
; calcule dern. Ligne + 1
move.b d1,2(a1)
btst 8,d1
bne s3
bclr 1,3(a1)
bra sui3
s3:
bset 1,3(a1)
sui3:
rts
even
dmasauv: dc.l 0
intsauv: dc.l 0
even
_gfxbase: dc.l 0
gfxname: dc.b “graphics.library ,0
even
clist:
(Suite page 51)
dc. w spr6pth,0
dc. w spr6ptl,0
dc. w spr7pth,0
dc. w spr7ptl,0
dc. w $ ffff,$ fffe fin_clist:
even
structure_sprite0:
dc. w i de, de de de de de de de de de de de
:.w %0000000011110000,%0000000100000000 :.W %0000000110011000,%0000001000000000 :.w %000000111 :.w ;.w
:.w %000000000011 :.w %0111110000011 j
:.w %0111100000001100,% 1000000000010000 ).W %0100110000011000,% 1010000000100000 :.W %0100011000110000,%1000100001000000 :.w%01000001111i structure_vide:
dc. w 0,0
; fin des datas
fin_structure:
even
chiplist: dc.l 0 struct0: dc.l 0 bplanel: dc.l 0
dc. w $ 008e,$ 1b81
( DIWSTRT ) définit
dc. w $ 0090,$ 38ff
( DIWSTOP ) l’écran
dc. w $ 0092, $ 0038
( DDF STRT ) le plus grand
dc. w $ 0094,$ 00d0
( DDF STOP ) possible
dc. w $ 00e0
(BPL1PTH)
b1 h: dc.w0
bitplanel high
dc. w $ 00e2
(BPL1PTL)
b11: dc.w 0
bitplanel low
dc. w $ 0108,0000
( BPL1MOD ) modulo = 0
dc. w $ 0100,$ 1200
( BPLCON0 ) 1 bitplane LOW-RES
dc. w $ 0102,$ 0000
( BPLCON1 ) pas de scrolling playfield
dc. w $ 0104,$ 0000
( BPLCON2 ) priorité des sprites play-
field
dc. w color0,$ 000
couleur fond = noir
dc. w colorl 7,$ f00 ; -> rouge
dc. w colorl 8,$ 0f0 ; -> vert
dc. w colorl 9,$ 00f; -> bleu
couleurs sprite 0
dc. w spr0pth
adresse structure sprite 0
im0h: dc.w 0
high
dc. w spr0ptl
im0l: dc.w 0
et low
dc. wspr1pth,0
ces addresses
dc. w spr1ptl,0
dc. w spr2pth,0
seront
dc. w spr2ptl,0
dc. w spr3pth,0 ;
préparées
dc. w spr3pt!,0
dc. w spr4pth,0 ;
lors du relogement
dc. w spr4ptl,0
dc. w spr5pth,0 ;
de la copper-liste
dc. w spr5ptl,0
; pour adresse copper-liste ; pour adresse structure sprite0 ; pour adresse bitplane
; en chip
; so , don’t panic !
; mots de contrôle
; 12 lignes
_
APPLICATION
JOUER UNE MUSIQUE SOUNDTRACKER EN G FA
Nous allons encore une fois faire une légère digression au sein de notre cours assembleur, pour étudier de près un problème soulevé par Jean-Marc Fauchan, gentil lecteur de Sâone-et-Loire, à savoir : jouer une musique SoundTracker depuis le GfA-Basic.
La PlayRoutine livrée avec SoundTracker est en effet destinée presque exclusivement à une inclusion au sein d’une démo, donc écrite en assembleur. Pour ce qui est de l'incorporer au GfA, c'est une toute autre paire de manches, que nous allons d’ailleurs relever dès maintenant et sans plus tarder. Bref, nous allons la disséquer joyeusement et l'adapter au format désiré, c’est-à-dire :
- la rendre entièrement relogeable (c'est indispensable et le plus gros du travail (quoique...)) ;
- rendre son appel possible depuis le GfA, c'est-à-dire lui faire accepter certains paramètres, passés par l'intermédiaire de la pile ;
- évidemment, l'appeler sous interruption, tous les 1 50° de seconde (à chaque VBL, quoi).
DU GFA AU LM
Avant que d’aller plus loin, je tiens à préciser que je ne dispose que de la version
2. 1 de la PlayRoutine, c’est-à-dire autorisant un maximum de 15 instruments (alors que mon SoundTracker est le 2.4 avec 31 instruments, allez-y comprendre quelque chose... Soit dit en passant, si un lecteur sympa veut bien m’envoyer la
PlayRoutine 2.4, je suis preneur), mais le principe reste le même.
Examinons tout d’abord comment le GfA-Basic passe ses paramètres à une routine en langage-machine. Ceux qui ont déjà tâté du C ne seront pas du tout dépaysés, puisque c’est exactement le même procédé qui est utilisé (d'où le nom de la fonction “C:”, d’ailleurs), à savoir que les paramètres sont passés par la pile, dans l’ordre de leur énumération. Ainsi par exemple mais pas par hasard :
VOID C:adresse%(L:parametre1 %, W: parametre2&, W: parametre3&) sera empilé comme suit :
SP Adresse de retour (pour RTS)
SP + 4 paramétré 1 % (32 bits)
SP + 8 paramétré 2& (16 bits)
SP +10 paramétré 3& (16 bits)
(nous ne traiterons pas des variables alphanumériques, qui n'ont strictement aucun rapport avec notre choucroute du mois (*)).
Pour récupérer ces paramètres, il suffit donc de faire quelque chose dans le genre :
move.l 4(sp),d0 ; d0 = paramètre 1 %
move.w 8(sp),d1 ; d1 = paramétré 2&
move.w 10(sp),d2 ; d2 = paramétré 3&
Mais pourquoi donc se masturber ainsi les neurones à passer des paramètres ? Tout simplement parce que la routine d'initialisation de la PlayRoutine nécessite de connaître l’adresse où est logé le module à jouer (ce sera notre “paramètre 1%”). Pour la frime, "paramètre 2&” permettra de définir la vitesse de la musique (commande F dans SoundTracker), ce qui peut s'avérer utile lorsqu'on veut jouer plusieurs morceaux les uns à la suite des autres et que les uns changent de vitesse sans prévenir personne. Et enfin, le troisième paramètre servira à définir la priorité du serveur d'interruption que nous allons créer spécialement pour l'occasion.
Amas bpbm
'
interrupt: dcb.b IS_SEE
; Nom du serveur d'iniermption (facultatif)
"SoundTracker en GFA ! Yeaah !'',0
dc. b
even
; A partir d'ici, commence la PiayRoutine normale
; Mega fasi playroutine for D.O.C-SoundTracker V2.1 ; Improved and omptimized by U’nknown of D.O.C ; Based on the playroutine from TJC
mt init
movei
mt_data(pc),a0
lea
$ 1d8(a0),a0
movei
S0080,d0
moveq
0,d1
mi initl:
movei
d1,d2
subq.w
l,d0
mt init2:
movei)
(a0)-r,dl
cmp.b
d2,d1
bgt.s
mt_init1
dbf
d0,mt init2
addqi)
l,d2
mt init3:
movei
mLdata(pc),a0
iea
mt_sample1(pc),al
asïi
S08,d2
asli
S02,d2
add.1
$ 0258, d2
add.1
a0,d2
moveq
S0e,d0
mt init4:
movei
d2,(a1)r
moveq
$ 00,d1
move.w
42(a0),d1
asli
1,d1
addi
d1,d2
lea
$ 1e(a0),a0
dbf
d0,mt_init4
lea
mt_sampie1(pc),a0
moveq
$ 00,d0
mt dear:
movei
(a0,d0),a1
clri
(al)
addqi
4,d0
cmpi
$ 3c,d0
bne.s
mt_ciear
clr.w
Sdi3a8
dr.w
$ dS2)b8
clr.w
$ dff0c8
clr.w
$ dff0d8
lea
mî_reloc(pc),a4
cki
mtjxiriniplay-mt_reloc(a4)
clri
mtj)artnote-mt_reloc a4)
clri
mt_partpoint-mi_reloc(a4)
movei)
mt_data+$ 1 d6(pc),d0
andi.w
$ 00ft,d0
move.w
d0,mt_majq)art-mt_reloc(a4)
rts
mi end:
CÎT.W
Sdff0a8
clr.w
Sdff0b8
clr.w
$ dâ25c8
clr.w
$ di3d8
move.w
$ 000t,$ dff096
ris
mtjreloc:
; C'est là qu'est l'astuce !
Nop mt music:
lea
mt_reloc(pc),a4
addqi
1 ,mt_counter-mireloc(a4)
mt cool:
cmpi
6,mt_counter-mueloc(a4)
bne.s
mt_noisix
clri
mt_counter-mt_reloc(a4)
bra
mt_rout2
mt notsix:
lea
mt aud1temp(pc),aô
tslb
M)
beq.s
mt_arp1
lea
$ dff0a0,a5
bsr.s
mt_arprout
mtarpl:
lea
mt_aud2temp(pc),aD
3(aô)
tsti)
mcciir
inciude
include
inciude
; d0=adresse du module ; sauvée dans mt data
mt_data(pc),a0
d0,(a0)
mt_cooi(pc),a0
st_inii1
; de la PiayRoutine
;al =struct Interrupt ;type(=interruption) . ; priorité
; Nom de l'interruption
; Adresse de la routine ; Pas de données,
; Interruption VBL
movei
lea
movei
clri
movei
jsr
rts
st_end:
moveai
lea
movei
jsr
F*
; Fin normale de la PiayRoutine
VBLServer:
movemi
bsr
Sauve les registres importants Appelle la PiayRoutine
a0 = CustomBase Tout va bien... dao !
Cette priorité, comprise entre -128 et +127, est la cuisine interne d’Exec et n’a pas ici un rôle réellement capital, une valeur de 0 étant tout à fait convenable. De toute manière, le temps que vole la PiayRoutine pendant la VBL suffit à faire passer l’Amiga pour un ZX81 de compétition. Que voulez-vous, on n’a rien sans rien...
Un mot encore à propos de l’adresse du module : il doit se trouver en CHIP-RAM pour que le processeur sonore puisse accéder aux instruments digitalisés. Le listing 3, superbe exemple en GfA, évidemment, s’occupe de ce détail. Le listing 2 quant à lui se cnarge de créer ie fichier REPLAY.INL qui sera inséré dans l’INLINE. Et enfin, le listing 1 est le source assembleur de la PiayRoutine modifiée (écrit pour Devpac 2, il serait temps dejeter K-Seka aux orties) plus les diverses initialisations et l’installation du serveur d’interruption (car chez moi, on fait les choses proprement, ma bonne dame).
Voilà. Je n’en dirai pas plus sur le GfA - “chacun son métier et les vaches seront bien gardées” - et vous laisse ipso facto avec quelques jolies lignes de datas bien fournies à taper (attention les crampes). On se retrouve le mois prochain avec une routine pour lire décompacter un fichier IFF ILBM toutes résolutions, même en Overscan. Ciaô.
Max
PS au passage : si vous avez quelques ioées comme ça de petits trucs à réaliser, faites m’en part ! J'attends vos suggestions avec impatience et à la rédaction.
; La pile se présente ainsi :
; SP Adresse de retour (pour RTS) (I)
; Spt4 Adresse du module (doit être enCHIP-RAM) (I) ;S?t8 Vitesse de dépan (0 à 15) (.W)
; Spt1 0Priorité (-127 à r128) (.W)
; Place la routine principale sous interruption moveai $ 4.w,aô
lea interrupt(pc),al
move.b
NT TTERRlJPT,LN_TYPE(a1) move.w movei)
LISTING 1 : la PiayRoutine légèrement customisée
d0,LN_PRI(a1)
VBLName(pc),a0
a0,LN_NAME(a1)
VBLServer(pc),a0
a0,IS C0DE(a1)
IS.DATA(al)
MB_VERTB,d0
jVOÂddlntServeifaô)
$ 4.w,a6
mtenupt(pc),a1
M8J ERT3,d0
_LVORemIntServer(aô)
mt_end
ara
bra
stjnit:
movei
lea
movei
lea
move.w
andi-b
beq.s
movei)
stjniti:
"Devpac2:Inciude " "exec inierrupisi" "exec execjib. I" "hardware inibiisi"
munit ; Initialisation normale
stjnit st end
Ca, c'est le serveur d'interruption qui sera appelé à chaque VBL
; écrite dans la playroutine (beurk!)
D2-d7 a0 a2-a4,-(sp)
mt_music
movemi (sp)-r,d2-d7 a0 a2-a4 lea $ dri000(a0
moveq 0,d0
rts
; Strucrure Interrupt (initialisée plus haut)
... ¦ --- ...¦ - '' - "
beq.s
mt_aip2
$ dfi0b0,a5
move.1
mt_parmrplay-mt reloc(a4),d0
iea
moveq
$ 00,dl
bsr.s
mi_aiprout
movei
(a2,d0),d1
mt_arp2:
asLl
$ 08, d1
lea
mt_aud3iemp(pc),a6
asli
$ 02,dl
tstb
3(a6)
addl
mt_partnote-mt_reloc(a4),d1
beq.s
mï_arp3
movei
d1 ,mtj)artpomt-mt.reloc(a4)
lea
$ dn0c0,a5
¦..
clr.w
mt dmacon-mt reloc(a4)
bsr.s
mt_arprout
lea
$ df(0a0,a5
mi_aud1temp(pc),a6
mt_aip3:
lea
lea
mt aud4temp(pc),a6
bsr
mt_playit
$ df(0b0,a5
ist b
M)
lea
beq.s
mi_arp4
lea
mt_aud2temp(pc),a6
lea
$ dï0d0,a5
bsr
mt_playit
bra.s
mLaiprout
lea
$ di0c0,a5
mt_aip4:
lea
mt_aud3iemp(pc),a6
rts
bsr
mrplayit
mt„arprout:
lea
Sdff0d0,a5
move.b
2(a6),d0
lea
mt_aud4temp(pc),a6
andb
$ 0f,d0
bsr
mtjolayit
$ 008a,d1
tstb
d0
move.w
beq.s
cmpi)
beq.s
mi arpegri 1,d0 mt_portup 2,d0
mtjoop:
dbf
move.w
d1 ,mtloop $ 8000,d0
cmp.b
orw
mt dmacon-mt reloc(a4),d0
beq.s
mt_portdwn
move.w
d0,$ dff096
rts
lea
mt aud4temp(pc),aô
mt_portup:
cmp.w
l,14(a6)
moveq
move.b
$ 00,d0
bne.s
mt voice3
3(a6),d0
moveJ
10(a6),$ dff0d0
sub.w
d0,22(a6)
move.w
1,$ dff0d4
cmp.w
* $ 71,22(a6)
mt_voice3:
bpls
mt ok1
lea
mt aud3temp(pc),ct6
move.w
$ 7l,22(a6)
cmp.w
1,l4(a6)
mt_ok1:
bnes
mt voice2
move.w
22(a6),6(a5)
move.1
10(a6),$ dff0c0
rts
move.w
l,$ dff0c4
mt_portdwn:
mt_voice2
moveq
$ 00,d0
lea '
mt aud2temp(pc),a6
movei)
3(a6),d0
cmp.w
bnes
1,14(a6)
add.w
d0,22(aô)
mt voicel
cmp.w
S358,22(a6)
movei
10 a6),$ dff0b0
bmi.s
mt ok2
move.w
1,$ dff0b4
move.w
$ 358,22(a6)
mt_voice1 :
mt_ok2:
lea
mt aud1temp(pc),a6
move.w
22(a6),6(a5)
cmp.w
1,14(a6)
rts
bnes
mt voice0
mi_capegrt:
move.1
10(a6)$ dfl0ct0
cmpi
1,mtjounter-mtjeIoc(a4)
move.w
1 dfî0a4
beq.s
mtJoop2
m!_voice0:
cmpl
2,mt_counter-mi_reloc(a4)
movei
mt_partnote-mi reloc(a4),d0
beq.s
rat_loop3
addl
510,d0
cmpl
beq.s
3,mtj»imter-mijeioç(a4)
movei
d0,mt_paitnoie-mi rdoc(a4) .
Mtjoop4
cmpi
$ 400,d0
cmpi
4,mt_counter-mt_reloc(a4)
bne.s
mi_stop
beq.s
mt_loop2
mtjiighen
cmpi
5,mt_counter-mi_reioc(a4)
clri
mtjparmote-mt_reloc(a4)
beq.s
mt_loop3
addqi
1,mrpaitmplay-mt reioc(a4)
rts
moveq
$ 00,d0
mt_loop2:
move.w
mi_maxpart-mt_reloc(a4),à0
moveq
$ 00,d0
movei
mi_partmplay-mt_æloc(a4),d1
movei
3(a6),d0
cmpi
d0,dl
îsri)
440
bneb
mi_stop
bras
mt_coni
clri
mt_partnrplay-mtieloc(a4)
mt_loop3:
mt.stop:
moveq
$ 00,d0
tstw
mt_status-mtjeloc(a4)
movei)
M) .à0
beqs
mi_stop2
andb
$ 0(,d0
clr.w
mî_status-mt_reloc(a4)
bras
mt_cont
bras
mt_higher
mt_loop4:
mt.stopi
move.w
I6(a6),d2
rts
bra.s
mt_endpart
mt_playit
(a0,d1 ), a6)
mt_cont
movei
aslw
l,d0
addqi
4,d1
moveq
$ 00, ai
moveq
$ 00,d2
move.w
1(Ka6),d1
movei
2(a6),d2
lea
mi_arpeggio(pc),a0
andb
$ f0,d2
mt_loop5:
Isrb
- 4,d2
move.w
(a0,d0),d2
tstb
d2
cmp.w
(00) , d1
beq.s
mt nosamplechange
beq.s
mt endpart
moveq
$ 00, 33
addqi
2,ct0
lea
mt samples(pc),a1
bra.s
mt_loop5
movei
d2,d4
mt_endpart
asli
2,d2
move.w
d2,6(a5)
mulu
$ 1e,d4
rts
movei
(a1,d2),4(a6)
mt_rout2:
move.w
(ct3,d4),8(a6)
movei
mtjdata(pc),a0
move.w
2(a3,d4),18(a6)
lea
Sc(a0),a3
move.w
4(a3,d4),d3
lea
51d8(a0),a2
ist.w
d3
lea
S258(a0),a0
beq.s
mt_displace
- m a
ï V-r':)
TT
00 i
movei
4(a6),d2
addi
d3,d2
movei
d2,4(a6)
movei
d2,10(a6)
move.w
6(a3,d4),8(a5)
move.w
6(a3,d4),14(a6)
movelw
18(a6),8(a5)
bras
mtjiosamplechange
mt_displace:
movei .
4(a6),d2
addi
d3,d2
movei
d2,10(a5)
move.w
6(a3,d4),14(a6)
move.w
18 a6),8(a5l
mtjiosamplechange:
ist.w
(afi)
beqs
mtjetrout
move.w
(00,16(0®
move.w
20(a6),$ dS096
movei
4(a6),(a5)
move.w
mm
move.w
mm)
move.w
20(a6),d0
or.w
d0,mt.dmacon-mtjeloc(a4)
mtjetrout:
Ist.W
m
beqs
mt nonewper
move.w
(a6j,22(a6)
mtjionewper
moveb
2(a6),d0
andb
$ 0f,d0
. Cmpb
11,d0
beq.s
mt_posjmp
cmpb
l2,d0
beq.s
mt setvol
cmpb
1340
beqs
mt break
cmpb
14,d0
beq.s
mt setfil
cmpb
15,d0
beq.s
mt_setspeed
rts
mtjposjmp:
notw
mt siatus-mt reloda4)
moveq
$ 00,d0
movei
3(a6),d0
subqb
$ 01,d0.
Movei
d0,mtj)aitniplay-mtjeloc(a4)
rts
mt setvot
moveb
mm
rts
mt break:
notw
mt_status-mt_reloc(a4)
rts
mt_seml:
moveq
$ 00,d0
moveb
3(a6),d0
andb
$ 01 ,d0
rolb
$ 01,d0
andb
$ îd,$ bfe001
orb
d0ibfe001
rts
mtjetspeed:
moveb
3(a6),d0
andb
$ 0t,d0
beqs
mt_back
dri
mt_counter-mtjeloc(a4)
moveb
d0mn_cool-mtjeioc+5(a4)
mt_badc
rts
mt_audltemp:
dcbw
10,0
dcw
$ 0001
dcb.w
2,0
mt_aud2temp:
dcbw
10,0
dcw
$ 0002
dcb.w
20
mt_aud3temp:
dcb.w
10,0
dcw
$ 0004
dcb.w
2,0
mt_aud4temp:
dcb.w
10,0
dc. w
$ 0008
dcb.w
2,0
mt_partnote:
dci
0
mtjxiriniplay:
dci
0
ÛQEiÛ ûQfliL
mt.counter:
dc. l
0
mtjxrrtpomt:
ded
0
mt_samples:
dc. l
0
mt_sample1 :
dcb.l
15,0
mt_maxpart:
dc. w
$ 0000
mt.dmacon:
dc. w
$ 0000
mt_status:
dc. w
$ 0000
mt_arpeggio:
dc. w $ 0358,$ 0328,$ 02FA,$ 02D0,$ 02A6,$ 0280,$ 025C
dc. w $ 023A,$ 021 A,$ 01 FC,$ 01 E0,$ 01 C5,$ 01 AC,$ 0194,$ 017D
dc. w $ 0168,$ 0153,$ 0140,$ 012E,$ 011 D,$ 010D,$ 00FE,$ 00F0
dc. w $ 00E2,$ 00D6,$ 00CA,$ 00BE,$ 00B4,$ 00 AA,$ 00A0,$ 0097
dc. w $ 008F, $ 0087, $ 007F$ 0078, $ 0071 ,$ 0000,$ 0000,$ 0000 mt_data:
de.! 0
LISTING 2 : créateur de REPLAY.INL
' Création de REPLAY.INL
- FREI0)
CLFtcode$
FOR i&=0 TO 765
READa$
code$ =code$ +MKl$ (VAL("8tFI"+a$ ))
NEXT i&
OPEN ”o", 1 ."REPLAY.INI"
BPUT 1, V:code$ , 1530 CLOSE 1
PRINT "Fichier REPLAYJNL créé."
END
' Vérifiez bien et sauvegardez avant 1 d'exécuter !
DATA 6000,0006,6000,0058,202F,0004,41 FA.05E6 DATA 2080,41 FA,016E.302F0008,0200,000F,6704 DATA 1140,0005,6100,009A,2C78,0004,43FA,005E DATA 137C,0002,0008,302F,000A,1340,0009,41 FA DATA 0062,2348.000A,41 FA,002E,2348,0012,42A9 DATA 000E.203C,0000,0005,4EAE,FF58,€75,2C78 DATA 0004,43FA,0028,203C,0000,0005,4EAE.FFS2 DATA 6100,00E4,4E75,48E7,3FB8,6100,00FE,4CDF DATA 1 DEC,41 F9,00DF,F000,7000,4E75,0000,0000 DATA 0000,0000,0000,0000,0000,0000,0000,0000 DATA 0000,536F,756E,6454,7261,636B,6572,2065 DATA 6E20,4746,4120,2120,5965,6161,6820,2100 DATA 207A.0532.41 E8.01 D8.203C,0000,0080,7200 DATA 2401,5340,1218,B202,6EF6,51 C8,FFF8,5202 DATA 207A0512,43FA,047E,E182,E582,0682,0000 DATA 0258, D488,700E,22C2,7200,3228,002A.E381 DATA D481,41 E8.001 E,51 C8,FFEE,41 FA,0458,7000 DATA 2270,0000,4291,5880,B0BC,0000,003C.66F0 DATA 4279,00DF.F0A8,4279,00DF.F0B8,4279,00DF DATA F0C8,4279,00DF,F0D8,49FA,003E,42AC,03DC DATA 42AC,03D8,42AC,03E4,103A,0680,0240,00FF DATA 3940,0428,4E75,4279,00DFF0A8,4279,00DF DATA F0B8,4279,00DF.F0C8,4279,00DF,F0D8,33FC DATA 000F,00DF,F096,4E75,4E71,49FA,FFFC,52AC DATA 03E0.0CAC,0000,0006,03E0,6608,42AC.03E0 DATA 6000,0114,4DFA,0352,4A2E,0003,6708,4BF9 DATA 00DF,F0A0,6138,4DFA,035A,4A2E,0003,6708 DATA 4BF9,00DF,F0B0,6126,4DFA,0362,4A2E,0003 DATA 6708,4BF9,00DF,F0C0,6114,4DFA,036A,4A2E DATA 0003,6708,4BF9,00DF,F0D0,6002,4E75,102E DATA 0002,0200,000F4A00.674E.B03C,0001,6708 DATA B03C,0002,6722,4E75,7000,102E.0003.916E DATA 0016.0C6E.0071,0016,6A06,3D7C,0071,0016 DATA 3B6E.0016,0006,4E75,7000,102E,0003,016E DATA 0016,0C6E,0358,0016,6B06,3D7C,0358,0016 DATA 3B6E.0016,0006,4E75.0CAC,0000,0001,03E0 DATA 672A.0CAC,0000,0002,03E0,672A,0CAC,0000 DATA 0003,03E0.672C.0CAC,0000,0004,03E0.670C DATA 0CAC,0000,0005,03E0,670C,4E75,7000,102E DATA 0003,E808,6012,7000,102E,0003,0200,000F DATA 6006,342E,0010,6018,E340,7200,322E,0010 DATA 41 FA,0314,3430,0000,B250,6704,5488,60F4 DATA 3B42,0006,4E75,207A,034C,47E8,000C,45E8 DATA-01 D8.41 E8,0258,202C.03DC,7200,1232,0000 DATA E181 ,E581 ,D2AC,03D8,2941,03E4,426C,042A DATA 4BF9,00DF,F0A0,4DFA,0210,6100,00F0,4BF9 DATA 00DF,F080,4DFA,021 C.6100,00E2,4BF9,00DF
DATA F0C0,4DFA,0228,6100,00D4,4BF9,00DFF0D0 DATA 4DFA,0234,6100,00C6,323C,008A, 51 C9.FFFE DATA 303C,8000,806C,042A,33C0,00DF,F096,4DFA DATA 0216.0C6E.0001,000E,6610,23EE,000A,00DF DATA F0D0,33FC,0001,00DF,F0D4,4DFA,01 E0.0C6E DATA 0001,000E,6610,23EE,000A,00DF,F0C0,33FC DATA 0001,00DF,F0C4,4DFA,01 AA,0C6E,0001,000E DATA 6610,23EE,000A,00DF,F0B0,33FC,0001,00DF DATA F0B4,4DFA,0174.0C6E.0001,000E,6610,23EE DATA 000A,00DF,F0A0,33FC,0001,00DF,F0A4,202C DATA 03D8,0680,0000,0010,2940,03D8.B0BC,0000 DATA 0400,661 A,42AC,03D8,52AC,03DC,7000,302C DATA 0428,222C,03DC,B280,6604,42AC.03DC.4A6C DATA 042C,6706,426C,042C,60DA,4E75,2CB0,1000 DATA 5881,7400,142E,0002,0202,00F0,E80A,4A02 DATA 6760,7600,43FA.017A,2802, E582,C8FC,001E DATA 2D71,2000,0004,3073,4000,0008,3073,4002 DATA 0012,3633,4004,4A43,6722,242E,0004,D483 DATA 2D42,0004,2D42,000A,3D73,4006,0008,3D73 DATA 4006,000E.3B6E,0012,0008,6016,242E,0004 DATA D483,2042,000A,3D73,4006,000E.3B6E,0012 DATA 0008,4A56,6722,3D56,0010,33EE,0014.00DF DATA F096.2AAE,0004,3B6E,0008,0004,3B56,0006 DATA 302E.0014,816C.042A.4A56,6704,3D56,0016 DATA 102E,0002,0200,000F,B03C.000B,671 A.B03C DATA 000C,6726,B03C,000D,6728,B03C,000E,6728 DATA B03C,000F,673E.4E75.466C,0420,7000,102E DATA 0003,5300,2940,03DC.4E75,1B6E,0003,0008 DATA 4E75,466C,042C,4E75,7000,102E,0003,0200 DATA 0001 ,E318,0239,00FD,00BF,E001,8139.00BF DATA E001 ,€75,102E,0003,0200,000F6708.42AC DATA 03E0,1940,000F,€75,0000,0000,0000,0000 DATA 0000,0000,0000,0000,0000,0000,0001,0000 DATA0000,0000,0000,0000,0000,0000,0000,0000 DATA 0000,0000,0000,0002,0000,0000,0000,0000 DATA0000,0000,0000,0000,0000,0000,0000,0000 DATA 0004,0000,0000,0000,0000,0000,0000,0000 DATA0000,0000,0000,0000,0000,0008,0000,0000 DATA 0000,0000,0000,0000,0000,0000,0000,0000 DATA0000,0000,0000,0000,0000,0000,0000,0000 DATA 0000,0000,0000,0000,0000,0000,0000,0000 DATA 0000,0000,0000,0000,0000,0000,0000,0000 DATA 0000,0000,0000,0000,0000,0000,0000,0000 DATA 0000,0000,0000,0358,0328,02FA,02D0,02A6 DATA 0280,0250,023A,021 A,01 FC.01 E0.01 05,01 AC DATA 0194,017D.0168,0153,0140,012E.011 D,010D DATA 00FE,00F0,00E2,00D6,00CA,00BE,00B4,00AA DATA 00A0,0097,008F,0087,007F0078,0071,0000 DATA 0000,0000,0000,0000,0000,0000,0000,0000
LISTING 3 : exemple
' Charger REPLAY.INL dans 1TNLINE ci-dessous INLINE playroutine%,1600 st_init%=playroutine% st_end%=playroutine%+4
module$ =”ST-00:Modules Alcatraz”
OPEN "i", 1 ,module$
1%=L0F( 1 )
CLOSE 1
ad%=MALL0C(l%,&H10002)
IF ad%
BLOAD moduleS,ad%
ELSE
ALERT 1,"Pas de CHIP-RAM !",1," Zut I",r& END ENDIF
1 Joue le module en vitesse 4, priorité 0 ~C:st_init%(L:ad%,W:4,W:0)
1 Attend un bouton de la souris REPEAT! On peut évidemment IJNT1L IvlOUSEK ! Faire autre chose
' Stopthezik
- C:st_end%()
~MFREE(ad%,l%)
EDIT
INITIATION
COMPTER PLUS VITE QUE SON OMBRE EN G FA.
Le GFABasic n’est certainement pas un BASIC comme les autres puisqu’il offre une rapidité d’exécution remarquable. Ses développeurs l'ont même doté de commandes spéciales, très proches du langage machine et du langage C, qui permettent d’améliorer sa vitesse de calcul. Ces commandes ne faisant pas partie du BASIC standard, elles passent trop souvent sous silence. Je vous propose de découvrir quelques-unes d’entre-elles qui, je le pense, vous seront utiles.
Dans le programme dont le listing suit cet article, vous pourrez consulter 6 mesures de rapidité effectuées suivant le même modèle. A chaque fois, on augmente une variable d’une unité dans une boucle, laquelle s’interrompt lorsque l’on atteint la limite fixée au préalable (ici 50000). Le timer de l’Amiga est consulté pour chaque boucle et nous donne une indication sur le temps écoulé. Il faut tout de même remarquer que les résultats sont très imprécis et on ne doit les considérer que comme des ordres de grandeur. L’imprécision est dûe à plusieurs facteurs parmi lesquels on peut citer en premier le fait que l’Amiga soit une machine multi-tâches, en second la précision du timer lui-même qui n’est pas idéale sur de courtes périodes de temps et aussi des instructions “transparentes” du GFABasic qui n’apparaissent pas dans le listing mais retardent (légèrement) le déroulement du programme (on peut citer le temps d’initialisation d’une nouvelle variable ou certains tests comme celui de la frappe d'une touche, qui sont négligeables mais expliquent pourquoi des différences très courtes ne doivent pas être prises en compte). Il est néanmoins possible de corriger pour une grande part ce défaut en plaçant les tests entre les instructions Forbid() et Permit(), qui désactiveront le multi-tâches pendant le test, chose très inAMIGAle pour l’ensemble du système.
La première remarque que je ferai concernera l’utilisation des nombres entiers par rapport à celle des nombres à virgule flottante. On a certainement dû vous répéter de nombreuses fois que les nombres entiers étaient calculés plus rapidement que les nombres à virgule flottante. Cela est tout à fait exact mais à condition que les opérations soient des opérations sur les entiers. Dans le cas du GFA-BASIC, un nombre entier sera dans une opération normale converti automatiquement en nombre à virgule flottante, d’où une perte de vitesse non négligeable. Les résultats des boucles 1 (36.76 secondes) et 3 (49.62 secondes) confirment d’ailleurs ce fait. La même opération est effectuée dans les deux boucles à la différence qu’une variable à virgule flottante est utilisée dans la première boucle et une variable entière dans la troisième.
Ce résultat est d'ailleurs paradoxal quand on examine la boucle
2. Par rapport à la boucle numéro 1, une variable à virgule flottante est utilisée dans l'instruction FOR. Cependant le résultat
(42. 9 secondes) montre l’intérêt d’utiliser des variables entières dans les boucles FOR à chaque fois que cela est possible.
La quatrième boucle démontre l’intérêt d’utiliser les opérations entières avec les entiers puisque la vitesse d'exécution de la boucle s’en trouve plus que doublée (17.24 secondes). L’instruction ADD est une de ces opérations sur les entiers. Son avantage est la correspondance de cette instruction avec le langage machine. C’est ce que l’on appelle une instruction de très bas niveau (puisque équivalente à l’assembleur).
Pour continuer dans l'optimisation, la cinquième boucle utilise l’instruction INC qui augmente un nombre entier de une unité. Le résultat est un gain supplémentaire de 6 secondes avec 11.12 secondes. Il faut donc ne jamais oublier cette instruction, surtout quand on est pressé.
L’utilité de la boucle FOR aurait pu être remise en question puisqu’il est possible de faire la même chose, notamment avec une instruction WHILE. Le score obtenu étant le plus mauvais de tous (55.225 secondes), on se rend compte à quel point l’instruction FOR est efficace.
Et pour la fin, on cherche à gagner quelques secondes précieuses en utilisant Forbid() et Permitf). Le gain n’est somme toute pas tellement spectaculaire avec 10.22 secondes. Il est évident que l’emploi de DisableO et Enable() aurait permis d’accélérer encore la boucle mais ces instructions sont tellement efficaces qu’elles arrêtent également le timer, la lecture du temps n'étant plus alors possible.
Pour conclure, je dirai que ces différentes façons de coder mettent en évidence la difficulté d’établir des Benchmarks pour comparer la rapidité du GFA-Basic avec les autres langages. Une boucle de 1 à 200000 s'exécute en langage C en un peu moins de 2 secondes. Suivant la manière dont on aura codé le programme "équivalent” en GFA-Basic on pourra affirmer que l’interpréteur du GFA-Basic est de 5 à 30 fois plus lent que le langage C. Cela signifie également que si vous avez un programme écrit en GFA-Basic qui n’utilisait pas ces instructions très particulières que sont INC, DEC, ADD, SUB, DIV, MUL ainsi que quelques autres (comme BTST ou AND, d’emploi beaucoup plus complexe mais pouvant faire des miracles) et que vous vous donniez la peine de réécrire celui-ci en fonction de ces instructions, il ne serait pas étonnant de voir sa vitesse d’exécution augmenter de façon significative. Les inconvénients étant une légère perte de lisibilité du programme et surtout la création d’un code spécifique au GFA-BASIC, qui ne serait pas compris par un autre dialecte BASIC. Mais optimisation et portabilité n’ont jamais fait très bon ménage ...
lim%=200000
PRINT "Tests de rapidité en GFABasic compte jusqu’à
";lim%;"."
I%=0
i=0
tl%=TTMER PFïINT "Boucle 1" j=0
FOR i%= 1 T01im% j=j+l
NEXTi% t2%=TIMER PRINT "Boucle 2" j=0
FOR i=l TO lim% j=j+l
NEXTi t3%=T!MER PRINT "Boucle 3"
-
1B B Cl B BBB H
mm
j%=0
FOR i%=l TO lim% j%=j%+l
NEXTi% t4%=TIMER PRINT "Boucle 4" j%=0
FOR i%= 1 TO lim% ADD j%,l
NEXT1% t5%=TIMER PRINT "Boucle 5" j%=0
FOR i%= 1 TO lim% INC j%
NEXT i%
t6%=TIMER
PRINT "Boucle 6"
j%=0
i%=l
WHILE i% =lim% INC i% INC j%
WEND
t7%=TIMER
* ********
* ************"
* ***********
'Numéro 1 : ";(t2%-t 1 %) 200; " secondes
'Numéro 2 : ";(t3%-t2%) 200;" secondes
'Numéro 3 : ";(t4%-t3%) 200;" secondes
'Numéro 4 : " ; (t5%-t4%) 200; " secondes
'Numéro 5 : ";(t6%-t5%) 200;" secondes
'Numéro 6 : ";(t7%-t6%) 200;" secondes
'Numéro 7 : ";(t8%-t7%) 200;" secondes' *************************************"
D. LORRE
PRINT"***
PRINT Résultats *";
FOR i%=l TO 12
PRINT"*”;
PAUSE 10
NEXT i%
PRINT PRINT'
PRINT'
PRINT PRINT PRINT PRINT PRINT PRINT PRINT
- ForbidO PRINT "Boucle 7" j%=0
FOR i%=l TO lim% INC j%
NEXTi%
- PermitO
t8%=TIMER
* ***********
APPLICATION
ARP et G FA
Qui ne connait pas aujourd'hui la fameuse arp.library et son non moins célèbre sélecteur de fichiers ? En tous cas cette bibliothèque de fonctions a été utilisée par de nombreux développeurs et dans des langages tels que le C, l’assembleur, le Modula II, l’Amiga BASIC, Arexx et aussi le HiSoft BASIC. A cette liste manquait le GFA-Basic. Cela est certainement dû à la relative nouveauté de ce langage qui a beaucoup de peine à s’imposer sur Amiga. Cependant ce langage possède toutes les commandes nécessaires à l’interfaçage de la bibliothèque ARP.
L’utilisation de l’ARP en GFA-Basic apporte-t-elle grand chose à ce langage. Oui, si l’on considère que celle-ci offre de nombreuses fonctions dont la reconnaissance des caractères génériques, l’évaluation d’une ligne de commande et bien sûr son fameux sélecteur de fichiers. Mais, me direz-vous le GFA-Basic possède son propre sélecteur de fichiers et il est inutile d’appeler celui de l’ARP. Je répondrai que le sélecteur de l’ARP est avantageux à plus d’un titre. Sa première qualité vient de sa renommée et de sa présence dans de nombreux programmes, qui font que ce sélecteur est connu de beaucoup d’utilisateurs d’Amiga. En second lieu, celui-ci est âgé de plusieurs années : il descend du vieux sélecteur de Charlie Fheath visible par exemple dans le programme PerfectSound, qui l’a constamment amélioré depuis. De cette ancienneté découlent naturellement efficacité, rapidité et facilité d’emploi. On peut affirmer sans crainte qu’il s’agit du sélecteur de fichiers le plus efficace jamais créé sur Amiga (celui d’AZ est un concurrent valable à qui j’attribuerai la palme de l’esthétisme). Efficace car le programmeur peut y ajouter ses propres gadgets et redéfinir la fenêtre d’affichage. Mon but n’est pas de critiquer le sélecteur du GFA-Basic, qui j’en suis sûr se bonifiera avec le temps. Le premier sélecteur de Charlie Fheath était loin d’être convivial, mais pour l’époque il était exceptionnel car la plupart des logiciels n’offraient pas encore de
liste de fichiers (chargez un fichier avec NotePad, vous comprendrez). Non, le seul défaut grave de ce sélecteur est un bug faisant que 48 octets ne sont pas rendus au système à la fin de son appel. Ceci a entraîné de nombreuses erreurs "aléatoires” dans plusieurs programmes particulièrement gros. C'est une raison suffisante pour utiliser la arp.library.
La version utilisée ici de l'ARP est la 1.3, numéro 39.1. Il est absolument garanti que le programme ci-dessous ne fonctionnera pas avec une version inférieure. Et j’ose espérer qu’il fonctionnera avec les versions postérieures. Le fichier arp.library doit être contenu dans le répertoire LIBS; et son numéro de version peut être obtenu avec la commande du CLI :
Version arp.library
Au cas où vous n’auriez pas ce fichier sous la main, je vous conseillerai premièrement de faire le tour de vos disquettes, un grand nombre de logiciels utilisant la arp.library (qui est domaine public, je le rappelle), il existe une chance non négligeable pour que vous retrouviez cè fichier dans le répertoire LIBS: de l’une d’entre elles. Sinon, les moyens possibles de se la procurer sont les disquettes Fish, ou d’autres collections du domaine public, et aussi la plupart des serveurs proposant du téléchargement sur Amiga. Vérifiez bien qu'il s’agit de la version 1.3. Pour ce qui est de la documentation, celle-ci existe en plusieurs parties. Comme le projet ARP comprend également des commandes remplaçant celles du CLI, la partie de la documentation qui y fait allusion ne vous intéressera pas. Ce qu’il vous faudra, c'est le fichier arp- pro13.zoo qui contient l’ensemble des informations utiles aux développeurs. Ce fichier (en anglais...) compressé avec Zoo vous renseignera sur l’utilisation de chacune des fonctions de la bibliothèque, vous permettra de retrouver les offsets de ces fonctions (arp_proto.h) et contient un accord de licence obligatoire pour les développeurs voulant diffuser légalement des programmes utilisant l'ARP. Je devais au moins vous prévenir.
;0©
Pour ce qui est de la partie technique, je me suis enfin décidé à documenter correctement mes programmes, ce qui devrait vous satisfaire...
Dominique LORRE
' Ce programme tente d'illustrer l'utilisation de l'arp.library ' avec le GFA-Basic.
' Ouverture de la arp.library et initialisations
initall
' appel du sélecteur de fichiers
résultat%=@filerequest(monfilereq%,"You know what?",WINDOWS, 170,30)
' le résultat est lu, si on a zéro, l'utilisateur a sélectionné Cancel ' ou il s'est produit une erreur lors de l'appel du sélecteur ' par exemple, une position incorrecte des coordonnées haut et gauche.
IF resultat%
' l'utilisateur a choisi un fichier. Son chemin se trouve dans fr_dir%
' et son nom dans fr_file%
file$ =CHAR(fr_file%]
chemin$ =CHAR(fr_dir%}
PRINT "ok"
PRINT "Fichier : ";file$
PRINT "Chemin : ";chemin$
USE
PRINT "Pas de sélection"
ENDIF
PRINT "Un bouton de la souris"
PRINT "Pour quitter le programme..."
RFPFAT
UNTILMOUSEK
freeall
PROCEDURE initall ' Dimensionnement du tableau de registres pour l'instruction RCALL DIM regs%( 15)
' Ouverture d'un écran séparé OPENS1
' Ouverture d'une fenêtre OPENW 5
' Ouverture de la arp.library arpname$ ="arp.library"+CHR$ (0) arpname%=V:arpname$ arpbase%=OpenLibrary(arpname%,0)
IF arpbase%=0
PRINT "Impossible d'ouvrir la arp.library"
END
ENDIF
' Allocation d'une structure FileRequest ' Celle-ci sera libérée par le CloseLibrary
monfilereq%=@arpallocfreq IF montilereq%=0
PRINT "Erreur d'allocation de la structure FileRequest" ~CloseLibrary(arpbase%)
END
ENDIF
RETURN
PROCEDURE freeall ' Fermeture de la arp.library
' Cet appel libère également le (ou les) sélecteurs) de fichiers ~CloseLibrary(arpbase%)
CLOSEW 5 CLOSES 1 RETURN
' Cette procédure permet d'accéder de manière simple aux ' différents éléments de la structure FileRequest PROCEDURE filereqstruct(filereq%)
ABSOLUTE fr_hail%,filereq% ! Titre
ABSOLUTE fr_file%,filereq%+4 ! Nom du fichier
ABSOLUTE fr_dir%,filereq%+8 ! Chemin du fichier
ABSOLUTE fr_window%,filereq%+12 ! Fenêtre
ABSOLUTE fr_funcflagsl,filereq%+16 ! Drapeaux
ABSOLUTE fr_flags2,filereq%+17 ! Autres drapeaux
ABSOLUTE fr_function%,filereq%+18 ! Fonction externe
ABSOLUTE fr_leftedge&,filereq%+22 ! Position gauche
ABSOLUTE fr_topedge&,filereq%+24 ! Position haut
RETURN
' Allocation d'une structure FileRequest ' l'adresse de la nouvelle structure est retournée dans ' le registre D0 (regs%(0)).
FUNCTION arpallocfreq regs%(14)=arpbase%
RCALL arpbase%-&H294,regs%()
RETURN regs%(0)
ENDFUNC
' Appel du FileRequest
' titre$ : chaîne de caractères affichée sur le sélecteur et ' sur l'écran
' fenêtre% : adresse de la fenêtre où le sélecteur sera affiché FUNCTION filerequest(filereq%,titre$ ,fenetre%,gauche&,haut&) ftitre$ =titre$ +CHR$ (0) filereqstruct(filereq%) fr_hail%=V:ftitre$ fr_window%=fenetre% fr_leftedge&=gauche& fr_topedge&=haut& regs%(14)=arpbase% regs%(8)=filereq%
RCALL arpbase%-&H126,regs%()
RETURN regs%(0)
ENDFUNC
D. LORRE
LES FICHIERS INFO
DOS INITIATION
Dans les précédents articles nous avons réalisé une disquette de Boot. Dans la majeure partie des cas vous voudrez y placer le Workbench (ou l’Atelier, comme on dit par chez nous). Il faut, avant toute autre chose, se poser la question de savoir quels sont les fichiers qui seront utilisés par le Workbench. Et bien, comme tout le monde le sait, le Workbench contient des icônes représentant des programmes et des fichiers, ainsi qu'une barre de menus. Ce qui nous intéresse ici sont les icônes. Vous ne le savez peut-être pas encore mais chaque icône est produite par un fichier spécial appelé fichier ,info.
Les fichiers .info sont particuliers dans le sens où ils accompagnent un autre fichier et n’ont pas d’autre utilité que sous ie Workbench. Par exemple, au programme Preferences est attribué un fichier Préférences.info. L’utilité des fichiers.info est avant tout de contenir l'image d’une icône, d’autre part de contenir les positions de l’icône sur l’écran du Workbench et en dernier lieu de contenir un certain nombre de paramètres qui seront transmis à l’initialisation du programme. Toujours dans le cas du programme Preferences, vous connaissez certainement les trois autres icônes qui lui sont associées et sont intitulées Pointer, Printer et Serial. A ces icônes sont rattachés les fichiers Pointer.info, Printer.info et Serial.info. Ces trois icônes servent à appeler Preferences avec un paramètre (PREFS) autorisant l’accès à un tableau différent de Preferences à chaque fois. Vous vous en rendrez mieux compte si vous sélectionnez une de ces icônes et activez l’entrée Info dans le menu Workbench.
Pour que le Workbench puisse utiliser correctement ces icônes, vous aurez besoin des fichiers info.library et icon.library dans le répertoire LIBS:, soit dans notre cas MonBooblibs. Si vous avez décidé de vous passer du Workbench ces fichiers ne seront pas nécessaires. Comme je l’ai précisé le mois dernier les fichiers LoadWB et EndCU seront nécessaires dans le répertoire C:
Voici maintenant la Startup-Sequence minimale de votre disquette Boot pour lancer le Workbench :
System SetMap f LoadWB EndCIi > NIL:
La première commande active le clavier français, la deuxième charge le Workbench et la troisième referme le CLI. Pour la troisième commande on utilise ce que l'on appelle une redirection (> NIL:). Les redirections servent à envoyer les sorties des commandes ailleurs que dans la fenêtre CLI. Cela peut être un fichier (> nom du fichier>) ou l’imprimante (> PRT:), ou encore nulle part ailleurs (> NIL:) comme ci-dessus.
Les manipulations suivantes vont nous obliger à utiliser un éditeur de textes. En effet, tant que la taille des Startup-Sequence que l’on créer est inférieure à cinq lignes on peut encore se passer d'un éditeur de textes, mais pas au-delà. L’éditeur que je recommande le plus vivement est AZ de Jean-Michel Forgeas. Ce n’est pas par chauvinisme mais parce qu'AZ offre ie meilleur compromis entre la puissance et la simplicité. En attendant que Commodore ait décidé de diffuser AZ en standard, nous nous contenterons de Ed. Pour ceux qui ne le connaissent pas, je vais m’efforcer de décrire les principales opérations de Ed.
Le programme Ed doit avant tout se trouver dans le répertoire C:. En effet, c’est dans son milieu naturel que nous serons le mieux à même de tirer partie des capacités de Ed. Pour lancer Ed, on doit obligatoirement spécifier un nom de fichier à sa suite, Si le fichier existe, il sera chargé et sinon il sera créé. Créons ensemble le fichier monfic avec la commande :
ed RAM:monfic
Une nouvelle fenêtre s'ouvre alors devant ia fenêtre Shell et un message orange au bas de l'écran (Creating new file) nous signifie qu’il s'agit d'un nouveau fichier. Un curseur se trouve en haut de l’écran, attendant avidement la frappe d'une touche. Lorsque l’on utilise un éditeur de textes pour la première fois, la tradition veut que l’on saisisse un poème.
Essayez donc les lignes suivantes :
Pour soulever un poid
si lourd, Sisyphe, il faudrait ton courage !
Bien qu'on ait du cœur à l’ouvrage,
L’Art est long et le Temps est court.
Loin des sépultures célèbres,
Mon cœur, comme un tambour voilé,
Vers un cimetière isolé,
Va battant des marches funèbres,
J’espère que vous n'aurez pas rencontré de problèmes pour entrer ces lignes avec Ed. Il suffit d’appuyer sur RETURN à chaque fin de ligne pour se retrouver au début de la suivante.
Nous allons à présent remettre ce début de poème dans une forme qui conviendra mieux à Baudelaire. Tout d’abord le mot poids à la première ligne a été mal orthographié. Pour vous retrouver en première ligne vous pouvez soit remonter manuellement avec la flèche vers le haut, soit appuyer sur Esc. Dans ce cas une étoile va apparaître en bas de l’écran. Vous n’aurez plus qu’à appuyer sur T et valider avec Return pour que ie curseur se positionne sur la première ligne. Comme le mot poids se trouve en fin de ligne vous pouvez vous y rendre directement avec la combinaisonde touches Ctrl et ] (le ] se trouve au dessus du pavé numérique). Une fois arrivé derrière le d vous pouvez enfin ajouter le s manquant.
La deuxième erreur consiste en ce que la première phrase doit contenir les mots “si lourd,” qui sont en début de deuxième phrase. Pour cela faites Esc J qui correspond à la fonction Join. Vous verrez alors la deuxième ligne se placer à la suite de la première. Placez ensuite le curseur sur le S de Sisyphe et appuyez sur la touche Return. La fin de la phrase à partir du mot Sisyphe va se retrouver en deuxième ligne.
Il nous reste à échanger la phrase “Vers un cimetière isolé,” avec celle qui la précède. Pour cela on se place sur la ligne et on l’efface avec Ctrl
B. Puis on se place au début de la ligne "Mon coeur, ...” (la combinaison Ctrl ] permet également de se placer en début de ligne) et on en crée une nouvelle en appuyant sur Return. Il ne nous reste plus alors qu'à remplir la ligne blanche ainsi créée avec la phrase manquante.
Comme sous le Shell, il est possible de supprimer des caractères avec les touches BackSpace ou Del. Ed possède beaucoup d’autres combinaisons de touches mais celles-ci devraient vous permettre de créer et modifier vos Startup-Sequence sans trop de problèmes. Citons néanmoins Esc B pour se retrouver en fin de texte, Ctrl U pour remonter dans le texte et Ctrl D pour descendre d’une vingtaine de lignes.
Mais peut-être ai-je oublié le principal, car je ne vous ai pas dit comment quitter Ed. Si vous désirez abandonner l’éditeur sans enregistrer les modifications, utilisez Esc q, puis répondez Y (pour Oui) à la question vous demandant si vous désirez sortir. Pour sortir en enregistrant les modifications vous pouvez utiliser Esc x. Et pour enregistrer le texte sans quitter Ed on utilisera Esc sa.
Nous pouvons enfin éditer le fichier Startup-Sequence avec Ed :
Ed S:Startup-Sequence
Voici un exemple simple de Startup-Sequence dont vous pourrez vous inspirer pour vos disquettes boot :
SetPatch > NIL:
AddBuffers DFO: 10 Sys:System SetMap f
Sys:System FastMemFirst BindDrivers SetClock opt LOAD FF > NIL: -0
Résident CLI L:Shell-Seg SYSTEM pure add Mount NEWCON:
Path RAM: Sys:System Sys:Utilities S: Sys:System ADD Path Sys:Tools Sys:Prefs ADD LoadWB Endeli > NIL:
Je dois vous prévenir avant tout de faire très attention en modifiant votre Startup-Sequence avec Ed. En général, après avoir quitté Ed, on reboote pour vérifier que la Startup-Sequence est correcte. Malheureusement il faut quelques secondes pour que le fichier soit enregistré. Si vous rebootez immédiatement il est probable que le fichier Startup-Sequence aura disparu de votre disquette, voire même celle-ci pourra avoir été endommagée car l'Amiga ne supporte pas que l’on reboote pendant qu’il est en train d’écrire,
BSEID BBHB
La première commande est quasi-indispensable pour ceux qui possèdent la ROM 1.3. La commande SetPatch corrige les bugs connus de celle-ci et son utilité n’est donc pas discutable (la commande SetPatch doit se trouver dans le répertoire C:).
La commande AddBuffers sert à accélérer les accès aux disquettes en leur attribuant plus de mémoire. Vous déciderez de la mettre ou non suivant votre patience et suivant la mémoire disponible (répertoire C: également). Je ne m’étendrai pas sur la commande SetMap qui a déjà été mentionnée. La commande FastMemFirst doit se trouver dans le répertoireSystem. Sans trop entrer dans les détails, je dirai qu’elle est inutile à ceux qui possèdent uniquement de la mémoire CHIP (Amiga 500 ou 1000 avec 512Ko de RAM ou Amiga 2000B 1Mo avec leSuperFatAgnus) (répertoire System).
La commande BindDrivers est présente dans toutes les Startup- Sequence. Sachez toutefois qu’elle ne sert qu'à charger les fichiers contenus dans le tiroir Expansion et qui sont destinés à piloter des cartes telles que la carte PC ou les cartes disque dur. Dans les autres cas vous pouvez la retirer sans dommage (répertoire C:).
La commande SetClock permet de lire le contenu de l’horloge permanente. Si votre Amiga n'en possède pas (comme les Amiga 500 de base) elle est Inutile (répertoire C:).
FF est un utilitaire permettant d’accélérer l'affichage des fontes sur l’Amiga. A vous de décider... (répertoire C:).
Je ne détaillerai pas non plus la commande Résident. Disons seulement que celle-ci est nécessaire pour que le Shell fonctionne (les fichiers L:Shell-Seg et C:Resident sont nécessaires).
La commande Mount Newcon : est aussi nécessaire pour le Shell. Pour cela les fichiers Lnewcon-Handler et DEVSiMountUst sont nécessaires. Le fichier Mountlist est aussi un fichier texte comme la Startup- Sequence. Dans notre cas vous pouvez le créer avec :
Ed DEVS:MountList
et y inclure les lignes suivantes :
NEWCON:
Handler = Lnewcon-Handler Priority = 5 Stacksize = 1000

La commande Path sert également à l’utilisation du Shell et donne la liste de tous les répertoires où le système ira chercher ses commandes (en plus de C: et du répertoire courant). Vous ne devrez donc y placer que ceux qui existent vraiment sur votre disquette.
Quant aux deux dernières lignes LoadWb et EndCLI, je pense que vous connaissez maintenant leur rôle, Le gros du travail est maintenant achevé. Dans les prochains articles je m’attacherai à éclaircir les quelques détails qui pourront encore vous paraître nébuleux et nous examinerons aussi quelques cas particuliers.
D. LORRE
APPLICATION
LE DISQUE RAD
Le Workbench 1,3 nous a apporté une nouveauté intéressante qui est le disque RAD:, un autre genre de RAM-Disk. L'intérêt du disque RAD: est multiple. Premièrement, il résiste au Reboot, ce qui est utile pour conserver des données quand on n’a pas une disquette sous la main, En second lieu, on lui attribue une taille fixe, l’intérêt est moins évident mais s'explique par le fait qu’on peut lui donner la taille d'une disquette. Avec le KickStart 1.3, il est possible de rebooter sur le RAD:, ce qui est une solution permettant d'accélérer le redémarrage du système. En dernier lieu on peut lui attribuer tous les avantages du RAM-Disk et notamment la rapidité.
Comment utiliser RAD: sans tomber dedans ? La réponse se trouve dans le fichier MountList, bien évidemment. En voici un exemple:
RAD: Device = ramdrive.device
Unit = 0 Flags = 0 Surfaces = 2 BlocksPerTrack = 11 Reserved = 2 Interleave = 0 LowCyl = 0 ; HighCyl = 79 Buffers = 5 BufMemType = 1

Comme nous le montre cette entrée du fichier DEVS.'MountList, le disque RAD: est géré par le fichier ramdrive.device. Si jamais vous l’avez retiré de votre système, le disque RAD: ne sera jamais accessible. Le numéro d’unité (Unit) sera toujours 0 en 1.3 (sauf que ... mais
j’y reviendrai). Flags est un paramètre passé à l’ouverture de la fonction OpenDevice et ici a une valeur nulle. Surfaces est le nombre de surfaces du support utilisé. Or, physiquement, on ne peut pas dire que la RAM: ait deux faces comme une disquette (où irait-on, je vous le demande). On peut d’ailleurs formuler la même remarque en ce qui concerne les paramètres BlocksPerTrack, Reserved, Interleave, LowCyl etHighCyl. Ces valeurs sont strictement les mêmes que celles d’une disquette, étonnant non? En fait, si vous n’avez pas beaucoup de mémoire disponible (ici 880 Ko, soit la taille d’une disquette), cette entrée risque de ne pas passer. Il vous faudra alors réduire la valeur de HighCyl en fonction de votre mémoire disponible (par exemple 19 pour un RAD: de 220 Ko). Buffers est le nombre de tampons utilisés avec ce disque et BufMemType le type de la mémoire utilisée par les tampons.
Ce premier exemple de disque RAD: offre l’énorme avantage d’être compatible avec le lecteur DF0:. Cela signifie que vous pourrez effectuer une commande du type :
DiskCopy FROM DF0: TO RAD:
sans l’ombre d’un problème. Il est connu que l’instruction DiskCopy est de loin la plus rapide pour la copie de fichiers, et cet ordre vous fera gagner beaucoup plus de temps qu’une instruction plus classique telle que :
Copy FROM DF0: TO RAM: ALL
Evidemment, il vous faudra dans ce cas beaucoup de mémoire disponible. Il est encore utile d’avoir un disque RAD : de la taille d’une disquette quand on utilise DiskSalv :
DiskSalv FROM DF0: TO RAD:
Ainsi si une de vos disquettes est endommagée, vous pourrez essayer de la récupérer sur RAD:. Cette manière d’utiliser RAD: est une raison valable de préférer de la mémoire supplémentaire à un second lecteur de disquette (1 Mo de RAM est très juste pour ce genre d'opérations).
JyjdBEÜD HBBB
Parlons maintenant du Fast File System. Si vous avez choisi de l’utiliser sur RAD: (en perdant alors la compatibilité avec les disquettes qui utilisent l'ancien système, appelé Old FileSystem), il vous faudra une entrée MountList de ce type :
RAD: Device = ramdrive.device
Unit = 0 Flags = 0 Surfaces = 2 BlocksPerTrack = 11 Reserved = 2 Interleave = 0 LowCyl = 0 ; HighCyl = 79 Buffers = 5 BufMemType = 1 FileSystem = LuFastFileSystem GlobVec = -1 DosType = 0X444F5301

Je dirai tout de suite que l'utilisation de RAD: en FastFileSystem n'a d’intérêt pratique que pour les développeurs voulant tester rapidement leurs programmes. Il reste que vous pouvez choisir cette option pour gagner quelques précieuses millisecondes ou encore pour pouvoir utiliser les protections R,W et X qui ne sont disponibles que sous FFS.
Beaucoup plus intéressante est la particularité qu’à RAD: de résister au Reboot. Sous KickStart 1.2, il suffit de faire MountRAD: après le Reset de la machine et théoriquement les données sont réutilisables immédiatement. Parfois DiskDoctor est utile pour remettre les choses en place. Sous KickStart 1.3 on peut même redémarrer avec RAD:. Dans ce cas, il faut avoir utilisé l'option r de la commande SetPatch dans la Startup- Sequence:
SetPatch > NIL: r
Cette option, très peu documentée, corrige un bug du système qui empêchait RAD: de résister au Reset. D’ailleurs, le facétieux développeur du ramdrive.device a donné le nom RAMB0 au disque RAD, lorsqu’il revient à la vie.
Et maintenant la question qui a tourmenté plus d’un utilisateur d'Amiga : Comment avoir plusieurs RAD: ? Pas si facile, évidemment. La première solution logique qui vient à l'esprit est le changement du numéro d’unité (le paramètre Unit de la MountList). Avec un numéro 0, on aurait le premier disque, 1 pour le second, 2 pour ie troisième, etc. Cette solution paraît logique car c’est ainsi que l’on procède pour les différents lecteurs de disquettes ou les disques durs. Malheureusement le
facétieux développeur du ramdrive.device n’avait (l’usage de l’imparfait n’est pas innocent) pas envisagé cette possibilité et ie ramdrive.device ne connaît que l’unité 0. La méthode la plus élégante étant impraticable, une autre possibilité moins satisfaisante mais néanmoins correcte était de partitionner le disque RAD: comme on le fait pour un disque dur. En théorie, on aurait pu le faire de la manière suivante :
RAD: Device = ramdrive.device
Unit = 0 Flags = 0 Surfaces = 2 BlocksPerTrack = 11 Reserved = 2 Interleave = 0
LowCyl = 0 ; FhighCyl = 9
Buffers = 5
BufMemType = 1

RAD1 : Device = ramdrive.device
Unit = 0 Flags = 0 Surfaces = 2 BlocksPerTrack = 11 Reserved = 2 Interleave = 0
LowCyl = 10 ; HighCyl = 79
Buffers = 5 BufMemType = 1

Malheureusement cette solution n’est qu’un nouveau moyen de bloquer le système. La commande Format refuse d’initialiser un disque RAD: si LowCyl ne vaut pas 0. La solution existe-t-elle ? Et bien oui, et je le prouve ! Tout d’abord je citerai mes sources en disant que la ruse provient d'un message de Carolyn Scheppnerdu CATS (Commodore Amiga Technical Support), envoyé sur UseNet et placé sur SgtFlam par François Rouaix. Comme je n’ai plus le texte original sous les yeux, je me contenterai de vous résumer la chose. Pour obtenir un second RAD:, il faut effectuer tout d’abord une copie du fichier ramdrive.device qui se trouve dans DEVS:. Cette copie devra se trouver également dans le répertoire DEVS: et portera par exemple le nom ramdrivl .device. Il faut ensuite se procurer un éditeur binaire (comme NewZap, éditeur officiel pour les fichiers de personnages des jeux d’aventures), repérer la chaîne de caractères ramdrive.device et la remplacer par ramdrivl .device. D’où l’importance que le nouveau nom soit de la même taille que l'ancien. Voici un exemple de MountList qui déclare deux disques RAD:
RAD: Device = ramdrive.device
Unit = 0 Flags = 0 Surfaces = 2 BlocksPerTrack = 11 Reserved = 2 Interleave = 0
LowCyl = 0 ; HighCyl = 9
Buffers = 5 BufMemType = 1

RAD1 : Device = ramdrivl .device
Unit = 0 Flags = 0 Surfaces = 2
BlocksPerTrack = 11 Reserved = 2 Interleave = 0
LowCyl = 0 ;HighCyl = 79
Buffers = 5 BufMemType = 1

La méthode est réapplicable à l’infini, à condition de nommer chaque fichier différemment. Pour finir, voici les deux instructions qui vous permettront de supprimer toute trace du disque RAD: :
RemRad
Assign DRIVE RAD: REMOVE
PS : Une des particularités du Workbench 2.0 sera de supporter les multiples disques RAD:. Le développeur facétieux a donc satisfait la demande générale.
D. LORRE
T
e trouver également dans le répertoire DEVS: et portera par exemple le nom ramdrivl .device. Il faut ensuite se procurer un éditeur binaire (comme NewZap, éditeur officiel pour les fichiers de personnages des jeux d’aventures), repérer la chaîne de caractères ramdrive.device et la remplacer par ramdrivl .device. D’où l’importance que le nouveau nom soit de la même taille que l'ancien. Voici un exemple de MountList qui déclare deux disques RAD: RAD: Device = ramdrive.device
Unit = 0 Flags = 0 Surfaces = 2 BlocksPerTrack = 11 Reserved = 2 Interleave = 0
LowCyl = 0 ; HighCyl = 9
Buffers = 5 BufMemType = 1

RAD1 : Device = ramdrivl .device
Unit = 0 Flags = 0 Surfaces = 2
BlocksPerTrack = 11 Reserved = 2 Interleave = 0
LowCyl = 0 ;HighCyl = 79
Buffers = 5 BufMemType = 1

La méthode est réapplicable à l’infini, à condition de nommer chaque fichier différemment. Pour finir, voici les deux instructions qui vous permettront de supprimer toute trace du disque RAD: :
RemRad
Assign DRIVE RAD: REMOVE
PS : Une des particularités du Workbench 2.0 sera de supporter les multiples disques RAD:. Le développeur facétieux a donc satisfait la demande générale.
D. LORRE
T

Click image to download PDF

AMIGA NEWS TECH numero 13 (06-1990)

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


Thanks for you help to extend Amigaland.com !
frdanlenfideelhuitjanoplptroruessvtr

Connexion

Pub+

68.1% 
7.4% 
2.3% 
2.1% 
1.6% 
1.5% 
1.3% 
1.1% 
1% 
0.6% 

Today: 11
Yesterday: 100
This Week: 589
Last Week: 1045
This Month: 2016
Last Month: 4209
Total: 92357

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