Obligement - L'Amiga au maximum

Jeudi 29 janvier 2026 - 03:59  

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 - les SegList-splits
(Article écrit par Frédéric Delacroix et extrait d'Amiga News - mai 1994)


Comment faire pour qu'un programme, une fois lancé à partir du CLI sans la commande "Run", puisse rendre la main à celui-ci, lui permettant de lancer un autre programme, ou même de se fermer ? Cet article va décrire une solution, certes pas unique, mais de loin la plus utilisée.

Généralités

Lorsqu'un programme est stocké sur disque, outre les données du programmes proprement dites, il existe des informations annexes. Le fichier exécutable est ainsi divisé en segments, chacun décrivant les données qu'il contient. Sans entrer dans les détails, disons que chaque segment contient les données de chaque segment du programme, c'est-à-dire les morceaux du programme séparés par la directive "Section" pour la plupart des assembleurs, ainsi que des informations de relogement nécessaire (pour que le programme fonctionne à partir de n'importe quelle adresse en mémoire).

On peut également trouver quelques données additionnelles tels que les noms des symboles ou des numéros de lignes destinés à un débogueur (c'est ce système qu'utilise l'option "HCLN" de Devpac 3). Une fois que le programme a été chargé en mémoire par le DOS, le relogement est automatiquement effectué par la fonction LoadSeg(). La division en segments subsiste toutefois, et ceux-ci peuvent se trouver éparpillés dans la mémoire. Pour que le DOS retrouve ses billes lors de la libération de mémoire à la fin du programme, il code quelques informations pour chaque segment. On trouve ces informations juste avant le début du code programme de chaque segment (repéré ici par le décalage 0) :
  • Décalage -4 : pointeur BCPL sur le prochain segment (0 si c'est le dernier segment). Ce pointeur pointe sur le pointeur "prochain segment" du segment suivant. Rappelons qu'à cause des excentricités originelles du BCPL, tous les pointeurs utilisés par ce langage pour le moins bizarre sont multiplier par 4 avant d'en faire une utilisation sensée.

  • Décalage -8 : mot long qui contient la taille en octets de la zone mémoire réservée pour ce segment. Cette zone inclut bien entendu ce mot long. Cette valeur n'est utilisée que par la fonction UnLoadSeg().
La solution

A la lumière de tout ceci, la voie est tracée : il suffit, après quelques initialisations éventuelles, de "couper" la liste des segments, de démarrer un nouveau processus grâce à la fonction Create[New]Proc() à partir du second segment et de laisser le premier se terminer normalement par un RTS. Le DOS libérera alors la SegList avec laquelle il avait lancé le programme par la fonction UnLoadSeg(), mais comme nous avons pris la précaution de la couper auparavant, la zone mémoire occupée par le second processus - celui que nous venons de démarrer - ne sera pas libérée, et celui-ci pourra tourner en paix. Lorsqu'il voudra se terminer, un simple RTS suffira. Enfantin, non ?

Quelques précautions élémentaires de programmation s'imposent toutefois : une fois la partie résidente du programme lancée, celle-ci ne doit pas chercher à accéder aux données qui étaient contenues dans le premier segment, étant donné qu'il s'agit alors de mémoire libérée. Ceci étant précisé, nous pouvons passer à l'écriture d'un programme d'exemple.

Un exemple

Pour avoir un bon confort de programmation, nous nous restreignons aux versions supérieures ou égales à la version 37 (Kickstart 2.04) du système d'exploitation. On pourrait - non sans un peu de mal - adapter cette méthode aux versions antédiluviennes du système, sur lesquelles la fonction CreateNewProc() de la dos.library n'existe pas.

Le programme commence par ouvrir la dos.library V37. Si cela réussit, il sauvegarde alors le contenu du pointeur "prochain segment" (trouvé à l'adresse Start-4) et met un 0 à sa place. Physiquement, il n'existe plus aucun lien entre le premier segment et les autres.

On démarre ensuite le processus à partir du pointeur que l'on avait sauvegardé grâce à la fonction CreateNewProc(). Examinons au passage cette fonction et ses arguments. Elle ne prend qu'un argument, une liste de marqueurs, en D1. Les marqueurs utilisables sont les suivants :
  • NP_SegList : indique la SegList à utiliser pour démarrer le nouveau processus. C'est ce que nous avons utilisé dans notre cas.

  • NP_Entry : indique le point d'entrée pour le nouveau processus. Celui-ci ne possèdera pas de SegList. L'un des marqueurs NP_SegList ou NP_Entry est nécessaire.

  • NP_FreeSegList : marqueur booléen indiquant s'il faut libérer ou pas la SegList à la fin du programme. Attention : dans les auto-docs, il est mentionné que la valeur par défaut de ce marqueur est "TRUE", ce qui est faux ! Il faut absolument fournir ce marqueur pour ne pas perdre la mémoire !

  • NP_Arguments : pointe sur la chaîne de caractères qui sera passée en paramètres au nouveau processus.

  • NP_Input, NP_Output, NP_Error : indiquent les FileHandles des canaux respectivement d'entrée, de sortie et d'erreur. Par défaut, NIL: est utilisé.

  • NP_Closelnput, MP_CloseOutput, NP_CloseError : marqueurs booléens indiquant s'il faut ou non fermer les canaux correspondant à la fin du processus.

  • NP_CurrentDir : indique, par un FileLock, le chemin courant du processus.

  • NP_StackSize : indique la taille de sa pile (4000 par défaut).

  • NP_Name, NP_Priority : indiquent son nom ("New process" par défaut) et sa priorité.

  • NP_WindowPtr : ce marqueur est très important dans l'utilisation que nous en faisons. En effet, lorsque le DOS doit afficher une requête système (dans le style "Le volume XXX est protégé en écriture"), il regarde le pointeur pr_WindowPtr de la structure Process concernée. S'il est valide, ce champ pointe sur une fenêtre qui sera utilisée pour déterminer l'écran où il faut afficher la requête. S'il est nul. l'écran public par défaut ("Workbench" en général) est utilisé, s'il est égal à -1, aucune requête n'est affichée. Dans la plupart des cas, le processus associé à un CLI a son champ pr_WindowPtr nul, mais il arrive que des petits programmes le remplacent, afin de ne pas avoir à revenir au Workbench si on utilise un CLI sur un autre écran.

    Le problème, c'est que le processus que nous créons ne peut pas être sûr que la fenêtre CLI du processus père est encore ouverte. Donc le champ pr_WindowPtr risque de contenir une valeur invalide s'il est non nul, d'où un gourou assuré en cas de requête système. Il faut donc le mettre à 0 dans notre cas.

  • NP_Cli : indique s'il faut créer une nouvelle structure CLI pour le nouveau processus (non par défaut).

  • NP_ConsoleTask, NP2FlomeDir, NP_Path : dans le cas où une structure CLI est associée au nouveau processus, ces marqueurs indiquent le processus de console, le tiroir d'origine et le chemin d'accès.
En retour de la fonction CreateNewProc(), on obtient un pointeur sur la structure Process nouvellement créée (rappelons qu'un processus est une tâche au sens Exec du terme avec quelques extensions, lui permettant notamment d'appeler le DOS), ou 0 si la création a échoué (par manque de mémoire, absence de l'un des marqueurs NP_Entry ou NP_SegList...). Dans notre cas, on teste si la création a réussi. Si c'est le cas, nous laissons le processus fils fermer la dos.library que nous avons ouverte, sinon nous la fermons nous-même.

Le processus fils est très simple : il se contente d'ouvrir une fenêtre CON:, d'attendre un caractère et de sortir en fermant tout derrière lui.

Une dernière remarque : même si le code est écrit en réentrant (c'est-à-dire utilisable par plusieurs tâches à la fois), un programme utilisant ce stratagème pour se détacher du CLI ne peut pas être rendu résident, pour la simple raison que la liste des programmes résidents est gérée en gardant des pointeurs sur les SegLists, cette même SegList que nous avons sauvagement sectionnée.

Sur ce, je vous laisse avec le listing du programme... La prochaine fois, nous verrons comment lire les arguments à partir du CLI, et les types d'outils à partir du Workbench.

	include	dos/dos.i
	include	dos/dos_lib.i
	include	dos/dostags.i
	include	exec/exec_lib.i

	section Demarrage,CODE

Start	move.l	4.w,a6	; on commence par ouvrir la dos.library
	lea	DOS.Name(pc),a1	; remarquez que DOS.Base est dans le
	move.l	#37,d0		; second segment
	jsr	_LVOOpenLibrary(a6)
	move.l	d0,DOS.Base
	beq.s	exit

	lea	Start-4(pc),a0	; pointe sur le pointeur "prochain segment"
	move.l	(a0),SecondProc.SegList	; que l'on sauvegarde
	clr.l	(a0)	; et on coupe la liste en y mettant un 0
	move.l	#NewProc.Tags,d1	; ensuite on démarre le
	move.l	DOS.Base,a6	; nouveau processus grâce à la fonction
	jsr	_LVOCreateNewProc(a6)	; CreateNewProc()
	tst.l	d0	; si d0 est non nul, tout s'est bien passé
	bne.s	exit

	move.l	DOS.Base,a1	; dans le cas contraire, on ferme la
	move.l	4.w,a6		; dos.library
	jsr	_LVOCloseLibrary(a6)
exit	moveq	#0,d0
	rts

NewProc.Tags	
	dc.l	NP_Seglist			; ces tags permettent de définir
SecondProc.SegList	
	dc.l	0					; certains attributs du nouveau
	dc.l	NP_Name,NomDuProg	; processus
	dc.l	NP_FreeSeglist,-1
	dc.l	NP_WindowPtr,0
	dc.l	TAG_DONE

DOS.Name	dc.b	'dos.library',0

	section	PartieResidente,CODE

	move.l	DOS.Base(pc),a6		; on ouvre une fenêtre console
	move.l	#NomDeLaFenetre,d1	; grâce au DOS
	move.l	#MODE_NEWFILE,d2
	jsr	_LVOOpen(a6)
	move.l	d0,Fenetre.Handle
	beq.s	PasDeFenetre
	move.l	d0,d1	; et on attend d'obtenir un caractère.
	jsr	_LVOFGetC(a6)
	move.l	Fenetre.Handle(pc),d1	; fermons la fenêtre
	jsr	_LVOClose(a6)
PasDeFenetre
	move.l	a6,a1	; on ferme la dos.library qui avait été ouverte
	move.l	4.w,a6	; par le programme de démarrage.
	jsr	_LVOCloseLibrary(a6)
	moveq	#0,d0
	rts

DOS.Base	dc.l	0
Fenetre.Handle	dc.l	0

NomDuProg	dc.b	'Programme auto-détachant',0
NomDeLaFenetre	dc.b	'CON:50/100/540/56/Appuyez sur RETURN',0


[Retour en haut] / [Retour aux articles]