Suivez-nous sur Mastodon

|
|
|
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 - Création d'une bibliothèque
(Article écrit par Frédéric Delacroix et extrait de Devpac 3.50 - janvier 1996)
|
|
Généralités
Lorsqu'un programme ouvre une bibliothèque, le système (plus précisément Exec) commence par chercher
dans la liste des bibliothèques en RAM:, dont la tête (une structure ListHeader) se situe dans la bibliothèque
ExecBase. Si aucune ne convient, soit parce que ce n'est pas le même nom, soit parce que la version n'est
pas la bonne, soit parce que l'initialisation a échoué pour une raison ou pour une autre, la fonction
OpenLibrary() retourne 0. Il est possible pour les applications d'ajouter des bibliothèques au système,
notamment par le biais des fonctions MakeLibrary() et AddLibrary() mais cela nécessite bien évidemment de
lancer un programme.
Il existe une autre méthode mais elle n'est valable qu'une fois que le système est complètement lancé et
plus précisément après que le module RAMLIB ait modifié les fonctions d'Exec pour lui permettre d'accéder,
grâce aux routines du DOS, aux fichiers dans les répertoires LIBS: et DEVS:. Il s'agit précisément d'y
ajouter des nouveaux fichiers, afin qu'Exec, après avoir épuisé la liste des bibliothèques en RAM:, puisse
charger automatiquement, grâce à la fonction LoadSeg() du DOS, un fichier bibliothèque depuis le disque.
Ces fichiers suivent donc une syntaxe très précise, que nous allons maintenant vous décrire.
Principe
Structure d'un fichier "Library"
Tout ce que nous allons raconter maintenant est valable aussi bien, moyennant quelques adaptations comme un
NT_LIBRARY à changer en NT_DEVICE, pour les bibliothèques que pour les périphériques logiques ("devices"),
que nous étudierons dans un autre chapitre.
La première chose que le module RAMLIB fera après avoir chargé un fichier sera de chercher un module
résident dans ce fichier. Ce module résident est identique à ceux utilisés par le système lors d'une
réinitialisation ("reset") mais dans un contexte différent bien sûr. Il s'agit donc d'une structure, connue
sous le nom original de "Resident" :
struct Resident {
UWORD rt_MatchWord;
struct Resident *rt_MatchTag;
APTR rt_EndSkip;
UBYTE rt_Flags;
UBYTE rt_Version;
UBYTE rt_Type;
BYTE rt_Pri;
char *rt_Name;
char *rt_IdString;
APTR rt_Init;
};
|
Le champ rt_MatchWord doit contenir le mot $4AFC (correspondant à l'instruction ILLEGAL), le champ rt_MatchTag
doit contenir un pointeur sur la structure Resident elle-même. Ce sont ces deux champs qui sont
examinés pour la recherche d'une structure Resident. Le champ rt_EndSkip, bien qu'il ne soit pas réellement
utilisé dans notre cas, doit pointer sur la fin du premier bloc de données ("hunk") du fichier. Le champ rt_Flags
contient un masque de quelques drapeaux ("flags"), dont le seul qui ait réellement une signification ici est
RTF_AUTOINIT. Nous y reviendrons. rt_Version contient bien sûr le numéro de version du module, rt_Type doit être mis
à NT_LIBRARY dans notre cas. rt_Pri n'a pas de signification ici. Le champ rt_Name contient un pointeur sur le
nom de la bibliothèque, très important puisqu'il sera comparé à celui qui avait été demandé lors d'OpenLibrary()
et rt_IdString contient une chaîne de description au format :
<nom> Version.Révision (Date)
|
Nous faisons ici un petit détour sur une contrainte souvent méconnue : les noms de bibliothèques doivent
toujours être en minuscules (exemple : intuition.library au lieu de Intuition.library). En effet, le DOS ne
fait pas la différence pour les noms de fichiers mais Exec, lorsqu'il scrute la liste des bibliothèques, si
(cette règle est valable aussi pour les périphériques logiques, les polices...).
Enfin, le champ le plus important, sans doute, est rt_Init. La valeur qu'il contient est un pointeur, qui
sera interprété comme l'adresse d'une fonction à appeler si rt_Flags est à 0 ou comme l'adresse d'une
table d'initialisation si le drapeau RTF_AUTOINIT est à 1. Dans le cas où ce drapeau n'est pas mis, la fonction
d'initialisation doit se charger de tout : allocation et initialisation d'une structure bibliothèque,
construction d'une table de saut ; c'est pourquoi nous laisserons ce cas de côté. Le format de la table
d'initialisation est donc le suivant (vous remarquerez qu'il s'agit simplement de l'énumération des paramètres
pris par la fonction MakeLibrary(), qui sera appelée automatiquement) :
- Un mot long contenant la taille de la partie positive de la structure de bibliothèque à réserver.
Cette taille n'inclut pas la zone de la table de saut située aux décalages négatifs, celle-ci sera calculée
automatiquement. Il va de soi que cette taille doit au moins être égale à la taille de la structure Library
mais on peut bien sûr l'étendre pour y ajouter des données personnelles.
- Un pointeur sur une table de pointeurs sur des fonctions. C'est cette table qui sera utilisée pour
construire la table de saut (grâce à une série de JMP). Petite particularité : il est possible de spécifier
l'utilisation de pointeurs relatifs, sur 16 bits. Pour cela, le premier mot (de 16 bits donc) doit être -1 et
les mots suivants les déplacements à effectuer pour pointer sur les fonctions par rapport à l'adresse de la
table. On remarque que c'est là une belle économie car, outre le fait qu'un pointeur ne prend qu'un mot au
lieu d'un mot long, cela dispense aussi de références, dans le fichier exécutable, à des adresses absolues donc
le DOS n'a pas besoin de stocker d'informations de relogement (dans les fameux blocs de données "Reloc32"),
d'où une économie supplémentaire d'un mot long par fonction. Il y a toutefois deux inconvénients : avec des
pointeurs relatifs, il est impossible de "traverser" les sections car on ne connaît l'adresse de chaque section
qu'au chargement et non à l'assemblage, d'où impossible de prédire un décalage fixe. Le second inconvénient
est qu'il est impossible de charger des bibliothèques dans l'espace mémoire situé dans les 64 derniers kilooctets
(là où les adresses commencent par $FFFF), puisque dans ce cas les pointeurs absolus seraient interprétés comme
des déplacements relatifs. C'est là un point très mineur (qui possède actuellement quatre gigaoctets de mémoire ?)...
- Une table d'initialisation, qui sera utilisée pour initialiser la structure nouvellement créée grâce à la
fonction InitStruct(). Bien que cette fonction soit très compacte et très performante, on préférera le plus souvent
utiliser les macros du fichier exec/initializers.i pour des questions de lisibilité. Cette table est utilisée
pour initialiser les champs ln_type, ln_Name, lib_IdString, lib_Flags, lib_Version et lib_Revision, plus éventuellement
d'autres champs personnels. Le champ lib_Flags aura obligatoirement le drapeau LIBF_CHANGED, probablement avec
LIBF_SUMUSED, pour que la fonction SumLibrary() corrige la somme de contrôle sans déclencher d'alerte.
- Un pointeur sur une routine qui sera appelée après que la structure ait été créée et initialisée, ainsi que
la table de saut. Cette fonction aura quelques paramètres importants dans ses registres :
- A0 : pointeur sur la SegList de la bibliothèque. Cette valeur est à conserver précieusement car elle
servira à libérer la mémoire en cas de purge.
- D0 : base de la bibliothèque nouvellement créée.
Le rôle de cette fonction est de se charger de toutes les initialisations supplémentaires requises :
ouverture d'autres bibliothèques, allocations de mémoire, initialisation de listes et de sémaphores...
Attention, il s'agit des données communes à toutes les tâches. Si des ressources doivent être allouées
individuellement pour chaque tâche, ce sera fait dans la fonction d'ouverture ! En retour, la fonction doit
fournir en D0 l'adresse de base de la bibliothèque si tout a bien marché ou 0 si une initialisation a échoué.
Dans ce cas, c'est à la fonction d'initialisation de libérer toutes les ressources qu'elle a elle-même allouées,
ainsi que celles qui ont été allouées automatiquement par Exec : il s'agit de libérer la zone mémoire pointée
par (Base - lib_NegSize) et de longueur (lib_NegSize + lib_PosSize).
Les fonctions
Comme vous le savez sans doute, les quatre premières fonctions aux décalages négatifs des bibliothèques
sont réservées. Elles se nomment respectivement LIB_OPEN(), LIB_CLOSE(), LIB_EXPUNGE() et LIB_RESERVED().
LIB_OPEN() est appelée lorsque la bibliothèque est ouverte, elle ne doit pas être confondue avec la
fonction d'initialisation qui est appelée lorsque la bibliothèque est chargée. Son rôle est d'incrémenter
le compteur de clients, c'est-à-dire le champ lib_OpenCnt et d'effacer le drapeau LIBF_DELEXP (DELayed EXPunge),
pour éviter que la bibliothèque ne soit purgée. La bibliothèque peut aussi allouer des ressources pour la
tâche qui ne sont pas globales comme celles allouées par la fonction d'initialisation. LIB_OPEN() reçoit en
paramètres la base de la bibliothèque en A6 et le numéro de version en D0, qui a déjà été comparé avec le champ
lib_Version. En retour, elle doit fournir la base de la bibliothèque en D0 si tout s'est bien passé ou 0 dans le cas
contraire, auquel cas la fonction OpenLibrary() échouera.
LIB_CLOSE() est le contraire : elle décrémente le compteur de clients et libère les ressources allouées à
une tâche particulière. Elle doit également tester le drapeau LIBF_DELEXP et, s'il est à 1, appeler LIB_EXPUNGE().
Elle doit retourner 0 dans le cas contraire.
LIB_EXPUNGE() est la plus compliquée de ces fonctions : il s'agit de l'opposé de la fonction d'initialisation
qui avait été appelée par MakeLibrary(), son rôle est de libérer la mémoire de la bibliothèque. La première chose
à faire est d'examiner le compteur de clients et de mettre le drapeau LIBF_DELEXP à 1, de façon à ce que la purge soit
appelée par LIB_CLOSE() dès que le compteur de clients est à 0. S'il est à 0, alors il faut libérer toutes les
ressources que la fonction d'initialisation avait allouées, retirer la structure Library de la liste du système,
ce qui se fait par la fonction Remove() d'Exec, libérer la mémoire occupée par la structure Library et la table de saut
suivant la formule citée plus haut et enfin retourner en D0 le pointeur sur la SegList qui avait été passé en
paramètres à l'initialisation.
LIB_RESERVED()r est inutilisée pour l'instant; elle doit se contenter de mettre D0 à 0.
La purge
Lorsque le système manque de mémoire lors d'une allocation, le processus de purge intervient : il s'agit de
libérer toute la mémoire occupée par les bibliothèques, périphériques logiques, polices, inutilisés.
Depuis le Kickstart 3.0, les applications peuvent aussi déclarer auprès d'Exec des "Low-Mem handlers"
permettant des opérations similaires pour de la mémoire allouée pour des applications. En ce qui nous concerne,
c'est le module RAMLIB qui ce charge d'appeler la fonction de purge de toutes les bibliothèques dont le
compteur de clients (lib_OpenCnt) est à 0. C'est pourquoi d'ailleurs certaines bibliothèques comme asl.library V38
gardent ce compteur à 0, pour pouvoir libérer de la mémoire annexe.
Première conclusion
Nous vous laissons méditer ces explications techniques, puis nous attaquerons un exemple pratique. Petite
chose tout de même : n'oubliez pas que le code doit être réentrant, c'est-à-dire exécutable par plusieurs tâches
à la fois, les variables globales non protégées par des sémaphores sont exclues ! De plus, les fonctions
d'initialisation et de purge ne doivent en aucun cas casser l'état Forbid(), y compris par un appel au DOS !
Ceux qui souhaitent des informations complémentaires peuvent se procurer les RKM, les Fish
741 et
742 et, éventuellement, les derniers numéros
de l'ancien magazine Amiga News Tech.
Nous espérons que vous avez bien digéré la théorie qui précède, car comme promis, nous vous donnons
maintenant un exemple de bibliothèque créée par la méthode décrite dans cet article.
Bien qu'assez court et finalement pas d'une grande utilité, cet exemple a le mérite d'illustrer de façon
concrète la marche à suivre.
La bibliothèque
Le rôle de cette bibliothèque, que nous avons baptisée "stringtools.library", est d'offrir des fonctions de
base pour le traitement des chaînes de caractères. Il s'agit pour la plupart de clones de fonctions standards
du C. Pour être honnête, nous devons signaler que nous nous sommes inspirés d'un article de Stéphane "Max" Screibber
paru dans Commodore Revue de décembre 1990.
Nous avons également ajouté une routine de tri par bulles, tout ce qu'il y a de plus classique dans son principe.
Petite particularité toutefois : au lieu d'utiliser une routine de comparaison fixe, cette routine, stBubbleSortHook(),
fait appel à un crochet (voir le fichier utility/hooks.i), ce qui lui permet de changer l'ordre de classement. On
peut même faire du tri sur de tout autres objets que des chaînes de caractères. Il suffit de dire à la routine
comment les comparer grâce à ce crochet. Nous vous renvoie au code source pour plus de détails.
Le fichier d'inclusion est également listé ici ; il est tout à fait standard, excepté que nous l'avons fusionné
avec le fichier définissant les LVO. On y définit la structure de base de la bibliothèque et d'autres
choses utiles.
La bibliothèque utilisant l'utility.library, son utilisation est réservée aux possesseurs d'AmigaOS version 37
(Kickstart 2.04) et plus (c'est-à-dire tout le monde, normalement).
Explications
Programme d'exemple
Afin d'illustrer le fonctionnement de stringtools.library, nous avons également ajouté un programme d'exemple.
Son rôle est de trier les arguments qu'il reçoit sur sa ligne de commande, ainsi :
Bibliothèque A.D.F.I.Édition A.D.F.I. Injector Helloween
|
...donnera, dans l'ordre : A.D.F.I., A.D.F.I.Édition, Helloween et Injector
(attention, pour ce crochet, les majuscules sont importantes).
Considérations de style
Première recommandation : ne faites pas de bibliothèque si ce n'est pas absolument indispensable. Premièrement
parce que c'est du travail supplémentaire pour le programmeur (vous !) qui doit faire attention à la
réentrance (pas de variables globales sans sémaphores !) et, deuxièmement, parce que cela encombre,
souvent inutilement, le tiroir LIBS: de l'utilisateur (mon disque dur en comporte 129 !). Les
bibliothèques sont réservées aux routines relativement délicates, répétitives ou encombrantes utilisées
par plusieurs programmes. Les routines de la stringtools.library seront avantageusement incluses dans
votre programme.
Au cas où votre (grosse) application utilise quelques bibliothèques très spécifiques, placez celles-ci
dans un répertoire spécial (les chemins complets sont des paramètres valides à OpenLibrary()). De cette façon,
l'utilisateur s'y perdra moins...
Maintenez toujours une compatibilité descendante totale. Si vous ajoutez des fonctions à votre bibliothèque,
vous devez incrémenter le numéro de version (pas seulement de révision) de façon à ce qu'une application
puisse être sûre que ces nouvelles fonctions existent en fournissant le nouveau numéro de version à l'ouverture.
Si vous diffusez une bibliothèque dans le domaine public, assurez-vous que vous fournissez bien les fichiers
d'inclusion C et assembleur, les autodocs (au format standard, pour qu'ils puissent être convertis par
AD2AG), le fichier FD, des scripts d'installation, des exemples, des informations de révision. Comme référence,
nous ne manquerons pas de vous citer la remarquable reqtools.library de Nico François...
Insérer dans l'ordre
- Le code source de stringtools.i.
- Library.s.
- Bibliotheque.s.
Code source de stringtools.i.
; libraries/stringtools.i
; Fichier d'inclusion pour stringtools.library
; version 1.0
IFND LIBRARIES_STRINGTOOLS_I
LIBRARIES_STRINGTOOLS_I SET 1
ST_VERNUM EQU 1
ST_REVNUM EQU 0
IFND EXEC_TYPES_I
include exec/types.i
ENDC
IFND EXEC_LIBRARIES_I
include exec/libraries.i
ENDC
STRUCTURE StringToolsBase,LIB_SIZE
ULONG stb_SegList
APTR stb_ExecBase
APTR stb_UtilityBase
LABEL stb_SIZEOF
STRINGTOOLSNAME MACRO
dc.b "stringtools.library",0
ENDM
; déplacements relatifs des fonctions
_LVOstStrLen EQU -30
_LVOstStrCmp EQU -36
_LVOstStrCpy EQU -42
_LVOstStrnCpy EQU -48
_LVOstStrCat EQU -54
_LVOstStrnCat EQU -60
_LVOstStrToUpper EQU -66
_LVOstStrToLower EQU -72
_LVOstBubbleSort EQU -78
_LVOstBubbleSortHook EQU -84
ENDC
|
Code source de Library.s.
opt AMIGA
OUTPUT LIBS:stringtools.library
include exec/initializers.i
include exec/resident.i
include exec/alerts.i
include exec/execbase.i
include exec/exec_lib.i
include utility/utility_lib.i
include stringtools.i
; au cas où un clown essaierait d'exécuter la bibliothèque
ST moveq #-1,d0
rts
RomTag dc.w RTC_MATCHWORD ; voir le texte de ce codage
dc.l RomTag,EndLib
dc.b RTF_AUTOINIT,ST_VERNUM
dc.b NT_LIBRARY,0
dc.l StringTools.Name,Library.ID
dc.l Library.Init
Library.Init dc.l stb_SIZEOF
dc.l Functions.Table
dc.l Data.Table
dc.l Lib.InitRoutine
Functions.Table
dc.l Lib.Open
dc.l Lib.Close
dc.l Lib.Expunge
dc.l NullFunc
dc.l stStrLen
dc.l stStrCmp
dc.l stStrCpy
dc.l stStrnCpy
dc.l stStrCat
dc.l stStrnCat
dc.l stStrToUpper
dc.l stStrToLower
dc.l stBubbleSort
dc.l stBubbleSortHook
dc.l -1
Data.Table
INITBYTE LN_TYPE,NT_LIBRARY
INITLONG LN_NAME,StringTools.Name
INITLONG LIB_IDSTRING,Library.ID
INITBYTE LIB_FLAGS,LIBF_SUMUSED!LIBF_CHANGED
INITWORD LIB_VERSION,ST_VERNUM
INITWORD LIB_REVISION,ST_REVNUM
dc.l 0
Lib.InitRoutine ; (D0)Success=Lib.InitRoutine(Base,SegList)(D0,A0)
movem.l d7/a5-a6,-(sp)
move.l d0,a5
move.l a6,stb_ExecBase(a5)
move.l a0,stb_SegList(a5)
lea Utility.Name(pc),a1
moveq #37,d0
jsr _LVOOpenLibrary(a6)
move.l d0,stb_UtilityBase(a5)
bne.s .UtilOpened
move.l #AG_OpenLib!AO_UtilityLib,d7
jsr _LVOAlert(a6)
bra.s .NoUtil
.UtilOpened
move.l a5,d0
.Back movem.l (sp)+,d7/a5-a6
rts
.NoUtil move.l a5,a1
moveq #0,d0
move.w LIB_NEGSIZE(a5),d0
sub.l d0,a1
add.w LIB_POSSIZE(a5),d0
jsr _LVOFreeMem(a6)
moveq #0,d0
bra.s .Back
Lib.Open
addq.w #1,LIB_OPENCNT(a6)
bclr #LIBB_DELEXP,LIB_FLAGS(a6)
move.l a6,d0
rts
Lib.Close
subq.w #1,LIB_OPENCNT(a6)
bne.s NullFunc
btst #LIBB_DELEXP,LIB_FLAGS(a6)
bne.s Lib.Expunge
NullFunc
moveq #0,d0
rts
Lib.Expunge
movem.l d2/a5-a6,-(sp)
move.l a6,a5
tst.w LIB_OPENCNT(a5)
beq.s .DoIt
bset #LIBB_DELEXP,LIB_FLAGS(a5)
moveq #0,d0
bra.s .Ret
.DoIt move.l stb_ExecBase(a5),a6
move.l a5,a1
jsr _LVORemove(a6)
move.l stb_UtilityBase(a5),a1
jsr _LVOCloseLibrary(a6)
move.l stb_SegList(a5),d2
moveq #0,d0
move.l a5,a1
move.w LIB_NEGSIZE(a5),d0
sub.l d0,a1
add.w LIB_POSSIZE(a5),d0
jsr _LVOFreeMem(a6)
move.l d2,d0
.Ret movem.l (sp)+,d2/a5-a6
rts
; (D0)Length=stStrLen(String)(A0)
; retourne la longueur de la chaîne
stStrLen
move.l a0,-(sp)
move.l a0,d0
.Loop tst.b (a0)+
bne.s .Loop
sub.l d0,a0
move.l a0,d0
subq.l #1,d0
move.l (sp)+,a0
rts
; (D0,Z,N)Result=stStrCmp(String1,String2)(A0,A1)
; Compare les deux chaînes:
; D0<0 => String1 < String2
; D0<0 => String1 = String2
; D0>0 => String2 > String2
stStrCmp
movem.l d1-d2/a0-a1,-(sp)
moveq #0,d0
.Loop move.b (a0)+,d1
beq.s .EOS1
move.b (a1)+,d2
beq.s .Sup
cmp.b d2,d1
beq.s .Loop
bcs.s .Inf
.Sup moveq #1,d0
bra.s .Ret
.EOS1 tst.b (a1)+
beq.s .Ret
.Inf moveq #-1,d0
.Ret movem.l (sp)+,d1-d2/a0-a1
rts
; stStrCpy(Buffer,String)(A0,A1)
; Copie la chaîne dans le tampon. Attention, le débordement du
; tampon n'est pas testé! Il vous faut corriger cette erreur.
stStrCpy
movem.l a0-a1,-(sp)
.Loop move.b (a1)+,(a0)+
bne.s .Loop
movem.l (sp)+,a0-a1
rts
; stStrnCpy(Buffer,String,Max)(A0,A1,D0)
; Pareil, mais ne copie au maximum que Max caractères.
stStrnCpy
movem.l a0-a1/d0,-(sp)
subq.l #1,d0
.Loop move.b (a1)+,(a0)+
dbne d0,.Loop
.Ret movem.l (sp)+,a0-a1/d0
rts
; stStrCat(String1,String2)(A0,A1)
; String1 <- String1 + String2
; attention : pas de test de débordement de tampon !
stStrCat
movem.l a0-a1,-(sp)
.LLoop tst.b (a0)+
bne.s .LLoop
subq.l #1,a0
.CLoop move.b (a1)+,(a0)+
bne.s .CLoop
movem.l (sp)+,a0-a1
rts
; stStrnCat(String1,String2,Max)(A0,A1,D0)
; pareil, mais au plus Max caractères de String2 seront ajoutés
; à String1.
stStrnCat
movem.l a0-a1/d0,-(sp)
.LLoop tst.b (a0)+
bne.s .LLoop
subq.l #1,a0
subq.l #1,d0
.CLoop move.b (a1)+,(a0)+
dbne d0,.CLoop
movem.l (sp)+,a0-a1/d0
rts
; stToUpper(String)(A0)
; convertion de la chaîne en casse majuscule
stStrToUpper
movem.l d0-d1/a0-a2/a6,-(sp)
move.l a0,a2
move.l stb_UtilityBase(a6),a6
.Loop move.b (a2)+,d0
beq.s .End
jsr _LVOToUpper(a6)
move.b d0,-1(a2)
bra.s .Loop
.End movem.l (sp)+,d0-d1/a0-a2/a6
rts
; stToLower(String)(A0)
; convertiopn la chaîne en casse minuscule
stStrToLower
movem.l d0-d1/a0-a2/a6,-(sp)
move.l a0,a2
move.l stb_UtilityBase(a6),a6
.Loop move.b (a2)+,d0
beq.s .End
jsr _LVOToLower(a6)
move.b d0,-1(a2)
bra.s .Loop
.End movem.l (sp)+,d0-d1/a0-a2/a6
rts
; stBubbleSort(Array)(A0)
; Cas particulier de stBubbleSortHook() avec un crochet
; consistant en un appel à stStrCmp() (tri alphabétique
; normal, en différenciant majuscules et minuscules) :
stBubbleSort
lea StrCmp.Hook(pc),a1
; stBubbleSortHook(Array,CompHook)(A0,A1)
; La fonction la plus délicate : trier un tableau de
; pointeurs sur des chaînes de caractères. Pour la
; comparaison, on utilise un crochet fourni par
; l'utilisateur, ce qui permet différents types de tri.
; Le crochet est appelé avec en A1 et A2 les deux chaînes
; à comparer.
stBubbleSortHook
movem.l d0-d3/a0-a6,-(sp)
moveq #0,d2
move.l a0,a5
move.l a1,d3
move.l stb_UtilityBase(a6),a6
.Loop1 tst.l d2
bne.s .End
moveq #-1,d2 ; fini
move.l a5,a3
tst.l (a3) ; a3= 1ere chaîne **
beq.s .Loop1
.Loop2 lea 4(a3),a4 ; a4= 2nde chaîne **
tst.l (a4)
beq.s .Loop1
move.l (a3),a1
move.l (a4),a2
move.l d3,a0
jsr _LVOCallHookPkt(a6)
tst.l d0
ble.s .NoSwap
move.l (a3),d0
move.l (a4),(a3)
move.l d0,(a4)
moveq #0,d2
bra.s .Loop2
.NoSwap move.l a4,a3
bra.s .Loop2
.End movem.l (sp)+,d0-d3/a0-a6
rts
stStrCmp.HookFunc
move.l a1,a0
move.l a2,a1
bra stStrCmp
StrCmp.Hook
dc.l 0,0,stStrCmp.HookFunc,0,0
StringTools.Name dc.b "stringtools.library",0
Library.ID dc.b "stringtools.library 1.0 (25/12/94)",0
Utility.Name dc.b "utility.library",0
EndLib
|
Code source de Bibliotheque.s.
opt AMIGA
include exec/exec_lib.i
include exec/exec.i
include dos/dos_lib.i
include stringtools.i
include dos/dos.i
move.l 4.w,a6
lea StringTools.Name(pc),a1
moveq #1,d0
jsr _LVOOpenLibrary(a6)
move.l d0,StringTools.Base
beq.s Exit
lea DOS.Name(pc),a1
moveq #37,d0
jsr _LVOOpenLibrary(a6)
move.l d0,DOS.Base
beq.s CloseST
move.l #Args.Template,d1
move.l #Args.Array,d2
moveq #0,d3
move.l DOS.Base(pc),a6
jsr _LVOReadArgs(a6)
move.l d0,Args.RDArgs
beq.s CloseDOS
move.l Args.Array(pc),a0
move.l StringTools.Base(pc),a6
jsr _LVOstBubbleSort(a6)
move.l Args.Array(pc),a2
move.l DOS.Base(pc),a6
PrintLoop
move.l (a2)+,d1
beq.s EndPrint
jsr _LVOPutStr(a6)
move.l #LineFeed,d1
jsr _LVOPutStr(a6)
bra.s PrintLoop
EndPrint
move.l Args.RDArgs(pc),d1
jsr _LVOFreeArgs(a6)
CloseDOS
move.l DOS.Base(pc),a1
move.l 4.w,a6
jsr _LVOCloseLibrary(a6)
CloseST move.l StringTools.Base(pc),a1
jsr _LVOCloseLibrary(a6)
Exit moveq #0,d0
rts
StringTools.Base dc.l 0
DOS.Base dc.l 0
Args.Array
dc.l 0
Args.RDArgs dc.l 0
StringTools.Name STRINGTOOLSNAME
DOS.Name DOSNAME
Args.Template dc.b "WORDS/M/A",0
LineFeed dc.b 10,0
|
Code source "TestST.s"
opt AMIGA
include exec/exec_lib.i
include exec/exec.i
include dos/dos_lib.i
include stringtools.i
move.l 4.w,a6
lea StringTools.Name(pc),a1
moveq #1,d0
jsr _LVOOpenLibrary(a6)
move.l d0,StringTools.Base
beq.s Exit
lea DOS.Name(pc),a1
moveq #37,d0
jsr _LVOOpenLibrary(a6)
move.l d0,DOS.Base
beq.s CloseST
move.l #Args.Template,d1
move.l #Args.Array,d2
moveq #0,d3
move.l DOS.Base(pc),a6
jsr _LVOReadArgs(a6)
move.l d0,Args.RDArgs
beq.s CloseDOS
move.l Args.Array(pc),a0
move.l StringTools.Base(pc),a6
jsr _LVOstBubbleSort(a6)
move.l Args.Array(pc),a2
move.l DOS.Base(pc),a6
PrintLoop
move.l (a2)+,d1
beq.s EndPrint
jsr _LVOPutStr(a6)
move.l #LineFeed,d1
jsr _LVOPutStr(a6)
bra.s PrintLoop
EndPrint
move.l Args.RDArgs(pc),d1
jsr _LVOFreeArgs(a6)
CloseDOS
move.l DOS.Base(pc),a1
move.l 4.w,a6
jsr _LVOCloseLibrary(a6)
CloseST move.l StringTools.Base(pc),a1
jsr _LVOCloseLibrary(a6)
Exit moveq #0,d0
rts
StringTools.Base dc.l 0
DOS.Base dc.l 0
Args.Array dc.l 0
Args.RDArgs dc.l 0
StringTools.Name STRINGTOOLSNAME
DOS.Name DOSNAME
Args.Template dc.b 'WORDS/M/A',0
LineFeed dc.b 10,0
|
Note : retrouvez le guide, les fichiers source et l'exécutable concernant cet article dans cette
archive.
|