L'ASSEMBLEUR microPIUP
L'assembleur microPIUP est intégré à l'archive du simulateur.
L'option à utiliser est -ass et le nom du fichier source est donné en argument :
java -jar microPIUP.jar -ass fichier_source
Le fichier exécutable produit se trouve dans le même répertoire que le fichier source et a un nom constitué du radical du nom du fichier source complété par l'extension .iup.
Ainsi la commande
java -jar microPIUP.jar -ass mesTests/test1.src
produit le fichier exécutable test1.iup dans le répertoire mesTests.
Le contenu d'un fichier source en langage d'assemblage est une suite de lignes.
Chaque ligne contient une directive, une instruction ou est réduite à un commentaire.
Un commentaire commence par // et se poursuit jusqu'à la fin de la ligne.
Les noms des registres : r0, r1, ... , r15, les noms d'instructions, les noms associés aux conditions (instructions Bcc et Jcc) et les directives sont des mots clés c'est-à-dire qu'ils ne peuvent être utilisés comme identificateurs (étiquettes). Ces mots clés peuvent s'écrire en majuscules ou en minuscules. Il n'est toutefois pas possible de mélanger majuscules et minuscules dans un même mot : word est un mot-clé, WORD aussi mais Word est un identificateur.
Les chaines de caractères littérales s'écrivent entre "
Seules les constantes entières sont utilisables (il n'y a pas d'instruction sur les nombres réels) elles s'écrivent en décimal ou en hexadécimal avec les notations habituelles. Le processeur ne manipulant que des données d'au plus 16 bits, si la valeur de la constante ne peut être codée sur 16 bits (en complément à 2) un avertissement est produit sur l'erreur standard.
Les identificateurs ("étiquettes" ou constantes nommées) ont la syntaxe habituelle. Tout identificateur représentant un emplacement (identificateur placé comme étiquette d'une instruction ou d'une directive rsw, rsb, byte, word, string) ou une valeur entière peut être utilisé avant d'être défini. Un identificateur défini comme "alias" d'un registre doit être défini avant d'être utilisé. Un identificateur ne peut avoir plusieurs définitions.
Les commentaires débutent par // et se poursuivent jusqu'à la fin de la ligne.
L'assembleur permet l'utilisation d'opérateurs arithmétiques + - * / (division entière) et % (modulo).
Le symbole $ représente la valeur courante du compteur d'emplacement de l'assembleur au début de l'assemblage de la ligne. L'exemple suivant montre quelles sont les valeurs successives du compteur d'emplacement lors de l'assemblage :
// initialement il vaut zéro org 0x1000 // ici il vaut 0x1000 word $+20 // l'expression vaut 0x1020 // ici le compteur d'emplacement vaut 0x1002 start debut // ici il vaut toujours 0x1002 debut stw r1,@$-2 // l'expression vaut 0x1000 // ici le compteur d'emplacement vaut 0x1006
Les expressions notées expression_entière et expression_d_adresse combinent la notation $ qui représente la valeur courante du compteur d'emplacement, des constantes entières et des identificateurs avec ces opérateurs et des parenthèses selon les règles habituelles. Elles ne se distinguent pas syntaxiquement mais, comme leurs noms l'indiquent, les premières représentent des nombres et les secondes des adresses.
Pour déterminer si une expression est une expression_entière ou une expression_d_adresse il faut utiliser récursivement les règles suivantes :
$ est une expression_d_adresse
un identificateur défini par une directive org, rsb, rsw, byte, word, string ou comme étiquette d'une instruction est une expression_d_adresse
un identificateur défini par une directive equ est de même type que l'expression située à droite du mot clé
une constante entière est une expression_entière
une configuration hexadécimale est une expression_entière
une expression de
la forme
expression_d_adresse +|-
expression_entière
est du type
expression_d_adresse
une expression de
la forme
expression_d_adresse *|/|%
expression_entière
est du type expression_entière
une expression de
la forme
expression_d_adresse +|-|*|/|%
expression_d_adresse
est du type expression_entière
une expression de
la forme
expression_entière +|-|*|/|%
expression_d_adresse
est du type expression_entière
une expression de la forme - expression_d_adresse est une expression_entière
une expression de la forme - expression_entière est une expression_entière
L'utilisation de chaînes de caractères ou de désignations de registres dans une expression arithmétique est interdite et provoque l'affichage d'un message d'erreur sur la sortie standard.
start expression_d_adresse
fixe l'adresse de la première instruction à exécuter.
Il y a au plus une directive start dans un texte. En son absence l'adresse de la première instruction est 0.
stackbase expression_d_adresse
détermine l'adresse de la base de la pile : au chargement du module exécutable le registre SP est initialisé avec cette adresse qui sert de borne lors de la visualisation de la pile.
Il y a au plus une directive stackbase dans un texte. En son absence l'adresse de la base de la pile est 0x1000.
identificateur org expression_entière org expression_entière
La valeur de expression_entière constitue l'adresse de chargement de l'exécutable produit et sert à initialiser le compteur d'emplacement de l'assembleur, elle doit donc pouvoir être évaluée immédiatement.
Dans la première forme la valeur de l'expression est associée à identificateur .
Cette directive ne peut figurer qu'une fois dans un fichier source et ne peut être précédée que de directives equ. En l'absence de directive org l'adresse de chargement de l'exécutable est 0.
identificateur equ expression_entière identificateur equ expression_d_adresse identificateur equ nom_de_registre_R
associe à identificateur l'expression ou le nom de registre figurant à droite du mot-clé : partout dans la suite du texte identificateur est remplacé par la valeur de l'expression ou par le nom du registre.
identificateur rsb expression_entière rsb expression_entière
réserve expression _entière octet(s) à partir de la valeur courante du compteur d'emplacement. L'expression expression_entière doit pouvoir être évaluée dès sa lecture (pas de référence en avant dans l'expression).
Dans la première forme la valeur associée à identificateur est la valeur courante du compteur d'emplacement (l'adresse du 1er octet réservé).
identificateur rsw expression_entière rsw expression_entière
réserve expression_entière mot(s) à partir de la valeur courante du compteur d'emplacement. L'expression expression_entière doit pouvoir être évaluée dès sa lecture (pas de référence en avant dans l'expression).
Dans la première forme la valeur associée à identificateur est la valeur courante du compteur d'emplacement (l'adresse du 1er mot réservé).
identificateur byte expression_entière byte expression_entière
réserve un octet et l'initialise avec la valeur de expression_entière . Si cette valeur ne peut se coder sur un octet elle est tronquée et un message d'avertissement est affiché sur l'erreur standard.
Dans la première forme la valeur associée à identificateur est la valeur courante du compteur d'emplacement (l'adresse du 1er octet réservé).
identificateur word expression_entière word expression_entière
réserve un mot et l'initialise avec la valeur de expression_entière . Si cette valeur ne peut se coder sur un mot elle est tronquée et un message d'avertissement est affiché sur l'erreur standard.
Dans la première forme la valeur associée à identificateur est la valeur courante du compteur d'emplacement (l'adresse du 1er mot réservé).
identificateur string chaine_de_caractères string chaine_de_caractères
réserve le nombre d'octets nécessaires et initialise ces octets avec les codes ASCII des caractères de la chaîne. Un caractère NUL est toujours ajouté par l'assembleur en fin de chaîne. Dans la première forme la valeur associée à identificateur est la valeur courante du compteur d'emplacement (l'adresse du 1er octet réservé).
N.B. Les noms utilisés dans le document Description de la machine microPIUP pour présenter le jeu d'instructions servent de mots clés dans le langage d'assemblage.
De façon générale un identificateur peut être placé en début d'instruction, il sert alors d'étiquette à l'instruction et l'assembleur lui associe la valeur courante du compteur d'emplacement (l'adresse du premier mot de l'instruction).
identificateur CODE nom_de_registre , nom_de_registre , nom_de_registre CODE nom_de_registre , nom_de_registre , nom_de_registre
Les registres figurent dans la ligne dans le même ordre que dans le mot d'instruction.
identificateur CODE nom_de_registre , nom_de_registre CODE nom_de_registre , nom_de_registre
Les registres figurent dans la ligne dans le même ordre que dans le mot d'instruction.
identificateur CODE nom_de_registre , nom_de_registre,# expression CODE nom_de_registre , nom_de_registre,# expression
Les registres figurent dans la ligne dans le même ordre que dans le mot d'instruction. expression est une expression_entière ou une expression_d_adresse et sa valeur est codée dans le mot d'extension.
identificateur CODE nom_de_registre , spécification_d_adressage CODE nom_de_registre , spécification_d_adressage
identificateur Jcc # expression_entière Jcc # expression_entière
le mnémonique de l'instruction est construit en concaténant J avec les deux lettres identifiant la condition (cf. Description de la machine microPIUP ).
La valeur du déplacement expression_entière est codée dans le mot d'extension.
identificateur CODE spécification_d_adressage CODE spécification_d_adressage
identificateur CODE CODE
identificateur CODE expression , nom_de_registre CODE expression , nom_de_registre
expression est une expression_entière ou une expression_d_adresse dont la valeur doit être comprise entre -128 .. et +127. Cette valeur est codée dans les bits 0 à 7 de l'instruction.
identificateur Bcc expression_entière Bcc expression_entière
le mnémonique de l'instruction est construit en concaténant B avec les deux lettres identifiant la condition (cf. Description de la machine microPIUP).
La valeur du déplacement expression_entière doit être comprise entre -128 .. et +127 et est codée dans les bits 0 à 7 de l'instruction .
A chaque mode d'adressage correspond une notation symbolique non ambiguë.
#expression_entière #expression_d_adresse
La valeur de l'expression constitue le contenu du mot d'extension.
nom_de_registre
( nom_de_registre )
( nom_de_registre ) +
- ( nom_de_registre )
@expression_entière @expression_d_adresse
La valeur de l'expression constitue le contenu du mot d'extension.
Il est à noter qu'on peut utiliser une adresse quelconque sans disposer d'une étiquette sur l'emplacement correspondant.
( nom_de_registre )expression_entière
La valeur de expression_entière constitue la valeur du déplacement.
* ( nom_de_registre )expression_entière
La valeur de expression_entière constitue la valeur du déplacement.
L'exemple ci-dessous est un programme calculant la factorielle d'un nombre, itérativement et sans se préoccuper des débordement ni de la validité du paramètre :
org 256 start debut // sommet de pile r15 // pour empiler on pre-decremente : la pile "monte" vers les adresses basses pile equ r15 // registre de liaison (adresse de retour) pour les appels de sous programmes r0 adRetour equ r0 // toute fonction retourne sa valeur dans r1 valRetour equ r1 // base locale local equ r14 // codes des "primitives" systeme exit equ 5 getline equ 6 putline equ 7 // int fact(int n) // C'est du C, les parametres sont passes par valeur n equ 4 fact stw local,-(pile) // sauvegarde du referentiel local stw pile,local // mise-a-jour du referentiel - pas indispensable ici // faire le dessin de la pile a ce point // int resultat, k ; resultat equ valRetour k equ r2 // k = n; ldw k,(local)n // resultat = k ; stw k,resultat // k -- ; adq -1,k // while (k > 1){ ldw r9,#1 while1 cmp k,r9 jle #endWhile1-$-2 // resultat = resultat * k ; mul resultat,k,resultat // k -- ; adq -1, k jmp #while1-$-2 // } // return resultat ; endWhile1 ldw pile,local // pas indispensable ici ldw local,(pile)+ // restauration du referentiel local rts //} // programme principal et variables "globales" // char donnee[20] ; donnee rsw 20 // 20 octets pour la variable donnee // int entier ; entier rsw 1 // 1 mot pour la variable entier // debut debut ldw pile,#2000 // initialisation du sommet de pile ldw local,pile // referentiel local - pas indispensable // donnee = lire() // ldw r0,#donnee // trp #getline // entier = aToInt(donnee) ; // aToI() est a definir // ldw r2,#donnee // stw r2,-(pile) // jsr @aToI // ldw r8,#2 // add pile,r8,pile // stw valRetour,@entier // en attendant d'avoir defini aToI() : entier = 5 ldw valRetour,#5 stw valRetour,@entier // fact(entier); stw valRetour,-(pile) jsr @fact ldw r8,#2 add pile,r8,pile // le resultat est dans le registre valRetour, i.e. r1 // en attendant d'ecrire une conversion int -> texte : ecrire "le resultat est dans r1" ldw r0,#texte trp #putline //fin trp #exit // retour au systeme texte string "le resultat est dans r1" // int aToI(char *chaine) aToI rts
Selon le cas, seuls les 4 ou 5 premiers caractères de chaque ligne d'un fichier exécutable sont interprétés par le chargeur. Le reste de la ligne constitue un commentaire (il est donc ignoré).
Les 4 premiers caractères de la première ligne d'un fichier exécutable sont des chiffres hexadécimaux qui représentent l'adresse de chargement du programme en mémoire.
Les 4 premiers caractères de la deuxième ligne sont des chiffres hexadécimaux qui représentent l'adresse de la première instruction à exécuter c'est-à-dire la valeur initiale du compteur ordinal.
Si le premier caractère de la troisième ligne est un S, les 4 caractères suivants sont des chiffres hexadécimaux qui représentent l'adresse de base de la pile, c' est-à-dire la valeur initiale du registre SP, sinon les 4 premiers caractères de la troisième ligne sont des chiffres hexadécimaux qui représentent le premier mot à charger en mémoire.
Les 4 premiers chiffres hexadécimaux des lignes suivantes représentent les mots qui doivent être rangés consécutivement en mémoire.