Suivez-nous sur X

|
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z,
ALL
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z
|
|
A propos d'Obligement
|
|
David Brunet
|
|
|
|
Programmation : Assembleur - XPK et ARexx : concepts et préliminaires
(Article écrit par Frédéric Delacroix et extrait d'Amiga News - novembre 1996)
|
|
Ce mois-ci et les suivants, nous nous lançons dans un grand projet d'interfaçage entre l'ensemble de compression de données
bien connu, XPK, et le langage de programmation natif de l'Amiga, ARexx. Cela va nous mener à l'écriture de la bibliothèque
rexxxpk.library.
Les hôtes ARexx
On ne présente plus ce langage et cet article suppose que vous soyez un peu familier avec la philosophie, sinon le langage,
ARexx. Nous nous attacherons donc à comprendre comment ARexx communique avec ses hôtes et, plus précisément, son comportement
avec les bibliothèques de fonctions (si le cas des autres hôtes et des commandes vous intéresse, faites-le moi savoir).
Lorsque l'interpréteur d'ARexx rencontre dans un script un nom de fonction qu'il ne comprend pas, il cherche dans sa liste
de bibliothèques. Celle-ci regroupe une liste d'hôtes de fonctions déclarés en tant que programmes séparés ou en tant que
bibliothèques. L'ordre de recherche est déterminé par les priorités relatives des différents hôtes, de 100 à -100. Le processus
principal REXX est lui-même un hôte de fonctions de priorité -60. On trouve beaucoup, dans le domaine public, de bibliothèques
de fonctions pour ARexx comme rexxarplib.library ou rexxreqtools.library.
Comme je l'ai dit plus haut, je me limiterai ici au cas des bibliothèques de fonctions. Une bibliothèque doit d'abord être
ajoutée à la liste maintenue par ARexx avant de pouvoir être utilisée. Ceci peut se faire par le programme "RXLIB", la
commande "SHOWLIB" au sein d'un programme ARexx, ou l'envoi d'un message de type "RXADDLIB" à "REXX". La seconde méthode
est la plus pratique, mais les trois sont possibles.
Les caractéristiques d'une bibliothèque de fonctions sont les suivantes : un nom de bibliothèque (tel qu'on en a l'habitude,
se terminant par ".library"), un numéro de version, une priorité (définie au moment de l'ajout à la liste des hôtes) et une
fonction d'interrogation. Cette fonction d'interrogation est destinée à être appelée par la tâche de l'interpréteur quand il
doit résoudre un nom de fonction. Elle est d'abord appelée pour demander à la bibliothèque si elle connaît le nom de fonction
incriminé. Dans l'affirmative, elle doit alors exécuter la routine demandée, en tenant compte des informations de contexte
fournies par ARexx.
Structure d'une bibliothèque ARexx
La bibliothèque écrite doit respecter la même structure qu'une bibliothèque Exec normale et résider au sein d'un fichier du
répertoire Libs:. Cela a déjà été décrit dans un article d'Amiga News il y a quelque temps mais je vais résumer pour les absents.
Après deux lignes de code pour empêcher l'exécution depuis un CLI, on doit trouver, une structure Resident (Cf. exec/resident.i)
correctement initialisée, destinée à indiquer au module ramlib qu'il a bien chargé une bibliothèque valide. En supposant le
flag RTF_AUTOINIT à 1 dans rt_Flags, on devra placer dans rt_Init un pointeur sur une table de quatre mots longs.
Le premier contient la taille de la zone mémoire à réserver pour la structure de base de la bibliothèque, le second, un pointeur
sur une table de pointeurs de fonctions terminée par -1. Le troisième est un pointeur sur une table utilisée par la fonction
InitStruct() d'exec.library pour initialiser la structure et le dernier pointe sur une routine à exécuter afin de terminer
l'initialisation.
Les trois premières fonctions sont réservées respectivement à l'ouverture, la fermeture et la purge de la bibliothèque. La
quatrième est réservée à une extension future et doit se contenter de mettre D0 à 0. Il ne nous reste donc que la fonction
d'interrogation ARexx à détailler. Je dois pour cela commencer un petit aparté sur les différentes structures utilisées.
Les structures RexxMsg et RexxArg
Commençons par la structure RexxArg. Il s'agit d'un compromis entre la facilité, pour les chaînes de caractères à la mode BCPL,
de connaître leur longueur, et la facilité de manipuler les chaînes C, allié à des facilités d'allocation et de conversion.
struct RexxArg {
LONG ra_Size;
UWORD ra_Length;
UBYTE ra_Flags;
UBYTE ra_Hash;
BYTE ra_Buff[8];
};
|
Le champ ra_Size désigne la taille de la zone mémoire allouée pour cette chaîne de caractères, il sert surtout à la copie et la
destruction de la chaîne. Le champ ra_Length contient sa longueur, le champ ra_Flags contient différents attributs indiquant par
exemple si la chaîne de caractères représente un nombre ou si elle fait partie du code source d'un programme. Je ne m'y
attarderai pas plus longtemps, pas plus que sur ra_Hash. Le champ ra_Buff, enfin, contient la chaîne de caractères elle-même,
toujours terminée par un octet nul.
Il se trouve qu'il est toujours plus commode de traiter les chaînes de caractères avec un pointeur sur des caractères plutôt
que sur une structure. C'est pourquoi on fait toujours référence aux structures RexxArg en tant que "ArgStrings". Il s'agit
d'un pointeur sur le champ ra_Buf d'une structure RexxArg, de sorte que les autres champs de la structure soient accessibles
aux décalages négatifs à partir de l'ArgString. Nous en savons maintenant assez pour passer à l'autre structure importante, la
structure RexxMsg, tout en notant que la rexxsyslib.library contient moult fonctions rendant enfantine la gestion des
ArgStrings. Il en est de même de la structure RexxMsg :
struct RexxMsg {
struct Message rm_Node;
APTR rm_TaskBlock;
APTR rm_LibBase;
LONG rm_Action;
LONG rm_Result1;
LONG rm_Result2;
STRPTR rm_Args[16];
struct MsgPort *rm_PassPort;
STRPTR rm_CommAddr;
STRPTR rm_FileExt;
LONG rm_Stdin;
LONG rm_Stdout;
LONG rm_avail;
};
|
Même s'il commence par une structure Message, ce RexxMsg n'est pas toujours utilisé comme tel, et c'est le cas qui nous intéresse
pour les bibliothèques de fonctions (ne jamais utiliser GetMsg() ou Remove() dans ce cas : le message n'est attaché à aucune
liste). Les arguments rm_Args sont la seule chose qui nous importe réellement dans ce message, les autres servant surtout
quand ARexx communique avec des applications hôtes en mode commande ou fonction, et lorsque des applications lancent des
programmes ARexx.
Sur ce, je vous laisse avec le début du code de la bibliothèque, concernant la gestion de sa "partie Exec". Le mois prochain,
nous attaquerons tout ce qui concerne la fonction d'interrogation.
opt AMIGA
opt NOSYMTAB,NODEBUG
OUTPUT LIBS:rexxxpk.library
REVISION MACRO
dc.b "1.00"
ENDM
REVDATE MACRO
dc.b "01.10.96"
ENDM
VERNUM EQU 01
REVNUM EQU 00
include HISOFT_DEVPAC:Macro/Header.i ; nécessite
include HISOFT_DEVPAC:Macro/defines.i ; HiSoft Devpac 3.50
include HISOFT_DEVPAC:Macro/exec.i
IMPORT exec,initializers
IMPORT exec,resident
IMPORT exec,alerts
IMPORT exec,execbase
IMPORT exec,exec_lib
IMPORT rexx,storage
IMPORT rexx,rxslib
IMPORT utility,utility
IMPORT utility,utility_lib
; déclaration de la structure de base de la bibliothèque
STRUCTURE RexxXPKBase
STRUCT RexxXPKLib,Library
BPTR rxpk_SegList
APTR rxpk_ExecBase
APTR rxpk_XPKBase
APTR rxpk_UtilityBase
APTR rxpk_RexxSysLibBase
ENDSTRUCT RexxXPKBase
ST moveq #-1,d0
rts
RomTag Uword RTC_MATCHWORD
Aptr RomTag
Aptr EndLib
Flag.b RTF_AUTOINIT ; initialisation par MakeLibrary()
Ubyte VERNUM
Ubyte NT_LIBRARY
Byte 0
Aptr RexxXPK.Name
Aptr Library.ID
Aptr Library.Init
dc.b 0,'$VER: '
Library.ID
dc.b 'rexxxpk.library '
REVISION
dc.b ' ('
REVDATE
dc.b ')',10,13,0
even
Library.Init
Ulong SIZEOF_RexxXPKBase ; zone à réserver
Aptr Functions.Table ; table des fonctions
Aptr Data.Table ; table d'initialisation
Aptr Lib.InitRoutine ; routine d'initialisation
Functions.Table
Fptr Lib.Open ; ouverture
Fptr Lib.Close ; fermeture
Fptr Lib.Expunge ; purge
Fptr NullFunc ; réservée
Fptr ARexxQuery ; interrogation par ARexx
Ulong -1 ; fin de la table
Data.Table
INITBYTE LN_TYPE,NT_LIBRARY
INITLONG LN_NAME,RexxXPK.Name
INITLONG LIB_IDSTRING,Library.ID
INITBYTE LIB_FLAGS,LIBF_SUMUSED!LIBF_CHANGED
INITWORD LIB_VERSION,VERNUM
INITWORD LIB_REVISION,REVNUM
EndLib dc.l 0
; cette routine est appelée lors de l'initialisation de la bibliothèque
; on y ouvre les bibliothèques nécessaires. Le paramètre SegList est
; à stocker.
Lib.InitRoutine ; (D0)Success=Lib.InitRoutine(Base,SegList)(D0,A0)
PUSH d7/a2/a5-a6,Lib.InitRoutine
move.l d0,a5
move.l a6,rxpk_ExecBase(a5)
move.l a0,rxpk_SegList(a5)
OPENLIB XpkMaster,2 ; ouverture de l'xpkmaster.library
move.l d0,rxpk_XPKBase(a5)
bne.s .XPKOpened
move.l #AN_Unknown!AG_OpenLib!AO_Unknown,d7
CALL Alert
bra.s .NoXPK
.XPKOpened
OPENLIB Utility,37 ; ouverture de l'utility.library
move.l d0,rxpk_UtilityBase(a5)
bne.s .UtilityOpened
move.l #AN_Unknown!AG_OpenLib!AO_UtilityLib,d7
CALL Alert
bra.s .NoUtility
.UtilityOpened
OPENLIB RexxSysLib,36 ; ouverture de la rexxsyslib.library
move.l d0,rxpk_RexxSysLibBase(a5) ; peu l'échec
move.l a5,d0
.Back POP Lib.InitRoutine
rts
.NoUtility
move.l rxpk_UtilityBase(a5),a1
CALL CloseLibrary
.NoXPK move.l a5,a1 ; libération des ressources en cas
moveq #0,d0 ; d'erreur d'initialisation de la
move.w LIB_NEGSIZE(a5),d0 ; bibliothèque
sub.l d0,a1
add.w LIB_POSSIZE(a5),d0
CALL FreeMem
moveq #0,d0
bra.s .Back
; fonction d'ouverture de la bibliothèque: incrémentation du compteur
; de clients et effacement du bit de demande de purge. Si la
; rexxsyslib.library n'est pas encore ouverte, on l'ouvre.
Lib.Open ; (D0)Base=Lib.Open(Base,Version)(A6,D0)
addq.w #1,LIB_OPENCNT(a6)
bclr #LIBB_DELEXP,LIB_FLAGS(a6)
tst.l rxpk_RexxSysLibBase(a6)
bne.s .OK
move.l a6,-(sp)
move.l rxpk_ExecBase(a6),a6
OPENLIB RexxSysLib,36
move.l (sp)+,a6
move.l d0,rxpk_RexxSysLibBase(a6)
bne.s .OK
moveq #0,d0
subq.w #1,LIB_OPENCNT(a6)
rts
.Ret POP Lib.Expunge
rts
; fonction de fermeture de la bibliothèque: décrémentation du compteur
; de clients et purge de la bibliothèque si nécessaire et possible. Dans
; ce cas, la fonction de purge renvoie le paramètre SegList. Sinon, on
; doit retourner 0.
Lib.Close ; (D0)SegList=Lib.Close(Base)(A6)
subq.w #1,LIB_OPENCNT(a6)
bne.s NullFunc
btst #LIBB_DELEXP,LIB_FLAGS(a6)
bne.s Lib.Expunge
NullFunc
moveq #0,d0
rts
; fonction de purge de la bibliothèque: appelée par RemLibrary(), par
; exemple lorsque le système manque de mémoire. Si le compteur de clients
; est nul, toutes les ressources allouées par la fonction d'initialisation
; sont libérées et le paramètre SegList est retourné. Sinon, le bit de
; demande de purge est mis à 1 et 0 est retourné.
Lib.Expunge ; (D0)SegList=Lib.Expunge(Base)(A6)
PUSH d2/a5-a6,Lib.Expunge
move.l a6,a5
tst.w LIB_OPENCNT(a5) ; encore du monde?
beq.s .DoTheExpunge
bset #LIBB_DELEXP,LIB_FLAGS(a5)
moveq #0,d0
bra.s .Ret
.DoTheExpunge
move.l a5,a1 ; retire la bibliothèque de la liste
CALL Remove,rxpk_ExecBase(a5) ; d'Exec
move.l rxpk_RexxSysLibBase(a5),d0
beq.s .NoRexxSysLib
move.l d0,a1
CALL CloseLibrary
.NoRexxSysLib
move.l rxpk_UtilityBase(a5),a1
CALL CloseLibrary
move.l rxpk_XPKBase(a5),a1
CALL CloseLibrary
move.l rxpk_SegList(a5),d2
moveq #0,d0
move.l a5,a1 ; libère la mémoire occupée par la structure
move.w LIB_NEGSIZE(a5),d0 ; de base de la bibliothèque
sub.l d0,a1
add.w LIB_POSSIZE(a5),d0
CALL FreeMem
move.l d2,d0 ; retourne le paramètre SegList
.Ret movem.l (sp)+,d2/a5-a6
rts
|
|