Veuillez consulter cette page à partir de l'interface principale.
Le langage assembleur template text
version : 2014-10-01 (OK)

Le langage assembleur du YASEP

Introduction

Cette page décrit l'assembleur du YASEP (c'est à dire le programme qui transforme les instructions en code binaire) ainsi que les conventions de langage (la syntaxe) permettant d'écrire un logiciel pour le YASEP. Ces informations sont spécifiques aux outils de ce site (yasep.org) et les développements externes (comme defora) peuvent différer sur de nombreux points, consultez leur documentations. Pour obtenir des informations sur une instruction en particulier, consultez la carte des opcodes.

Cette page couvre les points suivants :

Organisation

L'assembleur du YASEP est constitué de deux couches :

Anatomie d'une instruction

L'instruction est l'unité de base d'un programme, à la manière d'un atome.
* Pour le YASEP, cela consiste en un mot de 16 ou 32 bits, contenant différents champs qui indiquent ce qu'il faut faire avec quoi.
* Pour l'assembleur, une instruction est une ligne de texte qui contient tous ces champs sous une forme symbolique et lisible.

Une instruction contient typiquement :

La structure binaire des instructions est expliquée ici.

L'encodeur d'instructions

Les instructions du YASEP sont assez simples mais elles ne sont pas toujours pratiques, alors certains opcodes ont besoin de modifications mineures. Ce sont souvent des dérogations mineures aux règles de codage, qui sont indiquées par des flags (les propriétés internes de chaque opcode).

L'assembleur tient compte de ces flags pour transformer le texte source en code binaire, afin de garder les instructions faciles à utiliser, lisibles et cohérentes, indépendamment de leur encodage réel, des exceptions d'encodage ou de l'architecture du processeur.

Le but du langage assembleur est de permettre au developpeur de se concentrer sur le code, sans avoir besoin de garder en tête tous les details de l'architecture. Il lui suffit de se souvenir de ces 5 règles d'écriture :

Avec ces quelques règles, il est possible de comprendre la sémantique de toutes les instructions, même si on ne connaît pas les exceptions, subtilités ou variations spécifiques à chaque opcode.
Par exemple : add 3 r2 d3 LSB1 a2

Assemblage d'une instruction

Et c'est tout ce qu'il y a à dire au sujet de la "syntaxe".

Le format des nombres

L'assembleur accepte 3 formats de nombres :

Les nombres sont souvent utilisés dans des contextes où le nombre de bits significatifs est limité par la taille du conteneur (habituellement, le champ immédiat d'une instruction). Quand trop de bits sont donnés, il peut arriver que l'assembleur conserve seulement le nombre desiré de bits de poid faible, et efface les bits de poids forts. L'exemple suivant montre comment un nombre est tronqué : db 1234h.

Note : le désassembleur écrit toujours les valeurs en hexadécimal.

Pseudo-instructions

La capacité à créer des nombres arbitraires est souvent indispensable. L'assembleur dispose des quatre pseudo-instructions suivantes :

On peut mettre autant de nombres qu'on veut, cela dépend uniquement du moteur JavaScript, pas de l'assembleur. Ce dernier peut générer des suites de nombres si on lui fournit une fonctio emit_bin() comme dans l'exemple ci-dessous :

Résultat :

(messages d'erreurs)

 

Largeur du chemin de données

Depuis août 2008, le YASEP existe en versions 16 bits et 32 bits. Les opcodes ne changent pas mais quelques-uns ne riment à rien selon la taille des registres. Le code source peut spécifier qu'une certaine taille est utilisée, afin que l'assembleur émette un message de warning lorsqu'il rencontre un opcode invalide pour la version de YASEP attendue.

YASEP16 spécifie que le CPU est sur 16 bits. Toutes les instructions spécifiques au mode 32 bits émettent un warning.

YASEP32 spécifie que le CPU est sur 32 bits. Toutes les instructions spécifiques au mode 16 bits émettent un warning.

YASEP (par défaut) indique que le type de CPU est générique, indéfini, et toutes les instructions sont valides.

Ces pseudo-instructions ne génèrent aucun code binaire et peuvent être utilisées dans n'importe quel ordre, car elles contrôlent un flag interne. Ce flag est comparé avec les flags de toutes les instructions assemblées (voir aussi YASEP32_ONLY et YASEP16_ONLY).

Depuis 2012-02, ces pseudo-instructions ne sont plus utilisées car la largeur du chemin de données fait partie des nombreux paramètres qui constituent un "profile" de processeur, que l'on peut spécifier au moyen du mot-clé .profile et configurer avec une interface graphique.

Symboles réservés

L'assembleur basique reconnaît les symboles suivants et rejette tous les autres :

Les alias

Pour faciliter l'écriture du code assembleur (penser à ce que le processeur doit faire et laisser à l'assembleur le choix de comment ce sera codé), il y a deux types d'alias : les alias de formes (voir ALIAS_RR, c'est à dire une écriture simplifiée de certaines formes) et les alias d'instructions (ces alias sont listés après la table des instructions). Cette section parle des alias d'instructions.

Ces alias peuvent être utilisés comme des instructions normales mais elle permettent différentes formes ou sémantiques, tout en utilisant des vraies instructions sous le capot.

Le désassembleur ne peut pas deviner toutes les substitutions pour retrouver l'alias original, donc ne vous étonnez pas si certaines instructions comme NOT ou NEG sont correctement assemblées mais le désassembleur donne un opcode différent.

Formes d'instruction et flags

Bien que les instructions soient relativement simples, elles apparaissent sous plusieurs formes différentes en langage assembleur. Cela est habituellement dû à des raisons comme :

Les différentes formes (combinaisons d'opérandes et autres champs) sont décrites ici et les flags ont aussi leur propre page.

Valeurs par défaut

Toutes les données inutilisées ou non initialisées sont à zéro. Par exemple, la valeur par défaut du code de condition et des bits d'auto-update est 0.

Dans le cas des instructions acceptant le champ Imm20 comme MOV, le champ est toujours rempli avec une valeur correcte, même si les MSB ne sont pas décodés ou utilisés par YASEP16. Un warning est émis à l'assemblage.

 

L'assembleur haut niveau

Depuis 2012-02, les outils comprennent un "assembleur haut niveau", appelé YASMed. C'est une interface graphique qui gère les instructions ligne par ligne, indépendamment des détails de bas niveau. Vous pouvez lancer cet outil avec le menu "ASM" ou en cliquant sur une zone de code comme celle-ci :

  NOP ; exemple de code source

YASMed n'est pas un assembleur classique à passes multiples car il met à jour les informations au moment de l'édition. Pour sortir d'un conflit de résolution de symboles, il suffit de cliquer sur le bouton "réassembler", ce qui créera une nouvelle instance de l'éditeur avec la table des symboles à jour.

Actuellement, cet éditeur supporte certains mots-clés, qui commencent tous par un point au début de la ligne :

Attention, ces directives commencent au premier caractère de la ligne, elles ne sont pas reconnues s'il y a un espace avant.

 

.name nom

Définit le nom de la fonction ou du bloc, ainsi que du fichier lors de la sauvegarde.

.profile NomDeProfil

Spécifie le "profil" du CPU cible (ses caractéristiques) afin que l'assembleur bas niveau sache quels opcodes sont valides (entre autres).

.subst NouveauMot Mots à substituer

UTILISER AVEC PRUDENCE, SERA ABANDONNÉ DANS LE FUTUR

Définit une substitution à la manière de #define en langage C. Le premier mot sera substitué par la suite avec les mots suivants, avant d'envoyer le résultat à l'assembleur de bas niveau.

. NouveauMot

C'est une sorte de "label" qui substitue NouveauMot avec l'adresse de l'instruction qui suit.

.org Adresse

Normalement, un programme commence à l'adresse 0 mais on peut spécifier une adresse positive pour le premier octet du fichier. La directive .org doit donc se trouver au début d'un fichier, avant que des instructions soient assemblées.

La valeur spéciale auto (au lieu d'un nombre positif) indique que le bloc de code est indépendant de la position et peut être logé n'importe où par l'assembleur.

.align Nombre

Ajoute des octets de remplissage (égaux à 0) pour que la prochaine ligne ait une adresse multiple de "Nombre". "Nombre" doit être une puissance de 2 (1, 2, 4, 8, 16, 32, 64, 128, 256, 512). Les instructions du YASEP doivent être à des adresses paires, ce qui correspond à .align 2.

 

; Ceci est un exemple simple qui utilise les mots-clés ci-dessus.

.name ExempleBête ; lors d'une sauvegarde,
; le fichier sera automatiquement nommé ExempleBête.yas

.profile YASEP16 ; Le programme s'attend à tourner sur
; une version générique 16 bits du YASEP.

.subst Compteur R1  ; remplace le nom du registre
.subst tmp R2       ; par un nom symbolique

.org 22 ; loge le code à l'adresse 22

  mov 0 Compteur
. LabelBoucle

  ; corps de la boucle : on fait ce qu'on veut ici

.align 32 ; la prochaine instruction sera
 ; alignée sur un bloc de 32 octets

; Boucle 65536 fois :
  add 1 Compteur ; incrémente le registre de comptage

  mov LabelBoucle tmp ; charge l'adresse de rebouclage dans R2
  mov tmp PC NZ Compteur ; reboucle si le compteur n'est pas à 0

  HALT 1 ; Fin du programme : bloque le CPU