Programmes COM et EXE
Le système d'exploitation DOS reconnaît trois types de fichiers de programmes : ceux avec les extensions de fichier .BAT, .COM et .EXE. Cette page décrit la structure et les fonctions de ces deux derniers types de programme.
Une différence entre les fichiers programme .COM et .EXE réside dans la limite de taille pour chaque type de programme. Un programme .COM ne peut pas dépasser la taille de 64 Ko. Un programme .EXE peut être aussi grand que la capacité de mémoire disponible pour DOS.
Dans un programme .COM, le code du programme, les données et la pile sont entreposés dans une partition de 64 Ko. Tous les registres de segment sont définis au début du programme et restent fixes pendant toute la durée de l'exécution du programme. Ils pointent vers le début du segment de mémoire 64 Ko. Le contenu du registre ES peut cependant être modifié, car il n'a aucun effet direct sur l'exécution du programme.
Dans un programme .EXE, le code, les données et la pile peuvent être entreposés dans différents segments et, selon la taille du programme, peuvent être répartis sur plusieurs segments. Alors qu'un fichier de programme .COM est entreposé sur le disque en tant que copie d'image de la mémoire RAM, un fichier de programme EXE est stocké dans un format spécial étant décrit brièvement.
EXEC
Les deux types de programmes peuvent être chargés et démarrés à l'aide de la fonction DOS EXEC. N'importe quel utilisateur peut y accéder, mais le processeur de commandes l'utilise pour exécuter des commandes externes. Avant que la fonction EXEC ne charge le programme en mémoire, elle réserve la mémoire RAM pour contenir le programme. Au début de cette mémoire, la fonction EXEC entrepose une structure de données PSP (Program Segment Prefix). Le programme est alors chargé immédiatement après la PSP. Les registres de segments et la pile sont initialisés et le programme prend le contrôle. Plus tard, lorsque le programme se termine, la mémoire est libérée en fonction du contenu du PSP.
Voici la structure du PSP :
Positionnement | Taille | Description |
---|---|---|
+ 00h | 2 octets | Appel de l'interruption 20h |
+ 02h | 1 mot | Adresse de segment de la mémoire allouée pour un programme |
+ 04h | 1 octet | Réservé |
+ 05h | 5 octets | Appel de l'interruption 21h |
+ 0Ah | 2 mots | Copie du vecteur d'interruption 22h |
+ 0Eh | 2 mots | Copie du vecteur d'interruption 23h |
+ 12h | 2 mots | Copie du vecteur d'interruption 24h |
+ 16h | 22 octets | Réservé |
+ 2Ch | 1 mot | Adresse de segment du bloc d'environnement |
+ 2Eh | 46 octets | Réservé |
+ 5Ch | 16 octets | FCB 1 |
+ 6Ch | 16 octets | FCB 2 |
+ 80h | 1 octet | Nombre de caractères dans la ligne de commande |
+ 81h | 127 octets | Ligne de commande (terminée par CR) |
Le PSP lui même a toujours une longueur de 256 octets et contient des informations importantes pour DOS et le programme à exécuter.
L'emplacement mémoire 00h du PSP contient un appel de fonction DOS pour terminer un programme. Cette fonction libère la mémoire du programme et rend le contrôle au processeur de commandes ou au programme appelant. L'emplacement mémoire 05h du PSP contient un appel de fonction DOS pour l'interruption 21h. Aucun de ceux-ci n'est utilisé par DOS, mais ce sont des restes du système CP/M.
L'emplacement mémoire 02H de la PSP contient l'adresse du segment jusqu'à la fin du programme. L'emplacement mémoire 0Ah contient le contenu précédent du vecteur d'interruption de fin de programme. L'emplacement mémoire 0Eh contient le contenu précédent du vecteur d'interruption Ctrl+C ou Ctrl+Break. L'emplacement de mémoire 12h contient le contenu précédent du vecteur d'interruption d'erreur critique. Pour chacun de ces emplacements mémoire, le programme change l'un des vecteurs correspondants lors de son exécution ; DOS peut utiliser le vecteur d'origine au cas où il détecterait une erreur.
L'emplacement 2Ch contient l'adresse de segment du bloc d'environnement. Le bloc d'environnement contient des informations telles que le chemin de recherche actuel et le répertoire dans lequel se trouve le processeur de commandes COMMAND.COM sur le disque.
Les emplacements de mémoire 5Ch à 6Ch contiennent un bloc de contrôle de fichier. Ce FCB n'est pas souvent utilisé par DOS car il ne prend pas en charge les fichiers hiérarchiques (chemins) et est également laissé par CP/M.
La chaîne de caractères de paramètres entrés sur la ligne de commande après le nom du programme est appelée queue de commande. La queue de commande est copiée dans le tampon de paramètres dans la PSP en commençant à l'emplacement de mémoire 81h et sa longueur est entreposée à l'emplacement de mémoire 80h. Tous les paramètres de redirection sont éliminés de la fin de la commande lors de sa copie dans le tampon de paramètres. Le programme peut examiner les paramètres dans le tampon de paramètres pour diriger son exécution.
Le tampon de paramètres est également utilisé par DOS comme zone de transfert de disque (DTA) pour transmettre des données entre l'unité de disque et la mémoire. La plupart des programmes DOS n'utilisent pas le DTA contenu dans la PSP car il s'agit d'un autre restant du CP/M.
Une comparaison des programmes .COM et .EXE en mémoire :
Programmes COM
Les fichiers de programme .COM sont entreposés sur le disque en tant que copie d'image de la mémoire. De ce fait, aucun autre traitement n'est requis lors du chargement. Par conséquent, les programmes .COM se chargent plus rapidement et démarrent l'exécution plus rapidement que les programmes .EXE.
Un programme .COM se charge immédiatement après la PSP. L'exécution commence alors au premier emplacement de mémoire suivant le PSP au déplacement 100h. Pour cette raison, un programme .COM doit commencer par une instruction exécutable, même s'il ne s'agit que d'une instruction de saut vers le début réel du programme.
Limites de la mémoire du programme COM
Comme décrit dans la section précédente, un programme .COM est limité à 64 Ko (65 536 octets) de longueur. Le PSP (256 octets) et au moins 1 mot (2 octets) pour la pile doivent être réservés à partir de ce total. Même si la longueur du programme .COM ne peut jamais dépasser 64 Ko, DOS réserve toute la RAM disponible pour un programme. Par conséquent, DOS ne peut pas allouer de mémoire supplémentaire et le programme COM ne peut pas appeler un autre programme à l'aide de la fonction EXEC. Cette limitation peut être surmontée en libérant la mémoire inutilisée pour d'autres utilisations avec une fonction DOS. Lorsque le contrôle est remis au programme .COM, tous les registres de segment pointent vers le début de la PSP. De ce fait, le début du programme .COM (relatif au début de la PSP) est toujours à l'adresse 100h. Le pointeur de pile pointe vers la fin du segment de mémoire de 64 Ko contenant le programme .COM (généralement FFFEh). Lors de chaque appel de sous-programme dans le programme .COM, la pile est ajustée de 2 octets vers la fin du programme. Le programmeur est chargé d'empêcher la pile de croître et d'écraser le programme, ce qui entraînerait son plantage.
Il existe plusieurs façons de terminer un programme .COM et de rendre le contrôle au DOS ou au programme appelant :
Si le programme s'exécute sous DOS version 1.0, il peut être terminé en appelant la fonction 0 de l'interruption 21h ou en appelant l'interruption 20h. Il peut également être terminé en utilisant l'instruction assembleur RET (RETurn). Lorsque cette instruction s'exécute, le programme continue à l'adresse se trouvant en haut de la pile. Puisque la fonction EXEC a entreposé la valeur 0 à cet emplacement avant de passer le contrôle au programme .COM, l'exécution du programme continue à l'emplacement CS:0 (le début de la PSP). Rappelons que cet emplacement contient l'appel de l'interruption 20h terminant le programme.
Les programmes s'exécutant sur des versions ultérieures à la version DOS 1.0 sont terminés à l'aide de la fonction 4Ch de l'interruption 21h. Le programme terminant peut transmettre un code de retour numérique au programme appelant. Par exemple, une valeur de 0 peut indiquer que le programme s'est exécuté avec succès, tandis qu'une valeur différente de zéro indique une erreur lors de l'exécution.
Ensuite, nous parlerons de quelques-uns des détails dont le programmeur en langage assembleur devra s'occuper lors du développement d'un programme .COM. Notez que le programmeur de langage de haut niveau est généralement isolé de ces détails par le compilateur ou l'interpréteur, vous pouvez donc sauter plus loin.
Un programme .COM est limité à une taille de 64 Ko. Le code et les données du programme doivent être contenus dans un seul segment et adressés via les procédures NEAR. Par conséquent, un programme en langage assembleur devant devenir un programme .COM ne peut contenir aucune procédure FAR.
Avant d'appeler un programme .COM, DOS réserve toute la mémoire disponible pour le programme même s'il n'utilise normalement qu'un seul segment de 64 Ko et l'indique en définissant l'emplacement mémoire 2 dans la PSP. Habituellement, le programme se termine et la mémoire est à nouveau disponible pour DOS.
Dans certaines circonstances, vous voudrez peut-être écrire un programme devant rester résident après son exécution. Mais DOS pense qu'il n'y a pas de mémoire disponible. Cela empêche le chargement et l'exécution d'autres programmes.
Dans d'autres circonstances, vous souhaiterez peut-être exécuter un autre programme à partir de ce programme .COM à l'aide de la fonction EXEC. Encore une fois, puisque DOS pense que la mémoire n'est pas disponible, il ne permettra pas au nouveau programme de s'exécuter.
Ces deux problèmes peuvent être contournés en libérant la mémoire inutilisée.
Il existe deux approches pour ce faire : libérer uniquement la mémoire en dehors du segment COM 64K ou libérer la mémoire en dehors du segment COM 64 Ko plus toute mémoire inutilisée dans le segment COM de 64 Ko. Cela crée plus de mémoire pour d'autres programmes, mais déplace la pile en dehors de la mémoire de segment COM protégée, la laissant ouverte pour être écrasée par d'autres programmes. Pour cette raison, la pile doit être déplacée à la fin du segment de code avant de libérer la mémoire. La pile doit avoir une certaine taille limite (dans la plupart des cas, 512 octets suffiront amplement).
L'exemple de programme suivant peut servir d'exemple pour développer un programme .COM. Une petite routine (init) déplace la pile à la fin du segment de code après le démarrage du programme et libère toute la mémoire restante. Même lorsque ce programme charge un autre programme, il reste résident. Cette routine peut être utile aux applications et peut faire partie de n'importe quel programme .COM.
- ; com_test.asm
- Code Segment Para 'CODE' ; Définition du segment de CODE
- Org 100h ; Débute à l'adresse 100h directement derrière la PSP
- Assume CS:code, DS:code, ES:code, SS:code
- ;Tous les points de segment dans le code
- start: JMP init ; Appel la routine d'initialisation
-
- prog proc near ; Cette procédure est actuellement le corps du programme
- MOV AX,4C00h ; Terminer le programme en appelant une fonction DOS sur le code d'erreur 0
- INT 21h
- prog endp ; Fin de la procédure PROG
- init: MOV AH,4Ah ; Changer le numéro de fonction pour la taille de la mémoire
- MOV BX,Offset endp ; Calculer le nombre de paragraphes (16 octets chacun) disponibles pour le programme
- MOV CL,4
- SHR BX, CL
- INC BX
- INT 21h
- MOV SP,Offset endp
- JMP Prog
- init end label near
- DW (256-((init_end-init) shr 1)) dup (?)
- endp equ this byte
- code ends
- End start
Vous devez d'abord assembler le programme source à l'aide de l'assembleur. Dans l'exemple suivant, nous utilisons l'assembleur Microsoft. Après l'assemblage, vous reliez ensuite le code objet à l'aide du programme LINK. Lorsque vous exécutez le programme LINK, le message suivant apparaît :
Warning: no stack segment |
Vous pouvez ignorer ce message. Si le programme ne contient aucune erreur, le programme LINK crée un fichier EXE. Étant donné que vous souhaitez développer un programme COM et non un programme EXE, vous devez exécuter le programme EXE2BIN à la dernière étape. Cela convertit les programmes EXE en programmes COM. Voici les étapes de préparation d'un programme en langage assembleur à l'aide de l'assembleur Microsoft. Le programme à assembler se nomme COM_TEST.ASM :
MASM COM_TEST: LINK COM_TEST; EXE2BIN COM_TEST.EXE COM_TEST.COM |
Si toutes les étapes ont été effectuées correctement, le programme COM_TEST.COM peut être exécuté à partir du DOS en tapant simplement COM_TEST.
Programmes EXE
Les programmes EXE ont un avantage sur les programmes COM car ils ne sont pas limités à une longueur maximale de 64 Ko pour le code, les données et la pile. L'inconvénient est la plus grande complexité de ces fichiers. Cela signifie qu'en plus du programme lui-même, d'autres informations doivent être entreposées dans un fichier EXE.
EXE vs COM
Les programmes EXE contiennent des segments séparés pour le code, les données et la pile pouvant être organisés dans n'importe quel ordre. Contrairement à un programme COM, un programme EXE se charge en mémoire à partir du disque et subit un traitement par la fonction EXEC, puis commence enfin son exécution. Cela est nécessaire en raison des limitations déjà décrites pour les programmes COM.
Les programmes EXE ne sont pas limités au chargement à un emplacement mémoire fixe, mais à n'importe quel emplacement souhaité dans la mémoire étant un multiple de 16. Puisqu'un programme EXE peut avoir plusieurs segments, cela nécessite l'utilisation d'instructions en langage machine FAR. Par exemple, un programme principal peut être dans un segment et appeler un sous-programme dans un autre segment. L'adresse de segment doit être fournie pour cette instruction FAR en plus du déplacement pour que la routine soit appelée. Le problème est que l'adresse de segment peut être différente pour chaque exécution du programme.
Les fichiers COM évitent ce problème car la taille du programme est limitée à 64 Ko, ce qui rend inutile l'utilisation des commandes FAR. Les programmes EXE résolvent ce problème de manière plus complexe : le programme LINK place une structure de données au début de chaque fichier EXE contenant, entre autres, les adresses de tous les segments. Il contient les adresses de tous les emplacements de mémoire dans lesquels l'adresse de segment d'un certain segment est entreposée pendant l'exécution du programme.
Si la fonction EXEC charge le programme EXE, elle connaît les adresses où les différents segments doivent être chargés. Il peut donc entrer ces valeurs dans les emplacements mémoire au début du fichier EXE. De ce fait, plus de temps s'écoule entre l'appel initial du programme et le moment où le programme commence réellement l'exécution que pour un programme COM. Le programme EXE occupe également plus de mémoire qu'un programme COM. L'illustration suivante montre la structure de l'entête d'un fichier EXE :
Adresse | Contenu | Taille |
---|---|---|
+00H | Identificateur de programme EXE (5A4Dh) | 1 mot |
+02H | Longueur du fichier MOD 512 | 1 mot |
+04H | Longueur du fichier DIV 512 | 1 mot |
+06H | Nombre d'adresses de segment pour le passage | 1 mot |
+08H | Taille de la tête dans les paragraphe | 1 mot |
+0AH | Nombre minimum de paragraphes supplémentaires nécessaires | 1 mot |
+0EH | Nombre maximum de paragraphes supplémentaires nécessaires | 1 mot |
+10H | Contenu du registre SP au démarrage du programme | 1 mot |
+12H | Somme de contrôle basée sur l'entête du fichier EXE | 1 mot |
+14H | Contenu du registre IP au démarrage du programme | 1 mot |
+16H | Début du segment de code dans le fichier EXE | 1 mot |
+18H | Adresse de la table de relocalisation dans le fichier EXE | 1 mot |
+1AH | Numéro de recouvrement | 1 mot |
+1CH | Mémoire tampon | 1 mot |
+??H | Adresse des adresses de segment de passage (table de relocalisation) | 1 mot |
+??H | Code de programme, données et segment de pile | 1 mot |
Une fois que les références de segment dans le programme EXE ont été résolues en adresses actuelles, la fonction EXEC définit le registre de segment DS et ES au début du PSP précédant également dans tous les programmes EXE en mémoire. De ce fait, le programme EXE peut accéder aux informations contenues dans le PSP, telles que l'adresse du bloc d'environnement et les paramètres contenus dans la ligne de commande (commande tail). L'adresse de pile et le contenu du pointeur de pile sont entreposés dans l'entête du fichier EXE et accessibles à partir de là. Ceci s'applique également à l'adresse du segment de code contenant les premières instructions du programme, et au compteur de programme. Une fois les valeurs attribuées, l'exécution du programme démarre.
Pour assurer la compatibilité avec les futures versions de DOS, un programme EXE doit se terminer en appelant la fonction 4Ch de l'interruption 21h.
Bien sûr, de la mémoire doit être disponible pour le programme EXE. Le chargeur EXE détermine la taille totale du programme en fonction de la taille des segments individuels du programme EXE. Ensuite, il peut allouer cette quantité de mémoire et de la mémoire supplémentaire immédiatement après le programme EXE. Les deux premiers champs de l'entête du fichier programme EXE contiennent la taille minimale et maximale de la mémoire requise dans les paragraphes (1 à 6 octets).
Tout d'abord, le chargeur EXE essaie de réserver le nombre maximum de paragraphes. Si ce n'est pas possible, le chargeur essaie de réserver la mémoire restante ne devant pas être inférieure au nombre minimum de paragraphes. Ces champs sont déterminés par le compilateur ou l'assembleur, et non par l'éditeur de liens. Le minimum est 0 et le maximum autorisé est FFFFh. Ce dernier nombre est irréaliste dans la plupart des cas (il totalise 1 mégaoctet) mais réserve toute la mémoire au programme EXE.
Cela nous ramène au même problème que dans les programmes COM. Les fichiers EXE constituent des programmes résidents médiocres, mais un programme EXE peut avoir besoin d'appeler un autre programme lors de son exécution. Ceci n'est possible qu'en libérant d'abord la mémoire réservée supplémentaire. Le programme suivant ci-dessous contient une routine réduisant la mémoire réservée au minimum.
Le programme utilise des segments de code, de données et de pile séparés. Il peut servir de modèle pour d'autres programmes EXE que vous pouvez écrire.
- ;exe_test.asm
- Stack Segment Para Stack ;Définition d'un segment de pile
- DW 256 DUP (1) ; La pile a 256 mots
- Stack EndS
-
- Data Segment Para 'DATA'
- Data EndS
-
- Code Segment Para 'CODE' ;Définition d'un segment de CODE
- ASSUME CS:Code, DS:Data, SS:Stack
-
- Prog Proc Far
- MOV AX,data
- MOV DS,AX
- CALL setfree
- MOV AX,4C00h ; Termine avec une appel à la fonction DOS
- INT 21h
- Prog endp
-
- setfree Proc Near
- MOV BX,SS
- MOV AX,ES
- SUB BX,AX
- MOV AX,SP
- MOV CL,4
- SHR AX,CL
- ADD BX,AX
- INC BX
- MOV AH,4Ah
- INT 21h
- RET
- setfree endp
-
- code ends
- end prog
Pour développer un programme EXE, il doit être assemblé comme un programme normal avec un assembleur. Ensuite, il est lié au programme LINK. Si le programme ne contient aucune erreur, le programme LINK crée un fichier EXE.
Voici les différentes étapes de préparation d'un programme EXE à partir de la source de langage d'assemblage nommée EXE_TEST.ASM :
MASM EXE_TEST; LINK EXE_TEST; |
Si toutes ces étapes ont été exécutées correctement, le programme EXE_TEST.EXE peut être démarré à partir du niveau DOS en tapant EXE_TEST.