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 :
L'assembleur du YASEP est constitué de deux couches :
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.
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 :
Les seules exceptions à ces règles sont les formes FORM_Ri et FORM_RI car pour quelques instructions, la donnée immédiate représente une adresse de destination.
Et c'est tout ce qu'il y a à dire au sujet de la "syntaxe".
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, l'assembleur conserve seulement le nombre desiré de bits de poid faible, et efface les bits de poids forts. L'exemple suivant montre comment le nombre est tronqué : db 1234h.
Note : le désassembleur écrit toujours les valeurs en hexadécimal.
La capacité à créer des nombres arbitraires est souvent indispensable. L'assembleur dispose des trois 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)
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, la taille fait partie des nombreux paramètres qui constituent un "profile", que l'on peut spécifier au moyen du mot-clé .profile et configurer avec une interface.
L'assembleur basique reconnaît les symboles suivants et rejette tous les autres :
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.
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.
Normalement, toutes les données inutilisées ou non initialisées sont à zéro.
Cependant, lorsque certains champs ne sont pas utilisés (par FORM_ALONE or FORM_R...), ces champs peuvent être mis à d'autres valeurs, choisies par l'assembleur. Cela pourrait réduire la consommation et les émissions radioélectriques. grâce à des techniques comme la minimisation ou l'espacement des changements d'états (toggle minimization et toggle spacing).
L'assembleur du YASEP sera capable de calculer les bonnes valeurs assez facilement et utiliser les champs ignorés par certaines instructions. Une réduction de la consommation électrique de quelques pourcent pourrait être ainsi obtenue, bien que de plus grands gains viennent d'un meilleur encodage des instruction, d'un compilateur plus intelligent et d'algorithmes plus efficaces.
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 :
; Ceci est un exemple simple qui utilise les mots-clés ci-dessus. .name Dumb_Example ; lors d'une sauvegarde, ; le fichier sera automatiquement nommé Dumb_Example.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 ; Fin du programme : bloque le CPU