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 de retorno son enteros con signo de 64 bits. Se permite un máximo de 6 argumentos.
El valor de retorno y todos los argumentos se pasan en registros. Tu lenguaje debe admitir los siguientes registros:
| registro | rol |
|---|---|
rdi |
1.er argumento |
rsi |
2.º argumento |
rdx |
3.er argumento |
rcx |
4.º argumento |
r8 |
5.º argumento |
r9 |
6.º argumento |
rax |
valor de retorno |
Los registros comienzan 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, con la excepción de rdi, que se inicializa con 10, y rsi, que se inicializa con -20:
program(10, -20)
El valor de retorno 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 al mismo registro.
Cada línea del código ensamblador puede tener una de dos formas:
A menos que alguna instrucción lo modifique, el flujo de ejecución del programa avanza de forma lineal. Esto significa que una línea anterior se ejecuta por completo antes de que se ejecute 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 específicos del código para que las instrucciones los usen.
Las etiquetas distinguen entre mayúsculas y minúsculas.
Esto significa que Start, start y sTart son etiquetas diferentes.
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 este 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 instrucciones que tu programa debe admitir:
| código de operación | 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 |
Aparte 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 establece un estado interno del programa en una de tres opciones:
destination > source
destination == source
destination < source
Depende de ti cómo llevar internamente el seguimiento de este estado.
Hay algunas instrucciones que alteran el flujo del programa y transfieren la ejecución a otro punto del código. Se llaman instrucciones de salto.
Todas 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 salta a la etiqueta destino.
Las demás instrucciones de salto solo hacen el salto si el estado interno del programa está establecido en un valor específico.
Esto significa que normalmente van precedidas por 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 siempre se realiza |
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, ya sean de dos operandos o de un operando, no distinguen entre mayúsculas y minúsculas.
Esto significa que add, ADD y aDd se refieren a la misma instrucción.
Regístrate en Exercism para aprender y dominar Lean con 100 ejercicios y mentoría humana real, todo gratis.