Obligement - L'Amiga au maximum

Vendredi 08 mai 2026 - 18:39  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

Actualité (récente)
Actualité (archive)
Comparatifs
Dossiers
Entrevues
Matériel (tests)
Matériel (bidouilles)
Points de vue
En pratique
Programmation
Reportages
Quizz
Tests de jeux
Tests de logiciels
Tests de compilations
Trucs et astuces
Articles divers

Articles in English


Réseaux sociaux

Suivez-nous sur Mastodon




Liste des jeux Amiga

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


Trucs et astuces

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


Glossaire

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


Galeries

Menu des galeries

BD d'Amiga Spécial
Caricatures Dudai
Caricatures Jet d'ail
Diagrammes de Jay Miner
Images insolites
Fin de jeux (de A à E)
Fin de Jeux (de F à O)
Fin de jeux (de P à Z)
Galerie de Mike Dafunk
Logos d'Obligement
Pubs pour matériels
Systèmes d'exploitation
Trombinoscope Alchimie 7
Vidéos


Téléchargement

Documents
Jeux
Logiciels
Magazines
Divers


Liens

Associations
Jeux
Logiciels
Matériel
Magazines et médias
Pages personnelles
Réparateurs
Revendeurs
Scène démo
Sites de téléchargement
Divers


Partenaires

Annuaire Amiga

Amedia Computer

Relec


A Propos

A propos d'Obligement

A Propos


Contact

David Brunet

Courriel

 


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.


[Retour en haut] / [Retour aux articles]