Les instructions du YASEP ont un format fixe sur 16 bits avec un champ optionnel, immédiat ou étendu de 16 bits. Ce format est identique pour YASEP16 et YASEP32 : YASEP32 peut utiliser des instructions sur 16 bits et YASEP16 décode les instructions sur 32 bits, en fait le décodeur d'instructions varie peu et doit accepter les deux longueurs.
Chaque instruction peut contenir :
Ces champs et flags ne sont pas tous utilisés en même temps. Certaines combinaisons sont impossibles par construction, d'autres n'ont aucun sens, le reste peut être transformé par l'assembleur. Cependant, une grande variété d'instructions (un opcode suivi de noms de regitres, de données immédiates et d'autres flags) peut être écrite par un programmeur. Cette page explique quelles "formes d'instructions" sont disponibles, comment elles sont structurées et quand on les utilise.
Une "forme d'instruction" est nommée selon son écriture en langage assembleur. C'est une séquence de lettres, désignant chacune un champ :
La structure du YASEP ne permet qu'un nombre limité de formes. Les deux bits de poids faible d'une instruction du YASEP déterminent comment interpréter les champs suivants, ce qui peut donner 4 combinaisons:
Le YASEP écrit le résultat des opérations (si il y a lieu) dans un registre dont l'adresse est donnée par les champs si4, snd ou dst3, en fonction de la forme de chaque instruction :
Il peut sembler étrange que si4 soit le champ de destination pour les instructions longues. C'est nécessaire car snd est le seul champ, dans le chemin de données, où la négation (le n de snd) peut avoir lieu.
Ce système est un peu complexe et pourrait rendre les développement complexes dans un futur lointain. Cependant c'est indispensable pour conserver la compacité et l'orthogonalité des instructions. De plus, dans le chemin de données, ce qui compte vraiment (pour la performance) c'est le temps d'accès aux opérandes. Le "calcul" de l'adresse de destination peut être réalisé rapidement, en même temps que l'ALU (une bonne douzaine de niveaux logiques). Et puisqu'on ne va pas utiliser de "bypass de pipeline" (on n'attend pas l'écriture d'un résultat pour le réutiliser), on peut avoir plus de liberté avec les champs et obtenir le maximum de chaque combinaison de bits. C'est l'assembleur qui va se charger de mettre les bonnes valeurs dans les bons bits.
Commençons par les formes d'instructions les plus simples. Les instructions courtes ont leur bit de poids faible égal à zéro, ce qui indique une longueur de 16 bits. Il reste juste assez de place pour un autre flag, un opcode de 6 bits et 2 champs de 4 bits.
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
si4 | snd | opcode | Reg | court | |||||||||||
R2 | R1 | ADD | 0 | 0 |
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
si4 | snd | opcode | Imm4 | court | |||||||||||
-3 | R1 | ADD | 1 | 0 |
En pratique, ces deux combinaisons peuvent être utilisées de 5 façons en langage assembleur, puisque chacun de ces champs peut être plus ou moins utile en fonction du type d'opération désirée :
ADD R1 R2 ; R2 <- R1+R2C'est l'une des formes les plus courantes. si4 et snd donnent les adresses des deux registres contenant les opérandes. snd est aussi la destination du résultat.
ADD 2 R3 ; R3 <- 2+R3Une autre forme courante. si4 donne une valeur immédiate, donc snd sert d'adresse de registre pour l'autre opérande source ainsi que la destination.
PUT R1 3 ; Envoie le contenu de R1 dans le registre spécial n°3C'est une autre manière d'écrire une forme iR quand l'instruction utilise une valeur immédiate comme adresse de destination. Dans ce cas, il faut respecter la première règle du langage assembleur (la destination est la dernière opérande d'une instruction).
La forme iR est encodée exactement comme Ri :
PUT A5 4 ; => 4762h GET 4 A5 ; => 4742hLa valeur immédiate "4" reste à la même place mais c'est caché par l'assembleur. D'autre part, il est préférable de se conformer à quelques règles simples et générales qui facilitent le codage, en rendant l'ordre des opérandes indépendant des détails des opcodes.
NEG R1 ; en interne : R1 = -R1C'est aussi une façon courte d'écrire que les deux champs sont le même registre. L'assembleur va correctement remplir les champs si4 et snd si cette forme est acceptable pour l'opcode désiré.
CRIT 3 ; désactive les interruptions pour les 3 instructions suivantesRarement utilisé mais parfois nécessaire. si4 contient la valeur immédiate et snd est ignoré.
NOP ; pas d'opération, tout les champs sont ignorés.C'est un cas extrême où aucune opérande n'est nécessaire. si4 et snd sont ignorés.
YASEP interprète les instructions comme ayant une longueur de 32 bits quand leur bit de poids faible est à 1. Les 16 bits de poids faible sont exactement les mêmes que dans les formes courtes. Le second bit de poids faible indique comment interpréter le demi-mot de poids fort :
31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
Imm16 | si4 | snd | opcode | imm16 | long | ||||||||||||||||||||||||||
1234h | R2 | R1 | ADD | 0 | 1 |
ADD 1234h R2 R1 ; R1 <- 1234h + R2Cette forme est assez courante. En plus de l'opérande immédiate sur 16 bits, snd fournit l'adresse du registre qui sert de deuxième opérande. si4 donne la destination du résultat.
GET 1234h R1 ; R1 <- SR[1234h]Cette forme est utilisée pour les instructions GET ou MOV. Le champ snd est ignoré parce que seul si4 (ici, le champ d'adresse de destination) a un sens pour l'opération. L'assembleur détecte aussi si la valeur immédiate tient dans 4 bits pour utiliser une forme courte (FORM_iR) ou longue, et optimiser la taille du code binaire.
C'est aussi utilisé par les instructions booléennes (ROP2) et l'ASU (ADD et SUB) pour simuler FORM_iR mais avec une valeur immédiate sur 16 bits. Lorsqu'il trouve le flag ALIAS_IRR, l'assembleur va utiliser FORM_IRR et mettre la même adresse de registre dans les deux champs si4 et snd.
; Ces deux instructions font la même chose et sont encodées de la même manière : OR 1234h R1 R1 OR 1234h R1
.profile YASEP32 ; Les 4 bits de poids fort sont ignorés par YASEP16 CALL 12345h R1 MOV 12345h R1
PUT R1 1234h ; SR[1234h] <- R1Cette forme est uniquement utilisée pour l'instruction PUT. Elle est identique à FORM_IR mais l'ordre des opérandes est inversé par l'assembleur pour obéir à la règle n°1 selon laquelle "La destination est écrite à la fin de l'instruction".
Les formes dites "étendues" réutilisent la structure des formes courtes et rajoutent 16 bits pour fournir d'autres fonctionnalités :
31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
dst3 | Reg | si4 | snd | opcode | Ext | long | |||||||||||||||||||||||||
R3 | 0 | R2 | R1 | ADD | 1 | 1 |
ADD R1 R2 R3 ; R3 <- R1+R2si4 (R1) et snd (R2) sont tous les deux des registres sources, le résultat est écrit dans dst3 (R3).
31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
dst3 | Reg | si4 | snd | opcode | Ext | long | |||||||||||||||||||||||||
R3 | 1 | 5 | R1 | ADD | 1 | 1 |
ADD 5 R1 R3 ; R3 <- R1+5La valeur immédiate sur 4 bits de si4 (5) et la valeur du registre indiqué par snd (R1) sont les opérandes de l'addition, dont le résultat est écrit dans dst3 (R3).
Imm4 | code | valeur décodée |
-8 | 1000 | -16 |
-7 | 1001 | -14 |
-6 | 1010 | -12 |
-5 | 1011 | -10 |
-4 | 1100 | -8 |
-3 | 1101 | -6 |
-2 | 1110 | -4 |
-1 | 1111 | -2 |
0 | 0000 | 16 |
1 | 0001 | 18 |
2 | 0010 | 20 |
3 | 0011 | 22 |
4 | 0100 | 8 |
5 | 0101 | 10 |
6 | 0110 | 12 |
7 | 0111 | 14 |
Les instructions étendues du YASEP disposent de 7 bits pour définir la condition (ou le prédicat) qui valide (ou non) l'écriture du résultat de l'instruction courante. Ils permettent d'encoder trois types de conditions :
Toutes les conditions peuvent être inversées par le bit N. Donc on peut coder le prédicat "jamais" en inversant la condition "toujours" (qui est celle par défaut).
L'encodage est tel que la condition "toujours" (ALWAYS) est représentée avec tous les bits à 0.
Le registre "shadow" est pour l'instant indéterminé. Pour l'instant, on considère qu'il s'agit d'une copie locale du registre R1 mais on pourra le changer avec un SR pour indiquer s'il s'agit d'un port d'entrées ou d'autres choses encore.
A compléter plus tard... (autoupdates...)