La machine virtuelle MaP Le but de ce document est de fournir au lecteur une vue très simple du fonctionnement d’un ordinateur au niveau de sa programmation en langage machine. Il s’agit d’un outil pédagogique n’ayant aucunément l’ambition de se substituer à un produit professionnel du marché. L’avantage du produit décrit dans ce document est: d’être gratuit ; • de pouvoir être utilisé sur tout PC équipé d’un 80386 au moins; • d’être limité dans sa complexité afin justement de pouvoir être utilisé dans un contexte d’enseignement. • © Nino Silverio (octobre 1999) 1 La machine virtuelle MaP 1 L’architecture de la machine MaP Le cœur de la machine virtuelle MaP est un processeur orienté pile (stack machine). Outre le processeur proprement dit, la machine contient de la mémoire centrale subdivisée en un segment de code et un segment de données. Le processeur est doté de trois registres spéciaux: un compteur ordinal PC (program counter) pointant sur l’instruction à exécuter ; • un pointeur de pile SP (stack pointer) contenant l’adresse du sommet de la pile, c’est-à-dire l’adresse du dernier mot mémoire occupé dans le segment de données ; • le registre de base BP (base pointer) permettant d’accéder à des mots mémoire du segment de données via un déplacement relatif au contenu de ce registre. • processeur MaP PC segment de code BP SP segment de données Le segment de code et le segment de données sont physiquement séparés. L’adresse de base de chacun des segments est 0. La fonction du segment de code est de contenir le code machine que le processeur doit exécuter. Le segment de données sert à contenir les données que le programme contenu dans le segment de code doit traiter ainsi que les résultats que ce programme produit. Il est aussi l’endroit utilisé par le processeur pour stocker des données temporaires. Le programmeur peut utiliser le segment de données comme il l’entend, mais généralement, il a intérêt, afin d’éviter tout conflit, de réserver l’espace nécessaire au stockage des données et des résultats fixes (alloués pour toute la durée de l’exécution du programme) en un bloc 2 © Nino Silverio Le jeu d’instructions de la machine MaP contigu à partir de l’adresse 0. Le segment de données aura ainsi la structure usuelle suivante : valeurs temporaires données et résultats fixes du programme Structure du segment de données Ainsi, immédiatement après le dernier mot occupé par les données et résultats débute ce que l’on appelle la pile d’exécution qui peut s’étendre jusqu’à la fin du segment de données. Elle contiendra toutes les données temporaires nécessaires à la bonne exécution du programme. Cet espace mémoire croîtra et décroîtra au fur et à mesure de l’exécution du programme. 2 Le jeu d’instructions de la machine MaP Le processeur MaP est capable de traiter trois types différents de données : les nombres entiers (int) ; • les nombres décimaux (numeric) ; • les caractères (char). • La taille d’un mot mémoire du segment de données est tel qu’il peut contenir un nombre entier ou un caractère. Un nombre décimal nécessite deux mots mémoire du segment de données. La taille d’un mot mémoire du segment de code est la moitié de celle d’un mot mémoire du segment de données. Dans l’implémentation proposée, un entier (int) peut prendre une valeur allant de –2147483648 à +2147 483 647. © Nino Silverio 3 La machine virtuelle MaP Les caractères (char) sont représentés de manière interne en se basant sur la codification ASCII. Un numérique (numeric) peut prendre une valeur comprise entre 2.2 ⋅ 10 – 308 et 1.8 ⋅ 10 308 . Le processeur reconnaît 30 instructions de base parmi 58 instructions possibles. En effet, la plupart des instructions existent en deux ou trois versions suivant le type des données manipulées (int, numeric, char). Les instructions peuvent être classées de la manière suivante : • • • • • • opérations arithmétiques : ADD, SUB, MULT, DIV, NEG opérations logiques : AND, OR, NOT, EQ, GT, LS instructions de rupture de séquence : CALL, RET, JF, JP instructions de manipulation de la pile : DECR, INCR, DUPL, ASSGN, LOAD, PAIBP, PUSH, STORE instructions d’entrée-sortie : IN, OUT, OUTSTR instructions spéciales : CALLS, HALT, NEWBP, RSTRBP. Le code machine (exprimé en décimal) associé à chacune des instructions est le suivant : 4 addi 0 addn 1 and 2 assgnc 3 assgni 4 assgnn 5 call 6 calls 7 contc 8 conti 9 contn 10 decr 11 divi 12 divn 13 duplc 14 dupli 15 dupln 16 eqc 17 eqi 18 eqn 19 gtc 20 gti 21 gtn 22 halt 23 inc 24 ini 25 inn 26 incr 27 jf 28 jp 29 loadc 30 loadi 31 loadn 32 lsc 33 lsi 34 lsn 35 multi 36 multn 37 negi 38 negn 39 newbp 40 not 41 or 42 outc 43 outi 44 © Nino Silverio Le jeu d’instructions de la machine MaP outn 45 outstr 46 paibp 47 pushc 48 pushi 49 pushn 50 ret 51 rstrbp 52 storec 53 storei 54 storen 55 subi 56 subn 57 2.1 Les opérations arithmétiques Ces instructions existent pour le type entier ainsi que pour le type numérique. Ainsi, par exemple, une opération d’addition de deux entiers se fait à l’aide de l’instruction ADDI, alors qu’une instruction d’addition de deux numériques se fait avec l’instruction ADDN. L’ensemble des instructions arithmétiques s’écrit : ADDI, SUBI, MULTI, DIVI, NEGI, ADDN, SUBN, MULTN, DIVN, NEGN. Les instructions arithmétiques effectuent des opérations de calcul sur les mots mémoire situés au sommet de la pile d’exécution. Elles ne nécessitent pas d’opérandes car ceux-ci doivent se trouver sur le sommet de la pile. Le compteur ordinal est incrémenté d’une unité à la fin de chacune de ces instructions. Voici la signification de ces instructions : ADDI les deux opérandes entiers au sommet de la pile sont additionnés et remplacés par le résultat entier de l’addition. Le pointeur de pile décroît d’une unité. SUBI l’opérande entier au sommet de la pile est soustrait de l’opérande juste en dessous du sommet. Ces deux opérandes sont remplacés par le résultat entier de la soustraction. Le pointeur de pile décroît d’une unité. MULTI les deux opérandes entiers du sommet de la pile sont multipliés et remplacés par le résultat entier de la multiplication. Le pointeur de pile décroît d’une unité. DIVI l’opérande entier juste en dessous du sommet de la pile est divisé par l’opérande entier du sommet de la pile. Ces deux opérandes sont remplacés par le résultat entier de la division. Le pointeur de pile décroît d’une unité. Dans le cas où le diviseur vaut 0, une erreur est détectée et l’exécution est arrêtée. © Nino Silverio 5 La machine virtuelle MaP NEGI (NEGate) le signe de l’opérande entier au sommet de la pile est inversé. Le pointeur de pile reste inchangé. ADDN les deux opérandes numériques au sommet de la pile sont additionnés et remplacés par le résultat numérique de l’addition. Le pointeur de pile décroît de deux. SUBN l’opérande numérique au sommet de la pile est soustrait de l’opérande juste en dessous du sommet. Ces deux opérandes sont remplacés par le résultat numérique de la soustraction. Le pointeur de pile décroît de deux. MULTNles deux opérandes numériques du sommet de la pile sont multipliés et remplacés par le résultat numérique de la multiplication. Le pointeur de pile décroît de deux. DIVN l’opérande numérique juste en dessous du sommet de la pile est divisé par l’opérande numérique du sommet de la pile. Ces deux opérandes sont remplacés par le résultat numérique de la division. Le pointeur de pile décroît de deux. Dans le cas où le diviseur vaut 0, une erreur est détectée et l’exécution est arrêtée. NEGN (NEGate) le signe de l’opérande numérique au sommet de la pile est inversé. Le pointeur de pile reste inchangé. 2.2 Les opérations logiques Les instructions AND, OR et NOT ne sont pas typées. AND et OR fonctionnent avec deux opérandes qui sont les deux mots mémoire du sommet de la pile, NOT ne prend en compte que le mot mémoire du sommet de la pile (un seul opérande). Pour toutes ces opérations logiques, le compteur ordinal PC est avancé d’une unité. AND une opération logique et est effectuée avec les deux opérandes du sommet de la pile. Le résultat de cette opération remplace les deux opérandes au sommet de la pile. Le pointeur de pile décroît d’une unité. opérande 1 0 0 x x opérande 2 0 x 0 x résultat 0 0 0 1 Dans ce tableau, x représente une valeur quelconque différente de 0. 6 © Nino Silverio Le jeu d’instructions de la machine MaP OR une opération logique ou est effectuée avec les deux opérandes du sommet de la pile. Le résultat de cette opération remplace les deux opérandes au sommet de la pile. Le pointeur de pile décroît d’une unité. opérande 1 0 0 x x opérande 2 0 x 0 x résultat 0 1 1 1 Dans ce tableau, x représente une valeur quelconque différente de 0. NOT une opération logique non est effectuée sur l’opérande au sommet de la pile. Ceci signifie que si le sommet de la pile a comme valeur faux, celle-ci devient vrai et inversement. La valeur 0 est interprétée comme faux, toutes les autres valeurs comme vrai. Le pointeur de pile reste inchangé. Les autres instructions logiques EQ, GT et LS par contre existent pour chacun des trois types reconnus par la machine. Voici leurs significations respectives : EQC (EQual) les deux caractères au sommet de la pile sont dépilés et s’ils sont égaux, la valeur logique vrai 1 est placée sur le sommet de la pile, autrement c’est la valeur logique faux 0. Le pointeur de pile décroît d’une unité. LSC (LesS) les deux caractères au sommet de la pile sont dépilés et si l’opérande juste en dessous du sommet est strictement inférieur à l’opérande du sommet, la valeur logique vrai 1 est placée sur le sommet de la pile, autrement c’est la valeur logique faux 0. Le pointeur de pile décroît d’une unité. GTC (Greater Than) les deux caractères au sommet de la pile sont dépilés et si l’opérande juste en dessous du sommet est strictement supérieur à l’opérande du sommet, la valeur logique vrai 1 est placée sur le sommet de la pile, autrement c’est la valeur logique faux 0. Le pointeur de pile décroît d’une unité. EQI (EQual) les deux opérandes entiers au sommet de la pile sont dépilés et s’ils sont égaux, la valeur logique vrai 1 est placée sur le sommet de la pile, autrement c’est la valeur logique faux 0. Le pointeur de pile décroît d’une unité. © Nino Silverio 7 La machine virtuelle MaP LSI (LesS) les deux opérandes entiers au sommet de la pile sont dépilés et si l’opérande juste en dessous du sommet est strictement inférieur à l’opérande du sommet, la valeur logique vrai 1 est placée sur le sommet de la pile, autrement c’est la valeur logique faux 0. Le pointeur de pile décroît d’une unité. GTI (Greater Than) les deux opérandes entiers au sommet de la pile sont dépilés et si l’opérande juste en dessous du sommet est strictement supérieur à l’opérande du sommet, la valeur logique vrai 1 est placée sur le sommet de la pile, autrement c’est la valeur logique faux 0. Le pointeur de pile décroît d’une unité. EQN (EQual) les deux opérandes numériques au sommet de la pile sont dépilés et s’ils sont égaux, la valeur logique vrai 1 est placée sur le sommet de la pile, autrement c’est la valeur logique faux 0. Le pointeur de pile décroît de trois unités. LSN (LesS) les deux opérandes numériques au sommet de la pile sont dépilés et si l’opérande juste en dessous du sommet est strictement inférieur à l’opérande du sommet, la valeur logique vrai 1 est placée sur le sommet de la pile, autrement c’est la valeur logique faux 0. Le pointeur de pile décroît de trois unités. GTN (Greater Than) les deux opérandes numériques au sommet de la pile sont dépilés et si l’opérande juste en dessous du sommet est strictement supérieur à l’opérande du sommet, la valeur logique vrai 1 est placée sur le sommet de la pile, autrement c’est la valeur logique faux 0. Le pointeur de pile décroît de trois unités. 2.3 Les instructions de rupture de séquence Les instructions de la machine MaP sont exécutées par défaut en séquence, dans leur ordre d’apparition. A l’aide des instructions CALL, RET, JF et JP, le programmeur peut changer cet ordre afin de refléter la logique de traitement. Les instructions CALL et RET permettent de mettre en œuvre le concept de sous-programme. JP provoque un branchement inconditionnel alors que JF effectue un branchement en fonction de la valeur présente sur le sommet de la pile. JP op (JumP) effectue un branchement inconditionnel à l’adresse indiquée par l’opérande. Le compteur ordinal aura comme nouvelle valeur la valeur de l’opérande. Le pointeur de pile n’est pas affecté. 8 © Nino Silverio Le jeu d’instructions de la machine MaP JF op (Jump if False) effectue un branchement à l’adresse indiquée par l’opérande entier sous condition que la valeur logique faux (0) se trouve sur le sommet de la pile. Dans ce cas, le compteur ordinal aura comme nouvelle valeur la valeur de l’opérande, autrement, si cette condition n’est pas vérifiée, le compteur ordinal est simplement incrémenté de 3 (on passe à l’instruction suivante). Dans tous les cas, la valeur logique (le sommet de la pile) est dépilée et, par conséquent, le pointeur de pile est décrémenté d’une unité. CALL op l’adresse de retour (PC+3) est placée sur le sommet de la pile d’exécution, puis il y a un branchement inconditionnel dans le segment de code à l’adresse indiquée par l’opérande entier. Le pointeur de pile est incrémenté de 1 et le compteur ordinal a comme nouvelle valeur la valeur de l’opérande. RET le sommet de la pile est dépilé et cette valeur est affectée au compteur ordinal. Le pointeur de pile est décrémenté de 1 et le compteur ordinal a comme nouvelle valeur la valeur qui se trouvait sur le sommet de la pile. 2.4 Les instructions de manipulation de la pile Ces instructions peuvent être classées en deux catégories : les instructions de manipulation du pointeur de pile ; • les instructions modifiant le contenu de la pile et par extension le contenu du segment de données. • Les instructions DECR et INCR permettent au programmeur de changer la valeur du registre SP. DUPL, CONT, LOAD, PAIBP, PUSH sont des instructions modifiant le sommet de la pile tandis que ASSGN et STORE permettent de modifier n’importe quel mot mémoire du segment de données. DECR op le pointeur de pile SP est décrémenté de la valeur de l’opérande entier. Le compteur ordinal est incrémenté de 3. INCR op le pointeur de pile SP est incrémenté de la valeur de l’opérande entier. Le compteur ordinal est incrémenté de 3. Avant l’exécution de la première instruction d’un programme, SP vaut -1. DUPLC le caractère au sommet de la pile est dupliqué et est placé sur le sommet de la pile. Le pointeur de pile et le compteur ordinal sont incrémentés de 1. © Nino Silverio 9 La machine virtuelle MaP DUPLI l’entier au sommet de la pile est dupliqué et est placé sur le sommet de la pile. Le pointeur de pile et le compteur ordinal sont incrémentés de 1. DUPLN la valeur numérique au sommet de la pile est dupliquée et est placé sur le sommet de la pile. Le pointeur de pile est incrémenté de 2 et le compteur ordinal est incrémenté de 1. CONTC le sommet de la pile est remplacé par un caractère conservé à l’adresse indiquée par l’entier situé sur le sommet de la pile. Avant d’exécuter cette instruction, le sommet de la pile doit donc contenir l’adresse d’un mot mémoire contenant un caractère. Le compteur ordinal est incrémenté de 1, le pointeur de pile reste inchangé. CONTI le sommet de la pile est remplacé par un entier conservé à l’adresse indiquée par l’entier situé sur le sommet de la pile. Avant d’exécuter cette instruction, le sommet de la pile doit donc contenir l’adresse d’un mot mémoire contenant un entier. Le compteur ordinal est incrémenté de 1, le pointeur de pile reste inchangé. CONTNle sommet de la pile est remplacé par un numérique conservé à l’adresse indiquée par l’entier situé sur le sommet de la pile. Avant d’exécuter cette instruction, le sommet de la pile doit donc contenir l’adresse d’un mot mémoire contenant un numérique. Le compteur ordinal et le pointeur de pile sont incrémentés de 1. LOADC op le sommet de la pile est remplacé par op caractères contigus conservés dans le segment de données et dont l’adresse de départ est indiquée par le sommet de la pile. C’est l’opérande op qui fixe le nombre de caractères à copier et le sommet de la pile doit contenir l’adresse du premier caractère. C’est en fait une instruction CONTC généralisée. Le pointeur de pile est incrémenté de op–1 unités. Le compteur ordinal est incrémenté de 3. LOADI op le sommet de la pile est remplacé par op entiers contigus conservés dans le segment de données et dont l’adresse de départ est indiquée par le sommet de la pile. C’est l’opérande op qui fixe le nombre d’entiers à copier et le sommet de la pile doit contenir l’adresse du premier entier. C’est en fait une instruction CONTI généralisée. Le pointeur de pile est incrémenté de op–1 unités. Le compteur ordinal est incrémenté de 3. LOADN op le sommet de la pile est remplacé par op numériques contigus conservés dans le segment de données et dont l’adresse de départ est indiquée par le sommet de la pile. C’est l’opérande op qui 10 © Nino Silverio Le jeu d’instructions de la machine MaP fixe le nombre de numériques à copier et le sommet de la pile doit contenir l’adresse du premier numérique. C’est en fait une instruction CONTN généralisée. Le pointeur de pile est incrémenté de 2op – 1 unités. Le compteur ordinal est incrémenté de 3. PUSHC op l’opérande op de type caractère est placé sur le sommet de la pile. Le pointeur de pile croît d’une unité. Le compteur ordinal est incrémenté de 2. PUSHI op l’opérande op de type entier est placé sur le sommet de la pile. Le pointeur de pile croît d’une unité. Le compteur ordinal est incrémenté de 3. PUSHN op l’opérande op de type numérique est placé sur le sommet de la pile. Le pointeur de pile croît de 2. Le compteur ordinal est incrémenté de 5. PAIBP op1 op2 (Push Address Indexed by BP) op1 de type entier désigne le niveau d’imbrication de la variable référencée par op2. L’opérande op2 indique un déplacement entier qui est ajouté à la valeur du registre de base BP du niveau d’imbrication op1. Cette adresse est chargée sur le sommet de la pile. Le pointeur de pile croît d’une unité. Le compteur ordinal est incrémenté de 5. Ceci est une instruction spécialisée servant à implémenter l’accès aux identificateurs dans les langages de haut niveau supportant les structures de blocs. ASSGNC le sommet de la pile contient une valeur de type caractère elle-même empilée sur une adresse d’un mot-mémoire du segment de données. L’effet de cette instruction est d’affecter la valeur située sur le sommet à l’adresse placée juste en-dessous de cette valeur. Par après, cette valeur et cette adresse sont dépilées (SP diminue de 2) et le compteur ordinal est incrémenté de 1. ASSGNI le sommet de la pile contient une valeur de type entier elle-même empilée sur une adresse d’un mot-mémoire du segment de données. L’effet de cette instruction est d’affecter la valeur située sur le sommet à l’adresse placée juste en-dessous de cette valeur. Par après, cette valeur et cette adresse sont dépilées (SP diminue de 2) et le compteur ordinal est incrémenté de 1. ASSGNN le sommet de la pile contient une valeur de type numérique elle-même empilée sur une adresse d’un mot-mémoire du segment de données. L’effet de cette instruction est d’affecter la valeur située sur le sommet à l’adresse placée juste en-dessous de © Nino Silverio 11 La machine virtuelle MaP cette valeur. Par après, cette valeur et cette adresse sont dépilées (SP diminue de 3) et le compteur ordinal est incrémenté de 1. STOREC op est une instruction ASSGNC généralisée. L’opérande op indique le nombre de caractères qui doivent être enlevés du sommet de la pile et affectés dans une zone mémoire dont l’adresse de début est le mot mémoire immédiatement en dessous des caractères à transférer. Cette adresse ainsi que les valeurs à transférer sont dépilées. Le pointeur de pile est décrémenté de op+1 unités. Le compteur ordinal est incrémenté de 3. STOREI op est une instruction ASSGNI généralisée. L’opérande op indique le nombre d’entiers qui doivent être enlevés du sommet de la pile et affectés dans une zone mémoire dont l’adresse de début est le mot mémoire immédiatement en dessous des entiers à transférer. Cette adresse ainsi que les valeurs à transférer sont dépilées. Le pointeur de pile est décrémenté de op+1 unités. Le compteur ordinal est incrémenté de 3. STOREN op est une instruction ASSGNN généralisée. L’opérande op indique le nombre de numériques qui doivent être enlevés du sommet de la pile et affectés dans une zone mémoire dont l’adresse de début est le mot mémoire immédiatement en dessous des numériques à transférer. Cette adresse ainsi que les valeurs à transférer sont dépilées. Le pointeur de pile est décrémenté de 2op + 1 unités. Le compteur ordinal est incrémenté de 3. 2.5 Les instructions d’entrée-sortie Les instructions d’entrée-sortie permettent à la machine virtuelle de communiquer avec le monde extérieur. Trois instructions de base, déclinées selon le type des données (IN, OUT) sont prévues à cet effet : INC le processeur attend que l’utilisateur ait entré un caractère au clavier de l’ordinateur. Celui-ci est affecté à l’adresse indiquée par l’entier se trouvant sur le sommet de la pile. Ensuite cette adresse est dépilée. Le pointeur de pile est décrémenté de 1 tandis que le compteur ordinal est incrémenté de 1. INI le processeur attend que l’utilisateur ait entré un entier au clavier de l’ordinateur. Celui-ci est affecté à l’adresse indiquée par l’entier se trouvant sur le sommet de la pile. Ensuite cette adresse est dépilée. Le pointeur de pile est décrémenté de 1 tandis que le compteur ordinal est incrémenté de 1. 12 © Nino Silverio Le jeu d’instructions de la machine MaP INN le processeur attend que l’utilisateur ait entré un nombre décimal (numérique) au clavier de l’ordinateur. Celui-ci est affecté à l’adresse indiquée par l’entier se trouvant sur le sommet de la pile. Ensuite cette adresse est dépilée. Le pointeur de pile est décrémenté de 1 tandis que le compteur ordinal est incrémenté de 1. OUTC le sommet de la pile est interprété comme un caractère et il est affiché à l’écran à l’endroit du curseur. Ensuite cette valeur est dépilée. Le pointeur de pile est décrémenté de 1 tandis que le compteur ordinal est incrémenté de 1. OUTI le sommet de la pile est interprété comme un entier et il est affiché à l’écran à l’endroit du curseur. Ensuite cette valeur est dépilée. Le pointeur de pile est décrémenté de 1 tandis que le compteur ordinal est incrémenté de 1. OUTN le sommet de la pile est interprété comme un numérique et il est affiché à l’écran à l’endroit du curseur. Ensuite cette valeur est dépilée. Le pointeur de pile est décrémenté de 2 tandis que le compteur ordinal est incrémenté de 1. Par défaut, un tel nombre est affiché sur 8 positions, dont 2 derrière le point décimal. OUTSTR chaîne tous les mots mémoire qui suivent cette instruction dans le segment de code jusqu’au caractère ASCII 0 forment une chaîne de caractères qui est affichée à l’écran à l’endroit du curseur. Le pointeur de pile n’est pas affecté. Le compteur ordinal est incrémenté de 1 en plus du nombre de mots mémoire contenant les caractères formant la chaîne, le caractère ASCII 0 compris. 2.6 Les instructions spéciales CALLS op cette instruction permet d’effectuer un « appel système». L’opérande entier indique l’appel système qui doit être effectué. Les appels système possibles sont : 1 fixe le nombre de positions pour afficher un nombre (<I>,) 2 fixe le nombre de chiffres décimaux pour afficher un nombre (<I>,) 3 fonction carré (<N>,<N>) 4 fonction racine carrée (<N>,<N>) 5 fonction sinus (<N>,<N>) © Nino Silverio 13 La machine virtuelle MaP 6 fonction cosinus (<N>,<N>) 7 fonction tangente (<N>,<N>) 8 fonction arc sinus (<N>,<N>) 9 fonction arc cosinus (<N>,<N>) 10 fonction arc tangente (<N>,<N>) 11 renvoie un entier aléatoire compris entre 0 et 32767 inclus (,<I>) 12 calcule la puissance N3 = N1 N2 (<N1><N2>,<N3>) 13 initialise le générateur de nombres aléatoires (<I>,) 14 (,<I>) renvoie le nombre de secondes écoulées depuis le 1.1.1970 15 fonction valeur absolue (<N>,<N>) 16 fonction exponentielle (<N>,<N>) 17 fonction logarithme naturel (<N>,<N>) 18 indique si un nombre est impair ou non (<N>,<I>) 19 renvoie le nombre tronqué (sans partie fractionnaire) (<N>,<N>) 20 renvoie le nombre arrondi (<N>,<N>) 21 renvoie un caractère correspondant au code ASCII donné comme argument (<I>,<C>) 22 retourne le code ASCII correspondant à l’argument fourni (<C>,<I>) 23 renvoie l’argument entier transformé en numérique (<I>,<N>) 24 renvoie l’argument numérique transformé en entier (<N>,<I>) 14 © Nino Silverio L’assembleur MaP 25 renvoie le modulo (<I><I>,<I>) 26 renvoie le modulo (<N><N>,<N>) 27 renvoie la puissance I3 = I1I2 (<I1><I2>,<I3>) Dans cette description, l’expression entre parenthèses formalise le nombre et le type des arguments en entrée et en sortie. Le type d’un argument est placé entre «<>». I désigne un entier, N un numérique et C un caractère. Les arguments éventuels en entrée (avant la virgule) précédent le résultat de la fonction (après la virgule). Excepté pour les fonctions 11 et 14, au moment de l’appel, le sommet de la pile d’exécution doit contenir les paramètres utiles qui sont détruits à la fin de l’appel. Dans tous les cas, le compteur ordinal est incrémenté de 3. A la fin de l’exécution de la fonction de l’appel système, la valeur renvoyée éventuellement se trouve sur le sommet de la pile. RSTRBP (ReSToReBP) le sommet de la pile est dépilé et cette valeur est affectée au registre de base BP. Le pointeur de pile est décrémenté de deux tandis que le compteur ordinal est incrémenté d’une unité. NEWBP op l’opérande entier désigne le niveau d’imbrication permettant d’atteindre un BP préalablement sauvé. Le lien dynamique est empilé avant le lien statique. Le pointeur de pile est incrémenté de 1, le compteur ordinal est augmenté de 2. HALT provoque l’arrêt de la machine virtuelle et redonne le contrôle au système d’exploitation hôte. 3 L’assembleur MaP 3.1 Fonctionnement du système MaP Le lecteur pourra réaliser des programmes écrits en langage MaP et les exécuter sur son ordinateur personnel. Pour cela, il a à sa disposition un programme capable d’exécuter les instructions MaP. Ce programme s’appelle RUN. Lorsque ce dernier est exécuté sans argument, il produit l’affichage suivant 1 : Runtime MaP v3.0 Copyright N. Silverio CU/CRP-CU (7/10/99) Format : run nom_de_fichier[.int] [-i] © Nino Silverio 15 La machine virtuelle MaP Ceci signifie que RUN s’attend à exécuter un programme exécutable MaP. Le lecteur peut réaliser un tel programme exécutable de deux manières différentes : il crée manuellement un fichier exécutable ; • il utilise pour ce faire le programme ASSEMBLE qui génère directement le fichier exécutable. • La création manuelle d’un fichier exécutable, quoique réalisable, est néanmoins déconseillée car elle est très délicate et recèle plein de pièges. Elle ne sera pas décrite dans ce document. ASSEMBLE est un programme capable de traduire un fichier contenant des instructions MaP telles qu’elles ont été décrites dans la section précédente en un code exécutable par RUN. Le fichier contenant les instructions MaP écrites en clair, à l’aide d’un éditeur de textes, s’appelle le fichier source. Le contenu de ce fichier est encore appelé le programme source. Le programme ASSEMBLE prend en entrée un tel fichier et produit trois fichiers résultats : un fichier liste, un fichier exécutable par le programme RUN et un fichier nécessaire au déboguage du programme par un utilitaire adéquat. Le fichier liste comprend les lignes du fichier source, mais numérotées avec éventuellement un message d’erreur et, si le programmeur le souhaite, une trace du contenu du segment de code. Schématiquement, la situation est la suivante : fichier source .asm ASSEMBLE fichier liste .lis fichier exécutable .int fichier de déboguage .dbg 1. La date indiquée entre parenthèses peut être plus récente que celle indiquée dans cette trace d’exécution. 16 © Nino Silverio L’assembleur MaP Le programme ASSEMBLE, lorsqu’on l’exécute sans argument, affiche le message suivant 1 : Assembleur MaP v3.0 Copyright N. Silverio CU/CRP-CU (7/10/99) Format : assemble nom_de_fichier[.asm] Pour illustrer ce qui vient d’être dit, nous examinons un exemple de manière plus détaillée. Il s’agit en fait d’un programme calculant la somme de deux nombres entiers entrés au clavier de l’ordinateur. Ce résultat est affiché à l’écran. Voici ce programme source : {$d Ce programme calcule et affiche la somme de deux nombres entrés au clavier } n1 n2 somme equ equ equ 0 1 2 ; adresse de n1 ; adresse de n2 ; adresse de somme debut_ incr 3 ; réserver 3 mots mémoire pour n1, n2 et somme outstr "Entrez le 1er nombre " pushi n1 ; placer l'adresse de n1 sur le sommet de la pile ini ; la valeur lue au clavier est placée dans n1 outstr "Entrez le 2eme nombre " pushi n2 ; placer l'adresse de n2 sur le sommet de la pile ini ; la valeur lue au clavier est placée dans n2 ; la pile est vide pushi somme ; placer l'adresse de la variable somme sur la pile pushi n1 conti ; la valeur de la variable n1 se trouve sur la pile pushi n2 conti ; la valeur de la variable n2 se trouve sur la pile addi ; ces deux valeurs sont additionnées assgni outstr "La somme vaut : " pushi somme conti outi ; le contenu de la variable somme est affiché outstr "\n" ; passons à la ligne halt ; facultatif end debut_ Supposons que ce programme soit contenu dans le fichier somme.asm. Si on le soumet à ASSEMBLE, celui-ci produira trois fichiers, à savoir somme.lis, somme.exe et somme.dbg. Voici le contenu du fichier somme.lis : 1 {$d Ce programme calcule et affiche la somme de deux nombres entrés au clavier } 2 3 n1 equ 0 ; adresse de n1 1. La date indiquée entre parenthèses peut être plus récente que celle indiquée dans cette trace d’exécution. © Nino Silverio 17 La machine virtuelle MaP 4 n2 equ 1 ; adresse de n2 5 somme equ 2 ; adresse de somme 6 7 debut_ incr 3 ; réserver 3 mots mémoire pour n1, n2 et somme 8 9 outstr "Entrez le 1er nombre " 10 pushi n1 ; placer l'adresse de n1 sur le sommet de la pile 11 ini ; la valeur lue au clavier est placée dans n1 12 13 outstr "Entrez le 2eme nombre " 14 pushi n2 ; placer l'adresse de n2 sur le sommet de la pile 15 ini ; la valeur lue au clavier est placée dans n2 16 17 ; la pile est vide 18 19 pushi somme ; placer l'adresse de la variable somme sur la pile 20 pushi n1 21 conti ; la valeur de la variable n1 se trouve sur la pile 22 pushi n2 23 conti ; la valeur de la variable n2 se trouve sur la pile 24 addi ; ces deux valeurs sont additionnées 25 assgni 26 27 outstr "La somme vaut : " 28 pushi somme 29 conti 30 outi ; le contenu de la variable somme est affiché 31 outstr "\n" ; passons à la ligne 32 halt ; facultatif 33 end debut_ 34 Assemblage sans erreurs Code généré : 0 INCR3 3 OUTSTR"Entrez le 1er nombre " 16 PUSHI0 19 INI 20 OUTSTR"Entrez le 2eme nombre " 33 PUSHI1 36 INI 37 PUSHI2 40 PUSHI0 43 CONTI 44 PUSHI1 47 CONTI 48 ADDI 49 ASSGNI 50 OUTSTR"La somme vaut : " 60 PUSHI2 63 CONTI 64 OUTI 65 OUTSTR" 68 HALT 69 HALT L'exécution débute à l'adresse 0 18 © Nino Silverio L’assembleur MaP Pour exécuter le programme assemblé, il suffit de taper la commande : RUN somme On obtient, par exemple : Runtime MaP v3.0 Copyright N. Silverio CU/CRP-CU (7/10/99) Exécution en cours... Entrez le 1er nombre 6 Entrez le 2eme nombre -3 La somme vaut : 3 Exécution terminée Le fichier exécutable se présente ainsi : 0 27 0 0 0 3 0 46 0 69 110 116 114 101 122 32 108 101 32 49 101 114 32 110 111 109 98 114 101 32 0 0 0 49 0 0 0 0 0 25 0 46 0 69 110 116 114 101 122 32 108 101 32 50 101 109 101 32 110 111 109 98 114 101 32 0 0 49 0 0 0 1 0 25 0 49 0 0 0 2 0 49 0 0 0 0 0 9 0 49 0 0 0 1 0 9 0 0 0 4 0 46 0 76 97 32 115 111 109 109 101 32 118 97 117 116 32 58 32 0 0 49 0 0 0 2 0 9 0 44 0 46 0 10 13 0 0 23 0 23 0 Pour des raisons pédagogiques, ASSEMBLE génère un fichier exécutable qui est en fait un fichier texte contenant uniquement des nombres exprimés en décimal et séparés par des espaces. Le premier nombre décimal représente l’adresse de la première instruction à exécuter lorsque le programme en question est chargé dans le segment de code. Tous les autres nombres représentent un octet. Un mot mémoire du segment de code ou de données se compose de plusieurs octets. C’est ainsi que la taille d’un mot mémoire du segment de code est de deux octets alors qu’un mot mémoire du segment de données est composé de quatre octets. Dans l’exemple proposé ci-dessus, les six octets des trois premiers mots mémoire du segment de code ont les valeurs respectives : 27 0 0 0 3 0. Le premier mot mémoire logé à l’adresse 0 se compose donc des deux octets 27 et 0. Au début du chapitre 3, nous avons vu les codes attribués à chacune des instructions. Dans ce tableau, nous pouvons lire que le code 27 correspond à l’instruction INCR. Comment faut-il alors lire les deux octets ? Le premier est l’octet de poids faible, alors que le second est l’octet de poids fort. La valeur résultante est donc: 27 + 0 ⋅ 256 = 27 . Dans notre cas, le deuxième octet d’un mot mémoire contenant une instruction aura toujours comme valeur 0 car le nombre d’instructions est inférieur à 256 ! Si un octet a une valeur supérieure à 127, il est représenté par son complément à 256. Si par exemple, dans le code généré, nous voyons un octet de valeur -5, cela signifie qu’il représente le nombre 251 ! © Nino Silverio 19 La machine virtuelle MaP Un entier est codé sur deux mots mémoire du segment de code et un numérique sur quatre. Comme le processeur MaP a reconnu l’instruction INCR, il sait qu’elle admet un argument entier et par conséquent va lire les quatre octets suivants 0 0 3 0. Les deux premiers octets sont le mot mémoire de poids fort (valeur 65536) et les deux suivants le mot mémoire de poids faible. Chaque mot mémoire est à son tour une paire (octet poids faible, octet poids fort). Le tableau qui suit contient quelques exemples illustrant comment des nombres entiers sont codés : octets mots mémoire entier 0 et 3 3 0 0 96 –22 0 et 96 + 234 ⋅ 256 = 60000 60000 9 0 –64 39 9 et 10176 9 ⋅ 65536 + 10176 = 600000 0030 Les caractères sont représentés sous formes de nombres entiers en utilisant la codification ASCII. Chaque mot mémoire pouvant contenir deux octets, il suffit d’interpréter chacun des octets comme un caractère. Voici la signification des 11 mots mémoire suivants: 69 110 116 114 101 122 32 108 101 32 49 101 114 32 110 111 109 98 114 101 32 0 0 0 E n t r e z l e 1 e r n o m b r e Un mot mémoire incomplétement utilisé est rempli par un 0 et une chaîne de caractères doit se terminer par un mot mémoire de valeur 0. Les nombres décimaux (numériques) sont codés sur huit octets. Nous ne détaillerons pas davantage le fonctionnement de cette codification. Le programme RUN peut être exécuté avec l’option «-i» qui a pour effet d’indiquer à la fin de l’exécution l’état des trois registres PC, BP et SP, l’espace mémoire occupé par le programme dans le segment de code ainsi que l’espace mémoire maximal utilisé dans le segment de données. run test -i Runtime MaP v3.0 Copyright N. Silverio CU/CRP-CU (7/10/99) Exécution en cours... Entrez le 1er nombre 5 Entrez le 2eme nombre 8 La somme vaut : 13 Exécution terminée sp(2) pc(68) bp(0) code (70/20000) données (5/14000) 20 © Nino Silverio L’assembleur MaP 3.2 Syntaxe du programme ASSEMBLE Le programme ASSEMBLE est ce que l’on appelle dans le jargon informatique un assembleur. Il s’agit d’un programme dans lequel chaque instruction représente effectivement une instruction machine. Cette instruction est alors écrite par le programmeur à l’aide d’un code mnémonique lui permettant de mieux lire son programme. Dans un langage plus évolué (Pascal, C, etc.) chaque instruction est traduite en un bloc d’instructions machine, de quelques unes à plusieurs dizaines voire des centaines ! En utilisant un assembleur, le programmeur a généralement la facilité de pouvoir travailler avec des adresses et noms symboliques et de pouvoir utiliser des pseudo-instructions. Une ligne d’un programme assembleur MaP débute par : une étiquette facultative, suivie éventuellement du code mnémonique d’une instruction ou de la pseudo-instruction END avec leurs opérandes éventuels; • un identificateur suivi de la pseudo-instruction EQU et de la valeur associée; • le code mnémonique d’une instruction ou de la pseudo-instruction END avec leurs opérandes éventuels. • Une étiquette sert à désigner de manière symbolique une adresse mémoire. Elle doit débuter par une lettre, suivie de caractères alphanumériques ou du symbole «_». Une étiquette doit se terminer par le symbole «_». Chaque étiquette référencée dans un programme doit y être définie. Un nom symbolique sert à désigner une valeur. Il doit débuter par une lettre, suivie de caractères alphanumériques ou du symbole «_». Un symbole ne doit pas se terminer par le symbole «_». La valeur associée à un nom symbolique est définie à l’aide de la pseudoinstruction EQU. Cette valeur en question doit suivre la pseudoinstruction EQU. Exemples : nombre_lignes pi lettre_a autre_lettre_a nom EQU EQU EQU EQU EQU 25 3.1415 ’A’ ’\65’ "Nino Silverio" La virgule décimale est représentée par le point. Une constante numérique dans laquelle figure un point décimal est considérée comme étant une donnée numérique. © Nino Silverio 21 La machine virtuelle MaP Une valeur de type caractère est placée entre « ’ ». Pour représenter un caractère non représentable, il suffit de concaténer son code ASCII derrière le symbole «\ ». Si la chaîne de caractères doit contenir le caractère « ’ », celui-ci doit être doublé. Une valeur de type chaîne de caractères est placée entre « " ». Tous les caractères imprimables peuvent y être utilisés. Si la chaîne de caractères doit contenir le caractère «" », celui-ci doit être doublé. Exemple : une_quote EQU """" Une instruction assembleur peut référencer un symbole défini ultérieurement dans le programme. La pseudo-instruction END indique la fin logique du programme et provoque la génération de l’instruction machine HALT. END doit être suivi d’une adresse symbolique désignant la première instruction logique du programme, c’est-à-dire l’adresse par laquelle l’exécution doit débuter. Pour augmenter la lisibilité de son programme, le programmeur doit le compléter avec des commentaires. Un commentaire est un texte que l’on place dans le fichier source pour faciliter sa compréhension. L’assembleur les élimine et ils n’ont aucune influence sur la vitesse d’exécution du programme. Avec l’assembleur MaP, les commentaires peuvent être placés : en fin de ligne précédés du symbole « ; » ; • n’importe où en espace est permis et il peuvent s’étendre du plusieurs lignes s’ils sont délimités par une paire de symboles « { } ». • Il existe encore une directive d’assemblage {$d} ou {$D} qui provoque, à la fin de l’assemblage, l’affichage du code machine généré. Ce code figurera aussi dans le fichier .lis produit. 3.3 Liste des messages d’erreur de l’assembleur MaP Voici la liste des messages d’erreur pouvant survenir en travaillant avec l’assembleur MaP : Commentaire incorrect Chaîne > MAX_CHAINE caractères Chaîne de caractères pas terminée 22 © Nino Silverio L’assembleur MaP Caractère inconnu dans la source Etiquette ambigue Etiquette(s) non définie(s) EQU manque EQU non défini Valeur de la constante absente Identificateur ambigu Identificateur inconnu Une étiquette manque Erreur de syntaxe END manque END déjà défini ! Type incorrect Code opératoire inconnu ...ne devrait pas arriver 3.4 Liste des messages d’erreur de l’interprète d’exécution Run Code généré déborde du segment de code La pile déborde du segment de données Division par 0 Code opératoire non défini 3.5 Exercices 1) Réaliser un programme en assembleur MaP qui calcule le produit de deux nombres entiers entrés au clavier. 2) Réaliser un programme en assembleur MaP qui calcule le produit de deux numériques entrés au clavier. 3) Réaliser un programme en assembleur MaP qui calcule et affiche la moyenne de trois nombres décimaux entrés au clavier de l’ordinateur. © Nino Silverio 23 La machine virtuelle MaP 4) Réaliser un programme en assembleur MaP qui affiche le prix d’un article TTC après avoir entré le prix hors TVA et le taux de TVA. 5) Expliquer en détail ce que fait le programme suivant : a EQU 0 a_la_ligne_ OUTSTR "\n" RET debut_ INCR 2 ; une variable numérique PUSHI a INN PUSHI 3 CALLS 2 PUSHI a CONTN OUTN CALL a_la_ligne_ PUSHI a CONTN PUSHN 0. LSN JF bizarre_ PUSHI a CONTN NEGN OUTN JP fin_ bizarre_ PUSHI a CONTN OUTN fin_ END debut_ 6) Expliquer en détail ce que fait le programme suivant : a EQU 0 b EQU 2 a_la_ligne_ OUTSTR "\n" RET debut_ INCR 4 ; deux variables numériques PUSHI a INN PUSHI b INN PUSHI 3 CALLS 2 PUSHI a CONTN OUTN CALL a_la_ligne_ PUSHI b CONTN OUTN CALL a_la_ligne_ PUSHI a CONTN PUSHI b CONTN LSN JF bizarre_ PUSHI b 24 © Nino Silverio L’assembleur MaP bizarre_ fin_ CONTN OUTN JP fin_ PUSHI a CONTN OUTN END debut_ © Nino Silverio 25 La machine virtuelle MaP 26 © Nino Silverio