Organisation du programme et du fichier de code
Un programme relativement long peut tenir dans un seul fichier texte, être compilé en un seul morceau et exécuté comme un seul bloc de code. Mais comme de nombreux utilisateurs ont besoin de programmes de taille conséquente et comme le p-System de l'UCSD est un système pour microprocesseurs, ayant une capacité d'entreposage limitée, il est souvent nécessaire de diviser un programme et de le compiler en deux ou plusieurs morceaux.
La compilation séparée présente d'autres avantages. Une procédure unique peut être utilisée par plusieurs programmes différents, et il peut donc être plus pratique de compiler cette procédure une fois et de l'utiliser plusieurs fois. Il peut en être de même pour un ensemble de procédures collectées ou une structure de données particulière. Une utilisation judicieuse de la compilation séparée peut contribuer à l'organisation d'un grand projet de programmation.
L'option $Include du compilateur permet à un programmeur d'entreposer des parties de texte de programme dans des fichiers séparés. Le compilateur les lit et compile l'ensemble du programme en une seule fois. C'est souvent une chose utile à faire, surtout si les parties incluses ne sont pas trop longues et partagées par plus d'un programme. Mais l'utilisation de $Include ne résout pas le problème de la création d'un programme trop volumineux pour être compilé en un seul morceau.
De plus, il peut être avantageux d'intégrer des procédures d'un langage différent dans un programme hôte. C'est le cas lorsqu'un programme n'est généralement pas critique en termes de temps, mais contient certaines sections critiques en termes de temps - les sections en temps réel peuvent être isolées et écrites sous forme de routines en langage assembleur.
Cette section utilisent le terme "routine" pour désigner une procédure, une fonction ou un processus, et le terme "unité de compilation" pour désigner un programme ou un UNIT. Une unité de compilation utilisant des routines compilées séparément est appelée "compilation hôte" ou "client".
Remarque : bien que cette section utilise Pascal pour ses exemples de programme, les fonctions de compilation séparée et de gestion de la mémoire disponibles dans Pascal ont leurs analogues dans les autres langages de haut niveau fournis avec le p-System.
Un UNIT est un ensemble de routines et de structures de données. Elle peut également contenir un code d'initialisation et de terminaison. Comme un programme, il peut être compilé par lui-même, mais contrairement à un programme, il ne peut pas être exécuté, sauf lorsqu'il est invoqué à partir d'un programme. Les programmes et autres UNIT peuvent utiliser des UNIT ayant déjà été compilées.
Dans le p-System, un fichier de code est organisé en «segments». Une unité de compilation contient au moins un segment - les routines et les données de l'unité de compilation elle-même. Ce segment est appelé le «segment principal». Si l'unité de compilation contient des routines SEGMENT, chaque routine de segment sera un «segment subsidiaire» accompagnant le segment principal. Si l'unité de compilation référence des UNIT compilées séparément, celles-ci ne sont pas considérées comme des segments subsidiaires, mais sont nommées dans une liste de références de segments accompagnant le segment principal. Les segments sont l'unité de transfert de base lorsque le code est lu à partir d'un disque ou supprimé de la mémoire.
L'utilitaire LIBRARY peut être utilisé pour regrouper des unités de compilation dans un seul fichier de code et modifier l'organisation des fichiers de code existants. Les fichiers de code sont souvent appelés «bibliothèques», en particulier lorsqu'ils ne contiennent pas de programme.
Lorsqu'un programme hôte utilisant d'autres unités est exécuté, le système recherche le code approprié en utilisant la liste de référence de segment du segment hôte.
L'utilisateur peut maintenir un ou plusieurs «fichiers texte de bibliothèque», étant des fichiers contenant une liste de fichiers de code dont une compilation hôte peut avoir besoin. Lorsque le système recherche une unité nécessaire, il regarde d'abord (dans l'ordre) les fichiers de code nommés dans le fichier texte de bibliothèque par défaut de l'utilisateur, et si cette recherche échoue, il regarde dans *SYSTEM.LIBRARY. Le nom par défaut du fichier texte de bibliothèque de l'utilisateur est *USERLIB.TEXT ; il peut être modifié par une option d'exécution. Une unité de compilation peut également spécifier la bibliothèque dont elle a besoin en utilisant l'option $U du compilateur. Dans le code source, une unité de compilation «cliente» spécifie qu'elle a besoin d'une certaine UNIT (ou de plusieurs UNIT) par une déclaration immédiatement après l'identification du programme (ou de l'UNIT). Par exemple :
Un UNIT elle-même peut être décrite de la manière suivante :
- UNIT I_AM_A_SAMPLE;
-
- INTERFACE
-
- ... { déclarations de données et déclarations de procédures }
-
- IMPLEMENTATION
-
- ... { déclarations de données et code de procédure }
-
- BEGIN { bloc d'initialisation et de terminaison }
- ... { code d'initialisation }
- ***
- ... { code de terminaison }
- END.
Il existe deux parties principales. La partie INTERFACE contient les déclarations de procédures et de données pouvant être utilisées par le client. La partie IMPLEMENTATION contient le code des procédures déclarées dans la partie INTERFACE, ainsi que les déclarations de données et autres procédures utilisées par les procédures déclarées dans la partie INTERFACE, mais ne pouvant pas être utilisées par le client. Enfin, il existe une section facultative de code Pascal contenant deux parties : une partie d'initialisation, étant le code exécuté avant l'exécution du corps principal du programme hôte, et une partie de terminaison, étant le code exécuté une fois le code du programme hôte terminé. Ces deux parties sont séparées par '***;'.
Lorsque les routines sont assemblées plutôt que compilées, elles sont déclarées EXTERNAL dans le programme hôte, par exemple :
Les routines assemblées doivent respecter scrupuleusement les conventions d'appel et de passage de paramètres de Pascal, et respecter les contraintes du système sur l'utilisation des ressources de la machine telles que les registres.
Les routines externes (code assemblé) doivent être liées à un hôte par le Linker ; une fois liées, elles restent partie intégrante du programme. Si le programme hôte utilise des routines externes contenues dans des fichiers de code autres que SYSTEM.LIBRARY, le Linker doit être exécuté explicitement (à l'aide de la commande L(ink).
Pour partitionner un programme ou une UNIT en morceaux séparés étant chargés indépendamment à partir du disque selon les besoins, l'utilisateur peut désigner des routines comme des routines SEGMENT, par exemple :
Chaque routine de segment occupe un segment subsidiaire dans un fichier de code. Pendant l'exécution d'un programme, tous les segments de code, principaux et subsidiaires, se disputent la mémoire principale de manière dynamique. (La seule exception à cette règle concerne les segments de code natifs, pouvant devoir résider en mémoire). Les segments ne sont chargés que lorsqu'ils doivent être exécutés. Lorsqu'ils ne sont plus nécessaires, ils restent en mémoire jusqu'à ce que l'espace qu'ils occupent soit nécessaire pour une autre utilisation.
L'utilisation de routines de segment permet au système de mieux allouer la mémoire, car seuls les segments utilisés doivent être en mémoire à un moment donné. Les fonctions intrinsèques MEMLOCK et MEMSWAP peuvent être utilisées pour contrôler directement la résidence d'un segment.
Les routines d'initialisation et de terminaison d'un programme sont des candidats de choix pour être déclarées comme des SEGMENT, car elles sont souvent volumineuses et ne sont appelées qu'une seule fois. Elles n'ont pas besoin d'occuper de l'espace mémoire après (ou avant) d'avoir rempli leur fonction.
Les programmes peuvent être «chaînés», c'est-à-dire qu'un programme peut désigner un autre programme à exécuter lorsque le programme «enchaînant» a terminé son exécution. Voir l'intrinsèque CHAIN.
En utilisant le p-System, des programmes en langage assembleur autonomes peuvent être créés, liés, chargés et exécutés.
Remarque
- Il existe de nombreuses alternatives développés par des alternatives tiers pour convertir le P-code en code machine native, par exemple Zoom Pascal pour Commodore 64 offre un utilitaire nommé ZT-64 pour convertir du code P-code en 6502.