Programmes en morceaux
L'Apple Pascal prend en charge la séparation des procédures et des fonctions, ou des groupes de celles-ci, du programme principal. Lorsque vous développez un programme volumineux ou complexe, cela peut être très utile car cela vous permet de réduire la taille des fichiers de code, de réduire l'espace mémoire utilisé par le programme et d'utiliser un ensemble de procédures et de fonctions dans plusieurs programme. La séparation peut être réalisée à la fois au niveau du P-Code et au niveau de la langage de programmation source. Au niveau du P-Code, toute procédure ou fonction peut être désignée comme SEGMENT. Le résultat est que son code n'est pas chargé en mémoire tant qu'il n'est pas appelé par une autre partie du programme. Dès que la procédure ou la fonction SEGMENT n'est plus active, elle est "permutée"; c'est-à-dire que son espace mémoire est rendu disponible pour une autre utilisation telle que l'allocation de mémoire dynamique ou l'échange dans un autre SEGMENT. Cette technique est parfois appelée recouvrement.
Au niveau de la langage de programmation source, un groupe d'une ou de plusieurs procédures ou fonctions peut être compilé séparément en tant qu'unité. Le résultat de la compilation d'une UNIT est un fichier de bibliothèque; il peut être utilisé directement ou incorporé dans un autre fichier de bibliothèque tel que SYSTEM.LIBRARY. La compilation séparée présente plusieurs avantages dans le développement de tout programme volumineux ou compliqué, car elle vous permet d'aborder la tâche comme un groupe de tâches plus petites étant liées entre elles de manière simple et logique. Plusieurs des fonctionnalités puissantes d'Apple Pascal sont mises en oeuvres sous forme de UNIT. Pour utiliser une UNIT compilée séparément, un programme doit contenir une déclaration USES avec le nom de l'UNIT; le programme est alors appelé programme hôte.
Il existe deux types d'unités: les unités régulières et les unités intrinsèques. Lorsqu'un programme hôte utilise un USES avec une UNIT régulière, le code du UNIT est inséré dans le fichier de code du programme hôte par l'éditeur de liens. Il ne doit être fait qu'une seule fois à moins que le UNIT ne soit modifiée et recompilée; puis il doit être de nouveau lié au programme hôte. Lorsqu'un programme hôte utilise USES une UNIT intrinsèque, le code du UNIT reste dans son fichier de bibliothèque et est automatiquement chargé en mémoire lorsque le programme hôte est exécuté. Il réduit la taille du fichier de code du programme hôte, ce qui est particulièrement important si de nombreux programmes utilisent UNIT. Il permet également de modifier et de recompiler l'unité sans avoir besoin de se reconnecter.
Les options NOLOAD et RESIDENT du compilateur permettent un contrôle supplémentaire sur la gestion des UNIT intrinsèques et des procédures et fonctions SEGMENT. NOLOAD empêche toute UNIT d'être automatiquement chargée jusqu'à ce que son code soit activé par le programme hôte. L'option RESIDENT peut modifier l'effet de NOLOAD ou d'une procédure ou fonction SEGMENT; il force une procédure ou une fonction à être conservée en mémoire sur une plage spécifiée d'exécution du programme - en particulier, tant que la procédure ou la fonction contenant l'option RESIDENT est active, la procédure nommée dans l'option RESIDENT est conservée en mémoire.
Enfin, il y a le mécanisme EXTERNAL. Il permet de déclarer une procédure ou une fonction dans un programme hôte Pascal, sans aucune instruction sauf un en-tête et le mot EXTERNAL. La procédure ou la fonction est mise en oeure séparément en langage de programmation assembleur, assemblée, puis liée au programme hôte avec l'éditeur de liens. Il peut être avantageux pour des procédures ou des fonctions devant s'exécuter très rapidement.
Procédures et fonctions du segment
Les déclarations des procédures et fonctions SEGMENT sont identiques aux procédures et fonctions Pascal ordinaires sauf que le mot PROCEDURE ou FUNCTION est précédé du mot SEGMENT. Par exemple :
Le comportement du programme ne diffère pas; cependant, le code et les données d'une procédure ou d'une fonction SEGMENT sont en mémoire uniquement lorsque la procédure ou la fonction est en cours d'exécution. Il peut être modifié en utilisant l'option Compilateur (*$R nom *). Toute procédure ou définition de fonction peut avoir le mot SEGMENT. Il inclut les définitions FORWARD et les définitions imbriquées. L'avantage de l'utilisation des procédures SEGMENT est la possibilité d'insérer des programmes volumineux dans la mémoire disponible. Pour écrire un tel programme, divisez-le en deux ou plusieurs tâches principales étant mise en oeuvres en tant que procédures SEGMENT. Pour être efficace, chaque SEGMENT doit être de taille substantielle et le programme doit être conçu de manière à ce que les SEGMENT ne soient pas échangés trop fréquemment.
Exigences et limitations
Le disque contenant le fichier de code du programme doit être en ligne (et dans le même lecteur que lors du démarrage du programme) chaque fois qu'une des procédures SEGMENT doit être appelée. Sinon, le système tentera de récupérer et d'exécuter toutes les informations occupant maintenant cet emplacement particulier sur le disque maintenant dans cette unité, généralement avec des résultats très déplaisants. Les procédures SEGMENT doivent être les premières déclarations de procédure contenant des instructions générant du code.
Bibliothèques et unités
Jusqu'à présent, nous avons vu des programmes Pascal compilés en fichiers de code; un fichier de code peut être Run ou eXecuted. Nous allons maintenant considérer les UNIT, étant compilés dans des bibliothèques. Deux bibliothèques ou plus peuvent être combinées en un seul fichier. Une bibliothèque n'est pas Run ou eXecuted; à la place, elle est utilisée par un ou plusieurs programmes.
Une bibliothèque contient du code pour les procédures et/ou les fonctions étant disponibles pour tout programme utilisant la bibliothèque, comme si elles étaient définies dans le programme lui-même. Par exemple, le système Apple Pascal est livré avec une bibliothèque appelée SYSTEM.LIBRARY contenant du code pour plusieurs UNIT; l'un des UNIT est appelé TURTLEGRAPHICS, et il fournit un ensemble de procédures et de fonctions pour les graphiques haute résolution sur Apple. Pour utiliser ces procédures et fonctions, un programme n'a besoin que de la ligne :
- USES TURTLEGRAPHICS;
après l'entête du programme. Le programme peut alors utiliser une procédure TURTLEGRAPHICS telle que TURNTO ou MOVE. Vous pouvez créer et compiler vos propres UNIT, puis les ajouter à SYSTEM.LIBRARY ou créer vos propres bibliothèques à l'aide de l'utilitaire LIBRARY.
Si un UNIT utilisée par votre programme est contenue dans le fichier SYSTEM.LIBRARY, une commande Run invoquera automatiquement l'éditeur de liens pour effectuer la liaison nécessaire. Sinon, vous devez invoquer explicitement l'éditeur de liens. Notez que si UNIT n'est pas contenue dans le fichier SYSTEM.LIBRARY, vous devez utiliser l'option (*$U nomdefichier *) du compilateur pour indiquer au compilateur quel fichier bibliothèque contient l'unité. Le (*$U nomdefichier *) est placé n'importe où avant l'apparition du nom UNIT dans la déclaration USES.
Unités et utilisations
Le texte source d'un UNIT a une forme quelque peu similaire à un programme Pascal. En bref, il se compose de quatre parties :
- Un entête.
- Une partie INTERFACE définissant la manière dont le programme hôte communique avec les procédures et fonctions de UNIT.
- Une partie IMPLEMENTATION définissant les procédures et fonctionne eux-mêmes.
- Une initialisation consistant en un BEGIN et un END avec n'importe quel nombre d'instructions entre eux. Il s'agit du programme principal de UNIT, et est automatiquement exécuté au début du programme hôte. Notez que l'initialisation peut consister uniquement en BEGIN et END, sans instructions entre eux.
- Il existe deux saveurs différentes de UNIT, appelées régulières et intrinsèques.
UNIT régulières
L'entête d'une UNIT régulière a la syntaxe :
UNIT name; |
Le UNIT est liée au programme hôte une seule fois après la compilation du programme, et le code de l'unité entière est effectivement inséré dans le fichier de code du programme hôte à ce moment-là.
UNIT intrinsèque
Les UNIT intrinsèques ne peuvent être utilisés qu'en les installant dans le fichier SYSTEM.LIBRARY. Il est fait après la compilation en utilisant le programme utilitaire LIBRARY. Une UNIT intrinsèque est «pré-liée» et est insérée dans le fichier de code du programme hôte.
Un UNIT intrinsèque est "pré-liée" et son code n'est jamais réellement inséré dans le fichier de code du programme hôte. Lorsque vous exécute une commande Run pour un programme hôte, l'éditeur de liens n'est pas appelé et n'a pas besoin d'être en ligne. Le code de l'unité intrinsèque est chargé en mémoire lorsque le programme hôte doit être exécuté. Ainsi, un UNIT intrinsèque peut être utilisée dans de nombreux programmes différents, mais il n'y a qu'une seule copie entreposée du code du UNIT.
Il peut être particulièrement utile lors de l'écriture pour un système à un unité n'ayant pas de place pour l'éditeur de liens ou pour des programmes volumineux sur la disquette principale du système. Notez que le fichier SYSTEM.LIBRARY doit être en ligne à chaque exécution du programme appelant. Le titre d'un UNIT intrinsèque a la syntaxe suivante :
UNIT name; INTRINSIC CODE csegnum [DATA dsegnum]; |
où csegnum et dsegnum sont les numéros de segment à associer au UNIT lors de son installation dans le fichier SYSTEM.LIBRARY. Vous choisissez ces numéros et le système les utilise pour identifier le UNIT au moment de l'exécution. Les numéros de segment vont de 0 à 31, mais certains nombres entre 0 et 15 ne doivent pas être utilisés. Le UNIT générera un segment de données si elle déclare des variables non contenues dans des procédures ou des fonctions. Le segment de code sera associé au segment csegnum et son segment de données (s'il y en a un) sera associé au segment dsegnum.
Chaque unité d'une bibliothèque est associée à un numéro de segment spécifique. Les numéros de segment utilisés par les éléments déjà dans la bibliothèque sont indiqués entre parenthèses par les programmes utilitaires LIBRARY et LIBMAP. Lors du choix des numéros de segment pour une UNIT intrinsèque, la contrainte est que lorsque le programme hôte s'exécute, les numéros de segment utilisés par le programme ne doivent pas entrer en conflit. Observez ce qui suit :
- Pendant l'exécution d'un programme, le système utilise le segment 0 et le corps principal du programme utilise le segment 1. Par conséquent, n'utilisez jamais ces nombres pour une UNIT intrinsèque.
- Les segments 2 à 6 sont réservés à l'usage du système.
- Si le programme déclare des procédures ou fonctions SEGMENT, ces procédures ou fonctions utilisent des numéros de segment croissants séquentiellement commençant à 7.
- Chaque UNIT utilisée par le programme utilise le numéro de segment indiqué dans la liste de la bibliothèque.
- Si possible, évitez toute duplication des numéros de segment dans la bibliothèque.
En règle générale, il est judicieux d'utiliser des numéros de segment compris entre 16 et 31.
L'option SWAPPING du compilateur :
- (*$S+*)
Il doit toujours être utilisé lorsqu'une UNIT est compilée. Il doit précéder l'entête du UNIT
La partie INTERFACE d'un UNIT
La première partie d'une UNIT est l'INTERFACE. La partie INTERFACE suit immédiatement la ligne de tête du UNIT. Il déclare des constantes, des types, des variables, des procédures et des fonctions étant publics - c'est-à-dire que le programme hôte peut y accéder comme s'ils avaient été déclarés dans le programme hôte. La partie INTERFACE est la seule partie de UNIT étant visible de l'extérieur; il spécifie comment un programme hôte peut communiquer avec le UNIT. Les procédures et fonctions déclarées dans INTERFACE ne sont abrégées que par le nom de la procédure ou de la fonction et les spécifications des paramètres.
La partie IMPLEMENTATION d'un UNIT
La partie IMPLEMENTATION suit immédiatement la dernière déclaration dans l'INTERFACE. L'IMPLEMENTATION commence par déclarer les étiquettes, les constantes, les types, les variables, les procédures et les fonctions qui sont privées - c'est-à-dire non accessibles au programme hôte. Ensuite, les procédures et fonctions publiques déclarées dans l'INTERFACE sont définies. IIls sont définis sans paramètres ni types de résultats de fonction, car ils ont déjà été définis dans l'INTERFACE.
La partie d'initialisation d'une UNIT
A la fin de la partie IMPLEMENTATION, suite à la dernière fonction ou procédure, il y a la partie initialisation. Il s'agit d'une séquence d'états précédée de BEGIN et terminée par END. Le code résultant s'exécute automatiquement lorsque le programme hôte est exécuté, avant que le programme hôte ne soit exécuté. Il peut être utilisé pour effectuer toutes les préparations nécessaires avant que les procédures et fonctions de UNIT puissent être utilisées. Par exemple, la partie d'initialisation de l'unité TRANSCEND dans SYSTEM.LIBRARY génère une table de valeurs trigonométriques à utiliser par les fonctions transcendantales. Si vous ne voulez pas qu'une initialisation ait lieu, vous devez quand même avoir le END suivie d'un point.
Un exemple de UNIT
Esquissons un UNIT intrinsèque imaginaire ayant besoin d'un segment DATA, pour démontrer les informations données ci-dessus.
- (*$S+*) (* L'échange est nécessaire pour compiler les UNIT *)
-
- UNIT LAPIN; INTRINSIC CODE 25 DATA 26;
-
- INTERFACE (* Ce truc est public *)
-
- CONST TAILLEVOLE = 10;
- TYPE COULEURTYPE = (BLANC,NOIR,GRIS);
- VAR LAPINNOM:STRING[20]; (* Aura besoin d'un segment de données *)
-
- PROCEDURE SAUTE(DIST:INTEGER);
- FUNCTION RACE:INTEGER;
-
- IMPLEMENTATION (* Ce truc est privé *)
-
- CONST PI = 3.14159;
-
- TYPE ETC = 0..13;
-
- VAR LAPINLOC:INTEGER;
-
- PROCEDURE SAUTE; (* Remarque: pas de paramètres ici *)
- BEGIN
- LAPINLOC := LAPINLOC + DIST
- END;
-
- FUNCTION RACE;
- BEGIN
- (* Définition de la fonction ici *)
- END;
-
- (* Plus de procédures et de fonctions ici *)
- BEGIN
- (* Le code d'initialisation, le cas échéant, va ici *)
- END.
Les variables de type FILE doivent être déclarées dans la partie INTERFACE d'un UNIT. Un FILE déclaré dans la partie IMPLEMENTATION provoquera une erreur de syntaxe lors de la compilation.
Utilisation de l'unité exemple
Le UNIT ci-dessus, correctement complétée, serait alors compilée. Ensuite, le UNIT serait installée dans SYSTEM.LIBRARY, à l'aide de l'utilitaire LIBRARY. Une fois dans la bibliothèque, le UNIT pourrait alors être utilisée par n'importe quel programme hôte Pascal. Un exemple de programme pour utiliser notre UNIT est esquissé ci-dessous :
Un programme doit indiquer les UNIT qu'utilise USES avant la partie déclaration du programme; les procédures et fonctions ne peuvent pas contenir leurs propres déclarations USES. Lors de l'occurrence d'une déclaration USES, le compilateur fait référence à la partie INTERFACE du UNIT comme si elle faisait partie du texte hôte lui-même. Par conséquent, toutes les constantes, types, variables, fonctions et procédures définies publiquement dans UNIT sont globales. Des conflits de nom peuvent survenir si l'utilisateur défie un identifiant ayant déjà été déclaré publiquement par UNIT. Si le UNIT n'est pas dans le SYSTEM.LIBRARY, la déclaration USES doit être précédée d'une option "use library" pour indiquer au compilateur quel fichier de bibliothèque contient UNIT.
Immobilisation de UNIT
Un UNIT peut également USES une autre UNIT, auquel cas la déclaration USES doit apparaître au début de la partie INTERFACE. Par exemple, notre UNIT LAPIN peut utiliser les graphiques UNIT TURTLEGRAPHICS :
Lorsque vous utiliserez ultérieurement une telle UNIT, votre programme hôte doit déclarer qu'il USES d'abord l'unité imbriquée la plus profonde :
Il y a une limitation : un UNIT intrinsèque ne peut pas utiliser un UNIT régulière.
Changement d'un UNIT ou de son programme hôte
À des fins de test, vous pouvez définir un droit UNIT régulier dans le programme hôte, après l'entête du programme hôte. Dans ce cas, vous compilerez l'unité et le programme hôte ensemble. Toute modification ultérieure du UNIT ou du programme hôte nécessite que vous recompiliez les deux. Normalement, vous définirez et compilerez séparément un UNIT régulière et vous l'utiliserez comme fichier de bibliothèque (ou l'entreposez dans une autre bibliothèque à l'aide de l'utilitaire LIBRARY). Après avoir compilé un programme hôte utilisant une telle UNIT, vous devez lier ce UNIT au fichier de code du programme hôte en exécutant l'éditeur de liens. Essayer Run fichier de code non lié entraînera l'exécution automatique de l'éditeur de liens, à la recherche du UNIT dans la bibliothèque système. Si vous essayez de eXécuter un fichier non lié, le système vous rappelle de lier le fichier.
Les modifications apportées au programme hôte nécessitent que vous recompiliez le programme hôte. Vous devez également relier à nouveau l'unité UNIT, si elle n'est pas intrinsèque. Les modifications apportées à un UNIT standard vous obligent à recompiler l'unité, puis à recompiler et à relier tous les programmes hôtes (ou autres UNIT) utilisant ce UNIT. Les UNIT intrinsèques et leurs programmes hôtes peuvent être modifiés, mais il n'est pas nécessaire de les relier à nouveau.
Procédures et fonctions EXTERNAL
Les procédures EXTERNAL (.PROC) sont des procédures assemblées séparément, souvent entreposées dans un fichier de bibliothèque. Les programmes hôtes nécessitant des procédures EXTERNAL doivent les avoir liés dans le fichier de code compilé. Un programme hôte déclare qu'une procédure (ou une fonction) est EXTERNAL de la même manière qu'une procédure est déclarée FORWARD. Un entête standard est fourni, suivi du mot-clef EXTERNAL :
Il existe une règle spéciale pour l'entête d'une procédure ou d'une fonction EXTERNAL : Un paramètre VAR peut être déclaré sans aucun type. Les appels à la procédure EXTERNAL utilisent la syntaxe Pascal standard, et le compilateur vérifie que les appels à EXTERNAL correspondent au type et au nombre de paramètres avec la déclaration EXTERNAL. Il est de la responsabilité de l'utilisateur de s'assurer que la procédure en langage assembleur respecte la déclaration Pascal EXTERNAL. L'éditeur de liens vérifie uniquement que le nombre de mots de paramètres concorde entre les déclarations Pascal et en langage de programmation assembleur.
Les conventions du système environnant concernant l'utilisation des registres et les séquences d'appel doivent être respectées par les rédacteurs de routines en langage de programmation assembleur.
Sur le micro-ordinateur Apple, tous les registres sont disponibles et les emplacements hexadécimaux de page zéro de 0 à 35 sont disponibles en tant que variables temporaires. Cependant, le système Apple Pascal utilise également ces emplacements comme temporaires, vous ne devez donc pas vous attendre à ce que les données qui y restent soient présentes lorsque vous exécutez la routine la prochaine fois. Vous pouvez enregistrer des variables dans une mémoire de page différente de zéro en utilisant les directives .BYTE ou .WORD dans votre programme pour réserver de l'espace. Pour les fonctions de langage de programmation assembleur (.FUNC), la séquence est essentiellement la même, sauf que :
- Deux mots de zéros sont poussés par le compilateur après que tous les paramètres sont mis sur la pile.
- Une fois que la pile a été complètement nettoyée à l'heure de sortie de la routine, le .FUNC doit pousser le résultat de la fonction sur la pile.
La routine EXTERNAL dans cet exemple est liée manuellement au programme appelant.