En este ejercicio, definirás la sintaxis y el modelo de ejecución de un pequeño lenguaje inspirado en el lenguaje ensamblador x86-64.
El objetivo es formalizar cómo se escribe, se analiza y se ejecuta el código dentro de un entorno restringido.
Tu código ensamblador se escribirá dentro de una construcción especial:
let program := assemble!(
-- assembly code here
)
Esta construcción debe producir un programa, que simula una función que ejecuta el código ensamblador y devuelve un valor.
Luego, este programa se invoca con la siguiente sintaxis, donde a, b, c, etc. son argumentos:
let result := program(a, b, c, ...)
Todos los argumentos y el valor devuelto son enteros de 64 bits con signo. Se permite un máximo de 6 argumentos.
El valor devuelto y todos los argumentos se pasan en registros. Tu lenguaje debe admitir los siguientes registros:
| registro | rol |
|---|---|
rdi |
primer argumento |
rsi |
segundo argumento |
rdx |
tercer argumento |
rcx |
cuarto argumento |
r8 |
quinto argumento |
r9 |
sexto argumento |
rax |
valor devuelto |
Los registros empiezan con un valor predeterminado de 0, a menos que se usen para pasar argumentos a la función.
Por ejemplo, en el siguiente programa todos los registros tienen un valor de 0 salvo rdi, que se inicializa con 10, y rsi, que se inicializa con -20:
program(10, -20)
El valor devuelto del programa siempre se almacena en rax.
Los nombres de los registros no distinguen entre mayúsculas y minúsculas.
Esto significa que rax, RAX y rAx se refieren todos al mismo registro.
Cada línea del código ensamblador puede adoptar una de estas dos formas:
Salvo que alguna instrucción lo modifique, el flujo de ejecución del programa avanza de forma lineal. Esto significa que una línea se ejecuta por completo antes que la línea siguiente.
Las etiquetas tienen la siguiente sintaxis:
<label>:
No alteran el valor de ningún registro ni tienen ningún efecto sobre el programa aparte de marcar lugares concretos del código para que las instrucciones puedan usarlos.
Las etiquetas distinguen entre mayúsculas y minúsculas.
Esto significa que Start, start y sTart son etiquetas distintas.
La mayoría de las instrucciones tienen la siguiente sintaxis:
<opcode> <destination>, <source>
El opcode indica la operación que se ejecuta, usando los valores de los operandos destination y source.
El resultado de la operación se almacena en el operando destination.
Por ejemplo, la instrucción add suma los valores de los operandos destination y source y almacena ese resultado en el operando destination.
El operando destination es siempre un registro, mientras que el operando source puede ser un registro o un número literal.
Esta es una lista de las instrucciones que tu programa debe admitir:
| opcode | operación realizada |
|---|---|
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 |
Además de esas, tu programa debe admitir la instrucción cmp de dos operandos.
Esta instrucción no modifica ningún registro, sino que compara los operandos destination y source y fija un estado interno del programa a una de estas tres opciones:
destination > source
destination == source
destination < source
Cómo lleves la cuenta de este estado internamente es cosa tuya.
Hay algunas instrucciones que alteran el flujo del programa y transfieren la ejecución a otro punto del código. Se denominan instrucciones de salto.
Todas ellas toman un solo operando, que es una etiqueta que indica el destino del salto, es decir, el punto del código donde continuará la ejecución:
<opcode> <label>
La instrucción jmp siempre realiza el salto a la etiqueta de destino.
Otras instrucciones de salto solo saltan si el estado interno del programa tiene un valor concreto.
Esto significa que suelen ir precedidas de una instrucción cmp.
Estas son las instrucciones de salto que tu programa debe admitir:
| instrucción | operación realizada |
|---|---|
jmp |
salto incondicional. El salto se realiza siempre |
je |
salta si el estado interno es igual |
jl |
salta si el estado interno es menor que |
jg |
salta si el estado interno es mayor que |
Los códigos de operación de las instrucciones, tanto las de dos operandos como las de un operando, no distinguen entre mayúsculas y minúsculas.
Esto significa que add, ADD y aDd se refieren todos a la misma instrucción.
Regístrate en Exercism para aprender y dominar Lean con 100 ejercicios y mentoría humana real, todo gratis.