Assemble

Assemble

Difficile

Instructions

Dans cet exercice, tu vas définir la syntaxe et le modèle d'exécution d'un petit langage inspiré du langage assembleur x86-64.

L'objectif est de formaliser la façon dont le code est écrit, analysé et exécuté dans un environnement restreint.

Modèle d'exécution

Ton code assembleur s'écrit à l'intérieur d'une construction particulière :

let program := assemble!(
    -- assembly code here
)

Cette construction doit produire un programme, qui simule une fonction exécutant le code assembleur et renvoyant une valeur. Ce programme est ensuite appelé à l'aide de la syntaxe suivante, où a, b, c, etc. sont des arguments :

let result := program(a, b, c, ...)

Tous les arguments et la valeur de retour sont des entiers signés sur 64 bits. Un maximum de 6 arguments est autorisé.

Registres

La valeur de retour et tous les arguments sont transmis dans des registres. Ton langage doit prendre en charge les registres suivants :

registre rôle
rdi 1er argument
rsi 2e argument
rdx 3e argument
rcx 4e argument
r8 5e argument
r9 6e argument
rax valeur de retour

Les registres démarrent avec une valeur par défaut de 0, sauf lorsqu'ils servent à transmettre des arguments à la fonction.

Par exemple, dans le programme suivant, tous les registres valent 0, à l'exception de rdi, qui est initialisé à 10, et de rsi, qui est initialisé à -20 :

program(10, -20)

La valeur de retour du programme est toujours stockée dans rax.

Note

Les noms de registres sont insensibles à la casse. Cela signifie que rax, RAX et rAx désignent tous le même registre.

Code assembleur

Chaque ligne du code assembleur peut prendre deux formes :

  • Les étiquettes marquent des endroits précis du programme, que certaines instructions utilisent.
  • Les instructions représentent les opérations exécutées par le programme.

Sauf si une instruction en modifie le cours, l'exécution du programme se déroule de manière linéaire. Cela signifie qu'une ligne est entièrement exécutée avant que la ligne suivante ne le soit.

Étiquettes

Les étiquettes ont la syntaxe suivante :

<label>:

Elles ne modifient la valeur d'aucun registre et n'ont aucun autre effet sur le programme que de marquer des endroits précis du code pour que les instructions puissent les utiliser.

Note

Les étiquettes sont sensibles à la casse. Cela signifie que Start, start et sTart sont toutes des étiquettes différentes.

Instructions à deux opérandes

La plupart des instructions ont la syntaxe suivante :

<opcode> <destination>, <source>

L'opcode indique l'opération exécutée, à partir des valeurs des opérandes destination et source. Le résultat de l'opération est stocké dans l'opérande destination.

Par exemple, l'instruction add additionne les valeurs des opérandes destination et source et stocke ce résultat dans l'opérande destination.

L'opérande destination est toujours un registre, tandis que l'opérande source peut être un registre ou un nombre littéral.

Voici la liste des instructions que ton programme doit prendre en charge :

opcode opération effectuée
mov destination := source
add destination := destination + source
sub destination := destination - source
mul destination := destination * source
div destination := destination / source
xor destination := destination ^^^ source
and destination := destination &&& source
or destination := destination ||| source
shl destination := destination <<< source
shr destination := destination >>> source

En plus de celles-ci, ton programme doit prendre en charge l'instruction cmp à deux opérandes.

Cette instruction ne modifie aucun registre : elle compare les opérandes destination et source et place un état interne du programme sur l'une des trois options suivantes :

  • supérieur à, si destination > source
  • égal à, si destination == source
  • inférieur à, si destination < source

Libre à toi de choisir comment suivre cet état en interne.

Instructions à un opérande

Certaines instructions modifient le cours du programme en transférant l'exécution vers un autre point du code. On les appelle les instructions de saut.

Elles ne prennent qu'un seul opérande : une étiquette qui indique la cible du saut, c'est-à-dire le point du code où l'exécution se poursuivra :

<opcode> <label>

L'instruction jmp effectue toujours le saut vers l'étiquette cible.

Les autres instructions de saut n'effectuent le saut que si l'état interne du programme correspond à une valeur précise. Cela signifie qu'elles sont généralement précédées d'une instruction cmp.

Voici les instructions de saut que ton programme doit prendre en charge :

instruction opération effectuée
jmp saut inconditionnel. Le saut est toujours effectué
je saute si l'état interne est égal à
jl saute si l'état interne est inférieur à
jg saute si l'état interne est supérieur à
Note

Les opcodes d'instruction, qu'ils soient à deux opérandes ou à un opérande, sont insensibles à la casse. Cela signifie que add, ADD et aDd désignent toutes la même instruction.

Modifie via GitHub Le lien s'ouvre dans une nouvelle fenêtre ou un nouvel onglet
Lean Exercism

Prêt à commencer Assemble ?

Inscris-toi sur Exercism pour apprendre et maîtriser Lean avec 100 exercices, et un vrai mentorat humain, le tout gratuitement.