Obligement - L'Amiga au maximum

Jeudi 22 juin 2017 - 14:03  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

 · Accueil
 · A Propos
 · Articles
 · Galeries
 · Glossaire
 · Hit Parade
 · Liens
 · Liste jeux Amiga
 · Quizz
 · Téléchargements
 · Trucs et astuces


Articles

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

 · Articles in english
 · Articles in other languages


Twitter

Suivez-nous sur Twitter




Liens

 · Sites de téléchargements
 · Associations
 · Pages Personnelles
 · Moteurs de recherche
 · Pages de liens
 · Constructeurs matériels
 · Matériel
 · Autres sites de matériel
 · Réparateurs
 · Revendeurs
 · Presse et médias
 · Programmation
 · Développeurs logiciels
 · Logiciels
 · Développeurs de jeux
 · Jeux
 · Autres sites de jeux
 · Scène démo
 · Divers
 · Informatique générale


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


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


Partenaires

Annuaire Amiga

Amedia Computer

Relec

Hit Parade


Soutien

N'hésitez pas à soutenir le projet Obligement



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 "hunks", chacun décrivant les données qu'il contient. Sans entrer dans les détails, disons que chaque hunk 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 l'offset 0) :
  • offset -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.

  • offset -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 tags, en D1. Les tags 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 tags NP_SegList ou NP_Entry est nécessaire.

  • NP_FreeSegList : tag 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 tag est "TRUE", ce qui est faux ! Il faut absolument fournir ce tag 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 : tags 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 tag 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 tags 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 tags 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.

Assembleur
Assembleur


[Retour en haut] / [Retour aux articles]