Vous êtes sur la page 1sur 22

Jérémy BOUCHETEIL - Alexis REGEN

Projet informatique - Rapport d’Analyse


Table des matières
1 Introduction 4
1.1 Décomposition modulaire du projet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2 Analyse lexicale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.3 Analyse grammaticale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.4 Génération de la liste d’assemblage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.5 Génération du code binaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

2 Analyse lexicale 7
2.1 Ouverture du fichier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.2 Structures de données . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.3 Isoler les lexèmes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.4 Détermination des types des lexèmes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

3 Analyse grammaticale 11
3.1 Principe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.2 Grammaire générale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.3 Grammaire des instructions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.3.1 Bibliothèque d’instruction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.3.2 Vérification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

4 Génération de la liste d’assemblage et du code binaire 13


4.1 Gestion de l’adressage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
4.1.1 Gestion des sections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
4.1.2 Gestion des compteurs d’adresse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
4.1.3 Gestion des étiquettes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
4.2 Codage des instructions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
4.2.1 Exemple de codage d’une instruction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
4.3 Génération de la liste d’assemblage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
4.4 Génération des fichiers objets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
4.4.1 Fichier binaire .o . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
4.4.2 Fichier au format S19 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

5 Organisation du projet 18
5.1 Méthodologie de tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
5.2 Organisation des fichiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
5.3 Planning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

2
Appendices 20

A Bibliothèque d’instructions 21

B Taille de l’instruction MOVE 22

3
1 Introduction
Le but de ce projet informatique est de concevoir et de réaliser un assembleur pour microcontroleur
Freescale COLDFIRE en C sous Linux. Il s’agit de traduire l’assembleur, un langage compréhensible par l’homme
(quoi que de bas niveau), en un langage machine directement compréhensible par le microcontroleur.
À partir d’un fichier source écrit en langage assembleur, notre programme devra être capable de générer
trois sorties distinctes :
– une liste d’assemblage expliquant le déroulement du processus d’assemblage
– un fichier objet binaire *.o, traduction en langage machine du programme fourni en entrée
– un fichier objet au format S19, format normé utilisé par Motorola qui nous permettra de valider le
fichier créé grâce à un simulateur

1.1 Décomposition modulaire du projet

La conception d’un assembleur est un projet complexe et il est nécessaire de décomposer le programme
en plusieurs modules distincts. Cette décomposition permet d’aborder chaque problème dans son contexte sans
s’occuper de détails précédant ou suivant la partie du projet étudiée. De plus elle permet d’organiser le code et de
réaliser des test de fonctionnement progressifs sans forcément attendre que le programme soit entièrement fini.
Pour ce projet, nous avons fait le choix de décomposer le programme en quatre modules principaux. Nous
expliciterons tout d’abord leur rôles respectifs sans rentrer dans les détails puis nous leur accorderons a chacun
une partie expliquant les structures de données, les algorithmes et les fonctions utilisées pour les implémenter.
Les quatre modules retenus sont les suivants :
– analyse lexicale
– analyse grammaticale
– génération de la liste d’assemblage
– génération du code binaire

4
On a donc le processus d’assemblage suivant :

Figure 1.1 – Étapes du processus d’assemblage

1.2 Analyse lexicale

L’analyse lexicale est la première étape du processus d’assemblage. Réaliser l’analyse lexicale d’un lan-
gage, c’est décomposer et analyser les différents mots (ou ”lexèmes”) contenus dans le fichier source fourni. Il
s’agit donc de faire deux travaux distincts :
– il faut tout d’abord séparer les différents lexèmes les uns des autres en définissant les caractères
séparateurs types (espaces, retours à la ligne ...)
– il faut ensuite déterminer la nature de chaque lexème trouvé (étiquette, nombre décimal, nom de
registre ...)
Une fois l’analyse terminée, nous stockerons les informations récoltées dans une structure de donnée particulière
pour pouvoir les réutiliser dans la suite du programme.

1.3 Analyse grammaticale

Maintenant que les lexèmes ont bien été extraits du fichier source et que leur nature a été définie il
faut vérifier qu’ils sont employés dans un ordre correct. Cela reviens à vérifier que le fichier source fourni a bien

5
été rédigé dans un langage assembleur correct. C’est à cette vérification que l’analyse grammaticale correspond.
Cette étape ne modifiera pas les données enregistrées mais aura seulement un accès en lecture lui permettant de
décider si la syntaxe du fichier source est juste ou non.
On peut décomposer cette analyse grammaticale en deux parties distinctes : l’analyse grammaticale
”globale” qui correspond a vérifier que les natures des lexèmes s’enchainent correctement et ce sans rentrer
directement dans les instructions et l’analyse grammaticale des instructions qui correspond a vérifier que chaque
instruction possède bien les opérandes nécessaires et que celles ci sont du bon type. Pour l’analyse globale, on
utilisera principalement la grammaire définie dans l’annexe A du polycopié fourni alors que pour l’analyse des
instructions on utilisera la documentation détaillée de l’annexe D du polycopié fourni.
En cas d’erreur, le processus d’assemblage sera stoppé et un message contenant les détails du problème
rencontré (n˚ de ligne, lexème en cause, ...) sera affiché sur le terminal. Si au contraire le fichier source a une
syntaxe correcte le processus d’assemblage pourra continuer.

1.4 Génération de la liste d’assemblage

À cette étape du processus d’assemblage, notre programme possède dans une structure de donnée
particulière la liste des lexèmes et de leur nature et nous sommes surs que la syntaxe du fichier source est bonne.
Pour faciliter la génération du code binaire, nous allons maintenant créer une liste d’assemblage, sorte de compte
rendu du processus d’assemblage. Celle-ci contiendra (d’après le polycopié fourni Chap 6.1) :
– le numéro en décimal de la ligne du fichier source
– l’adresse en hexadécimal de l’instruction ou de la donnée
– le codage en hexadécimal de l’instruction ou de la donnée
– la contenu de la ligne source
– les erreurs éventuellement détectées sur la ligne suivante
On ajoutera enfin une table des symboles définis dans le fichier source.
Il n’est pas forcément possible de générer cette liste d’assemblage en un seul parcours de la structure
de données contenant les lexèmes. En effet certaines instructions demanderons des adresses dont on ne connaitra
pas la valeur pour pouvoir être codées. Il sera donc nécessaire de parcourir deux fois la structure de données pour
pouvoir générer complètement la liste d’assemblage. Ce processus d’adressage sera détaille dans la partie 4.

1.5 Génération du code binaire

Une fois la liste d’assemblage créée il devient assez simple de fabriquer un fichier objet contenant
seulement les instructions en binaire du programme assemblé. Nous détaillerons l’algorithme utilisé en partie 4.
Quelques calculs de plus sont nécessaires pour créer le fichier objet respectant la norme S19 mais tout comme
lors de l’analyse grammaticale, le programme ne modifie plus les données stockées et a seulement un accès en
lecture sur celles ci. A la fin de cette étape trois fichiers sont crées dans le répertoire courant du terminal :
– une liste d’assemblage : file.l
– un fichier objet binaire : file.o
– un fichier objet texte au format S19 : file.s19
Enfin, un message est affiché dans le terminal indiquant que l’assemblage c’est déroulé sans problème.

6
2 Analyse lexicale
2.1 Ouverture du fichier

Pour stocker les informations relevées lors de l’analyse lexicale nous utiliserons un tableau de listes
chainées de lexèmes contenant autant de lignes que le fichier source en contient. Pour cela il faut tout d’abord
compter le nombre de lignes que contient le fichier source. Ce comptage est effectué par l’algorithme suivant :
1 o u v e r t u r e du f i c h i e r
2 n b L i g n e s <− 0
3 TantQue c a r a c t e r e l u != EOF
4 c a r a c t e r e l u <− l i r e c a r a c t e r e ( )
5 S i c a r a c t e r e l u == ” /n”
6 n b L i g n e s += 1
7 FinSi
8 FinTantQue

Pour lire un caractère on utilisera la fonction fgets du langage C.

2.2 Structures de données

Pour stocker les différents lexèmes dans le tableau on réalise ensuite une liste chainée de lexèmes utilisant
la structure suivante :
1 s t r u c t lexeme {
2 char ∗ ChaineDeCaracteresLue [ 8 0 ] ;
3 type lexeme type ;
4 i n t donnee ;
5 }

Cette structure est composée des éléments suivants :


– ChaineDeCaracteresLue correspond à la chaine de caractères ”brute” lue dans le fichier source qui
définit le lexème, nous verrons par la suite pourquoi on suppose qu’elle est de 80 caractères au
maximum.
– type correspond au type du lexème, on utilisera pour plus de clarté l’énumération suivante :
1 enum t y p e l e x e m e {
2 CONSTANTE,
3 ETIQUETTE ,
4 INSTRUCTION ,
5 VIRGULE ,
6 NOMBRE DEC,
7 NOMBRE HEXA,
8 DIRECTIVE DONNEES ,
9 DIRECTIVE SECTION ,
10 DIRECTIVE CONSTANTE ,
11 OPE REGISTRE DIRECT ,

7
12 OPE ADRESS DIRECT ,
13 OPE ADRESS INDIRECT ,
14 OPE ADRESS INDIRECT PRE ,
15 OPE ADRESS INDIRECT POST ,
16 OPE ADRESS INDIRECT DEPL ,
17 OPE ADRESS ABS ,
18 OPE ADRESS PC ,
19 OPE ADRESS IMM ,
20 NONDEFINI ,
21 }

– donnee est un entier pouvant contenir une information supplémentaire sur le lexème, cette donnée ne
sera pas toujours utilisée. On aura la répartion suivante :
– pour un registre d’adressage direct (OPE_ADRESS_DIRECT), donnee sera le numéro du registre
– pour un registre de données direct (OPE_REGISTRE_DIRECT), donnee sera le numéro du registre
– pour une étiquette donnée sera l’adresse de l’étiquette

2.3 Isoler les lexèmes

Nous venons de voir que nous allions lire le fichier source ligne par ligne. Chaque ligne sera stockée
dans une chaine de caractères temporaire tronquée à 80 caractères. Cette valeur de 80 caractères correspond
au nombres de caractères affichables par ligne dans un terminal type. Au vu de la syntaxe plutôt concise de
l’assembleur, nous considèrerons qu’il n’est pas possible de supprimer de l’information utile au compilateur (c’est
a dire autre qu’un commentaire) en tronquant les lignes lues à 80 caractères.
Pour séparer les lexèmes nous allons utiliser la fonction strtok du langage C. Cette fonction prend
en argument deux chaines de caractères et sépare la première en utilisant la seconde comme séparateur. Nous
décidons d’utiliser le caractère ” ” (espace) comme séparateur.
Cependant la ligne lue n’est pas forcément formatée comme il le faut pour que cette méthode fonctionne :
si elle contient des espaces consécutifs entre les lexèmes la séparation ne se fera pas correctement. Il sera donc
nécessaire de créer une fonction formater_ligne_lue() qui aura le comportement suivant :
– supprimer tous les blancs au sein des parenthèses
– remplacer toutes les virgules ”,” par des virgules entourées d’espaces ” , ” sauf dans les parenthèses
– ne pas laisser plus d’un espace consécutif entre chaque caractère
– supprimer un commentaire de fin de ligne éventuel
À titre d’exemple la chaine lue :
MVS.W ( 0xAA2 ,PC), D2 /ceci est un commentaire
Deviendra MVS.W (0xAA2,PC) , D2
Puis en passant dans la fonction strtok on aura isolé les lexèmes : MVS.W, (oxAA2,PC), ,, D2

2.4 Détermination des types des lexèmes

Maintenant que nos lexèmes sont isolés, il est nécessaire de déterminer leur type. Pour cela on définit
deux automates à états finis, le premier sert à déterminer la nature du lexème sans rentrer dans les détails des
opérandes. Si jamais une instruction est détectée alors on passe sur l’automate à états finis des opérandes pour

8
déterminer sa nature. Pour différencier les constantes des instructions du langage assembleur, si l’automate a
isolé une chaine uniquement composée de caractères il effectue une recherche dans une bibliothèque contenant
l’ensemble des instructions. Si la recherche trouve une correspondance, on peut affirmer que l’on a un lexème de
type instruction et sinon une constante. Devant le faible nombre d’instructions différentes (moins de 40) nous
n’utiliserons pas une table de hachage pour accélérer cette recherche, cependant nous tenterons d’implémenter
une recherche dichotomique (la bibliothèque étant classée par ordre alphabétique). L’automate à états finis utilisé
pour typer les lexèmes est le suivant :

Figure 2.1 – Automate à états finis utilisé typer les lexèmes

9
L’automate à états finis utilisé pour les opérandes est le suivant :

Figure 2.2 – Automate à états finis utilisé pour typer les opérandes

10
3 Analyse grammaticale
3.1 Principe

Une fois que l’analyse lexicale a été correctement effectuée, notre programme dispose en mémoire d’une
liste complète des lexèmes présents dans le fichier source en ordre par ligne. Il faut maintenant vérifier que cet
ordre est logique et conforme a la syntaxe de l’assembleur du COLDFIRE. Cette vérification sera effectuée lors
de l’analyse grammaticale.
Aucune opération d’écriture ne sera effectuée sur les données stockées en mémoire durant cette étape,
cependant toute erreur de syntaxe détectée sera remontée en affichant sur un terminal le numéro de la ligne
en cause et les lexèmes impliqués. Le programme continuera ensuite son analyse mais une variable de contrôle
interne lui ordonnera de se stopper après génération de la liste d’assemblage pour ne pas produire de fichier objet
invalide.
En réalité c’est deux taches distinctes qu’il sera nécessaire d’effectuer :
– il faut tout d’abord vérifier que la grammaire générale est correcte, c’est à dire sans rentrer dans les
détails des instructions
– il faut ensuite vérifier que les opérandes associées à chaque instruction sont de types conformes à
ceux attendus

3.2 Grammaire générale

Pour vérifier la grammaire générale on implémentera en mémoire de façon récursive la grammaire du


langage assembleur définie dans l’annexe A du polycopié. On définira ensuite une série de fonctions de type
vericationQueCetteLigneEstUneInstruction() qui renverrons 1 si la ligne est bien de type instruction et
0 si ce n’est pas le cas. Ces fonctions s’enchainant récursivement en partant de la globalité du fichier source
jusqu’à la ligne, il suffira de vérifier à la fin que chaque ligne a au moins reçu une fois une approbation de l’une
des fonctions pour valider la grammaire générale de la source.

3.3 Grammaire des instructions

3.3.1 Bibliothèque d’instruction

Pour faciliter la vérification des types d’opérande on crée une bibliothèque d’instruction dans un fichier
texte qui sera chargée au démarrage de notre programme. Chaque ligne de cette bibliothèque est composée de
la façon suivante :
1 n o m I n s t r u c t i o n nombreOperandes v e r i f i c a t i o n O p e r a n d e D e s t i n a t i o n v e r i f i c a t i o n O p e r a n d e S o u r c e T a i l l e

Les deux nombres verificationOperandeDestination et verificationOperandeSource sont des suites de


0 et de 1. Chaque chiffre correspond à un type d’opérande. En cas de 0 l’opérande n’est pas acceptée par

11
l’instruction (et en cas de 1 oui). La colonne destination contient un chiffre de plus qui correspond aux étiquettes.
La colonne taille est utilisée lors de l’adressage et sa signification sera expliquée par la suite. La bibliothèque est
disponible en annexe A.

3.3.2 Vérification

Une fois cette bibliothèque correctement définie en mémoire, il devient aisé de comparer chaque opérande
lue dans la structure de lexèmes avec les bits de vérification pour savoir si les opérandes sont acceptées ou non.
Cette bibliothèque offre aussi un confort d’utilisation si jamais nous sommes amenés un jour a ajouter une
instruction.

12
4 Génération de la liste d’assemblage et du
code binaire
4.1 Gestion de l’adressage

Pour que le code généré par notre assembleur soit correctement compréhensible par le microntroleur il
faut pouvoir définir une adresse mémoire correcte pour chaque instruction ou étiquette rencontrée. Cependant
cette gestion des adresses est compliquée par plusieurs règles du langage assembleur, à savoir que :
– il est possible de définir les sections .text et .data à plusieurs endroits (chaque section possède une
plage d’adresse qui lui est propre)
– la taille des instructions en mémoire dépend de l’instruction étudiée et des opérandes qui la composent
– il est possible d’appeler une étiquette avant qu’elle ne soit définie

4.1.1 Gestion des sections

Le langage assembleur impose que les adresses des éléments définis dans la section .data ne soient pas
sur la même page que celles des éléments définis dans la section .text (en fait ils se trouvent sur la page de
mémoire suivante). Par conséquent il est nécessaire de savoir dans quelle section se trouve l’instruction traitée
par la routine d’adressage.
Cependant il est possible de faire appel à chacune des sections à plusieurs endroits du fichier source.
On définira donc une variable typeSection ayant le comportement suivant :

typeSection Section en cours


0 .text
1 .data

À chaque fois que la routine d’adressage rencontrera un lexème de type DIRECTIVE_SECTION elle
vérifiera de quelle section il s’agit et actualisera la variable typeSection en conséquence.

4.1.2 Gestion des compteurs d’adresse

Pour savoir quelle adresse affecter à chaque instruction il est nécessaire de tenir a jour des compteurs
sommant a chaque itération les tailles en mémoire des différentes instructions passées.

Taille des instruction

Les instructions du COLDFIRE sont codées sur 16, 32 ou 48 bits. Les 4 bits de poids fort sont intrinsèques
à l’instruction elle-même. Les 12 bits suivants désignent la taille des opérandes et leur mode d’adressage. Ces
informations sont données en annexe du sujet. Cependant le mode d’adressage peut changer la longueur du mot
binaire. En effet, les modes (d16,An), (d8,An) et (xxx).w nécessitent un mot en plus de l’instruction elle

13
même. Le mode d’extension (xxx).L nécessite lui deux mots d’extension et le mode #<data> un ou deux mots
selon la taille de l’opérande considérée.
La bibliothèque des instruction contient des informations pour déterminer la taille que prendra une
instruction suivant les opérandes qui lui sont associées. On y définit un chiffre ayant la signification suivante :

Valeur de la variable Signification


2 l’instruction prends toujours deux mots en mémoire (4 octets)
1 l’instruction prends toujours un mot en mémoire (2 octets)
0 la taille en mémoire dépend des opérandes

Si la taille en mémoire dépends des opérandes (par exemple MOVE) on se référera alors à un tableau
contenant les différentes tailles possibles suivant les opérandes considérées (voir annexe B).

Compteur .text

Pour affecter les adresses de la partie .text on va tenir à jour un compteur qui sera incrémenté (si et
seulement si typeSection==0) à chaque itération de la taille de l’instruction lue déterminée grâce au procédé
décrit ci-dessus.
Ce compteur pourra être initialisé de deux façons différentes : si jamais une adresse de départ est donnée
en argument lors de l’appel du programme alors on la définira comme adresse de départ du compteur. Sinon on
prendra comme valeur par défaut l’adresse 0x1000.

Compteur .data

Le cas du compteur .data est un petit peu plus complexe. En effet nous devons l’initialiser à l’adresse
correspondant à la première adresse de la page suivant la fin de la section .text. Par conséquent on l’initialise
à une valeur adresseDepart. Toutes les adresses affectées (lorsque typeSection==1) par la suite seront alors
relatives. Lorsque l’ensemble du fichier source aura été parcouru on pourra définir adresseDepart et donc définir
les adresses absolues des instructions de la partie .data.

4.1.3 Gestion des étiquettes

Les étiquettes présentent un double problème lors de l’affectation des adresses. Premièrement il est
possible d’appeler une étiquette au sein d’une instruction sans l’avoir définie auparavant dans le code source. Il
faut donc parcourir deux fois le fichier source pour que chaque étiquette aie son adresse définie. De plus, on doit
pouvoir vérifier que chaque étiquette utilisée est bien définie (et seulement une fois) au sein du fichier source.
Pour résoudre ces problèmes on définit tout d’abord une structure pour enregistrer les données relevées
sur les étiquettes rencontrées :
1 struct infoEtiquette {
2 c h a r ∗ n o m E t i q u e t t e [ 9 ] ; // 9 p o u r 8 c a r a c t e r e s au maximum + c a r a c t e r e ’ \ 0 ’
3 int adresse ;
4 int etatDefinition ;
5 }

14
Cette structure est composée des éléments suivants :
– nomEtiquette est une chaine de caractères qui contiendra la référence de l’étiquette
– adresse contiendra l’adresse une fois que celle ci aura été définie
– etatDefinition est une variable qui permettra de savoir si l’étiquette a été définie (1) ou non (0). Elle
aura le comportement suivant :
1 // r o u t i n e d ’ a d r e s s a g e
2 (...)
3 S I t y p e l e x e m e l u == ETIQUETTE
4 S I i l e x i s t e d e j a une s t r u c t u r e i n f o E t i q u e t t e a v e c n o m E t i q u e t t e==c h a i n e C a r a c t e r e L e x e m e
5 S I e t a t D e f i n i t i o n == 0
6 SI e t i q u e t t e d e f i n i e dans l a l i g n e e t u d i e e
7 a d r e s s e <− c o m p t e u r A c t u e l
8 e t a t D e f i n i t i o n <− 1
9 FINSI
10 ELSE
11 SI e t i q u e t t e d e f i n i e dans l a l i g n e e t u d i e e
12 a f f i c h e r ( Erreur etiquette deja d e f i n i e )
13 FINSI
14 FINELSE
15 ELSE
16 creerStructure infoEtiquette
17 n o m E t i q u e t t e <− c h a i n e C a r a c t e r e L e x e m e
18 SI e t i q u e t t e d e f i n i e dans l a l i g n e e t u d i e e
19 a d r e s s e <− c o m p t e u r A c t u e l
20 e t a t D e f i n i t i o n <− 1
21 ELSE
22 e t a t D e f i n i t i o n <− 0
23 FINELSE
24 FINELSE
25 FINSI

Comme il n’est pas possible de savoir à l’avance combien d’étiquettes seront rencontrées dans le fichier source
nous utiliserons une liste chainée de cette structure particulière pour stocker les données.

4.2 Codage des instructions

Pour coder les instructions, c’est à dire transformer chaque ligne d’instruction en la suite d’octets qui
lui correspond, il aurait été idéal de définir une fonction générale qui aurait pu coder chaque fonction suivant
les paramètres qu’elle prend en opérandes. Cependant devant la multiplicité des comportements différents des
instructions en jeu, et devant le nombre raisonnable de celles ci nous procéderons d’une tout autre façon.
Nous allons créer un fichier bibliothequeDeTraduction.c qui contiendra autant de fonctions qu’il
existe d’instructions. La forme générique de ces fonctions est la suivante :
1 i n t t r a d u c t i o n N o m I n s t r u c t i o n ( i n t nombreOperandes , ( c h a r ∗ o p e r a n d e 1 , t y p e l e x e m e t y p e O p e r a n d e 1 ) ,
2 ( char ∗ operande2 , t y p e l e x e m e typeOperande2 ) ) ;

Chacune d’elles prend en entrée les opérandes éventuelles ainsi que leur type et renvoie un entier correspondant
au codage en binaire de l’instruction.

15
4.2.1 Exemple de codage d’une instruction

Cette section détaillera l’exemple de codage d’une instruction (même comportement que la fonction qui
lui serait associée). On prend l’exemple de l’instruction suivante :

MVS.W D0, D1

D’après l’annexe des spécifications détaillées des instructions, cette instruction est de la forme :
MVS.sz <ea>y, Dx
– Les 4 bits de poids fort vont être 0111.
– Les 3 bits suivants (11,10 et 9 ) désignent le registre de Dx (ici D1) ie. 001
– Les deux bits suivants sont fixés a 10
– Pour un Word la size (bit 6) est donc de 1
– La source est D0 et en regardant dans le tableau de l’instruction MVS de l’annexe D du polycopié :
le registre de D1 s’écrit 001, et le registre source D0 s’écrit 000000
Ainsi le codage de la première instruction s’écrit : 0111001101000000 soit en hexadécimal 7340.

4.3 Génération de la liste d’assemblage

Une fois le travail d’adressage correctement effectué la génération de la liste d’assemblage ne pose plus
de problème particulier. Notre programme ré-ouvrira le fichier source pour lire chaque ligne avant de l’écrire telle
quelle dans la liste car nous avons supprimé les commentaires à l’analyse lexicale, puis il complètera les différentes
colonnes à l’aide des données stockées et des fonctions de traduction évoquées ci dessus.

4.4 Génération des fichiers objets

4.4.1 Fichier binaire .o

Plusieurs règles sont à respecter pour créer le fichier binaire d’un programme :
– chaque ligne ne dépassera pas 16 octets de code
– chaque ligne prend 32 bits en mémoire sur l’ordinateur
– l’entête de la section de texte du fichier binaire comprend 4 octets désignant l’adresse du début de
la section puis les 4 octets suivants désignent la taille (nombre d’octets d’instruction que l’on notera
0xN) puis les octets suivants seront les 0xN octets de codes d’instructions
– l’entête de la section data comprend 4 octets d’adresse puis 4 octet de taille
– les 4 octets de données initialisées
L’algorithme utilisé sera le suivant :
1 Compteur1 = 0
2 Compteur2 = 0
3 T a i l l e =0
4 A d r e s s e o r d i n a t e u r= 00000000
5 E c r i r e d a n s l e f i c h i e r o b j e t b i n a i r e A d r e s s e o r d i n a t e u r e t un e s p a c e
6 A d r e s s e o r d i n a t e u r= A d r e s s e o r d i n a t e u r +20
7 Tant que a d r e s s e != a d r e s s e d e r n i e r e i n s t r u c t i o n // compte l a t a i l l e d e s i n s t r u c t i o n s
8 l i t 1 c h i f f r e hexa

16
9 Compteur1 =c o m p t e ur 1+1
10 F i n t a n t que
11 Compteur1= c o m p t eu r 1 /2 // t a i l l e d e s i n s t r u c t i o n s
12 T a i l l e=Hexa ( c o mp t e u r 1 / 2 ) // c o n v e r t i en h e x a l e nombre t r o u v e
13 R e c o p i e r t a i l l e dans l e f i c h i e r o b j e t
14 Pour 1< i <16 // c o m p l e t e l a p r e m i e r e l i g n e du f i c h i e r o b j e t b i n a i r e
15 Recopie dans l e f i c h i e r o b j e t 1 c h i f f r e
16 End l o o p
17 A l l e r a l a l i g n e dans l e f i c h i e r o b j e t
18 n o u v e l l e l i g n e d a n s l e f i c h i e r o b j e t e t e c r i r e A d r e s s e o r d i n a t e u r e t un e s p a c e
19 A d r e s s e o r d i n a t e u r= A d r e s s e o r d i n a t e u r +20
20 Tant que a d r e s s e != a d r e s s e d e r n i e r e i n s t r u c t i o n
21 Recopie c h i f f r e 1 hexa dans l e f i c h i e r o b j e t
22 Compteur2=c o m p t e u r2+1
23 S i Compteur2=32
24 alors a l l e r a la ligne
25 FinSi
26 e c r i r e A d r e s s e o r d i n a t e u r e t un e s p a c e
27 A d r e s s e o r d i n a t e u r= A d r e s s e o r d i n a t e u r +20
28 Compteur 2 =0
29 F i n t a n t que

Pour l’entête du .data, on recopie l’adresse du data sur 4 octets chiffre par chiffre (définie en partie en D)) en
testant si le nombre de chiffres ne dépasse pas 16 dans la ligne (sinon il faudra revenir a la ligne et écrire la valeur
de  Adresse ordinateur  plus un espace) puis la taille sur 4 octets du data (2 octets pour le data auquel on
additionne le nombre d’octets pour définir les variables de sortie) et enfin on termine par écrire les 4 octets de
données initialisées.

4.4.2 Fichier au format S19

Pour que le code généré soit compréhensible pour le microcontroleur, il est nécessaire de le formater
dans le format imposé par le constructeur, ici le S19.
Le fichier commencera par le caractère S0 suivi des 1 octets de count sum (égal au nombre d’octet dans
la ligne), suivit de O codé sur 2 octets puis 3 octets HDR qui valent 484452 le dernier octet est le check sum
(=FF- byte count-data byte {adresse byte) ici check sum =FF-6-48-44-52-0=1B
Ainsi la première ligne sera :
S00600004844521B La deuxième ligne d’instructions commencera par S1. Il faut d’abord compter le
nombre d’octets dans la ligne. On a 32 octets (64 chiffre hex) maximum de codage des instructions faites dans
la liste d’assemblage au quel on ajoute 2 octets liés à l’adresse de départ et 1 octet de check sum.
On écrit la valeur de cette somme en hexadécimal a la suite de S1. Puis l’adresse de la première
instruction. On écrit le nombre de chiffre hexa¡64 de codage des instructions.
Puis le check sum se calcule de la même manière que ce qui précède sauf que l’on ne retiendra que les
deux derniers chiffres hexadécimaux de la somme suivante : byte count+data byte +adresse byte.
Les autres lignes se coderont de la même manière que la précédente.
La dernière ligne commencera par S9 suivi du nombre count byte=03 puis de 0000 et le check sum
sera donc égal a FC.

17
5 Organisation du projet
5.1 Méthodologie de tests

Notre programme étant décomposé en modules plus ou moins indépendants les uns des autres nous
avons pour objectif de réaliser une batterie de tests à la fin de la création de chaque module général (analyse
lexicale, analyse grammaticale, adressage et génération du code binaire).
Les tests seront de complexité croissante allant d’un simple test routinier à des tests impliquant des
erreurs volontaires dans le fichier source. Pour faciliter le débogage nous utiliserons ddd, une surcouche graphique
à gdb.

5.2 Organisation des fichiers

Dans le but d’organiser au maximum le code, nous utiliserons plusieurs fichiers .c.
– io.c contiendra toutes les fonctions relatives à la gestion de la lecture du fichier source et à l’écriture
des fichiers de sortie
– analyseLexicale.c contiendra toutes les fonctions relatives au module analyse lexicale
– analyseGrammaticaleGenerale.c contiendra toutes les fonctions relatives au module de
vérification de la grammaire générale
– analyseGrammaticaleInstructions.c contiendra toutes les fonctions relatives au module de
vérification des instructions
– adressage.c contiendra toutes les fonctions relatives au module d’adressage
– listeAssemblage.c contiendra toutes les fonctions relatives à la génération de la liste d’assemblage
– codage.c contiendra toutes les fonctions relatives au codage des deux fichiers objets
– et enfin un fichier main.c aura pour rôle de relier ces différents modules entre eux et de faire remonter
au terminal les erreurs et/ou signes de bon comportement du programme.

18
5.3 Planning

Devant la complexité du projet, il apparait comme nécessaire de définir un planning précis :

Date Durée Type de séance Travail Jérémy BOUCHETEIL


Lundi 18/10 4h non encadrée conception io.c
Vendredi 22/10 4h encadrée fin conception io.c, tests
Vendredi 5/11 4h encadrée conception analyse lexicale lexèmes
Vendredi 12/11 4h non encadrée fin analyse lexicale, tests, début conception adressage
Vendredi 19/11 4h encadrée tests adressage, début codageinstruction.c et tests au cas par cas
Mercredi 24/11 4h non encadrée Fin codageInstructions.c ,tests et conceptionlisteassemblage.c
Vendredi 26/11 4h encadrée TESTS

Date Durée Type de séance Travail Alexis REGEN


Lundi 18/10 4h non encadrée création de l’arborescence unix du projet, début conception main.c
Vendredi 22/10 4h encadrée fin création main.c
Vendredi 5/11 4h encadrée conception analyse lexicale opérandes
Vendredi 12/11 4h non encadrée fin conception lexicale,tests, début conception grammaticale
Vendredi 19/11 4h encadrée fin grammaticale, tests
Mercredi 24/11 4h non encadrée conception codage.c et test
Vendredi 26/11 4h encadrée TESTS

Les séances encadrées seront l’occasion de poser des questions sur les problèmes que nous aurions
rencontré lors des séances en autonome. Si jamais a la fin d’une séance trop de retard a été pris, il sera nécessaire
de rattraper celui ci avant la séance suivante.

19
Appendices

20
A Bibliothèque d’instructions

21
B Taille de l’instruction MOVE

22

Vous aimerez peut-être aussi