Neste exercício, você vai definir a sintaxe e o modelo de execução de uma pequena linguagem inspirada na linguagem assembly x86-64.
O objetivo é formalizar como o código é escrito, analisado e executado dentro de um ambiente restrito.
Seu código assembly será escrito dentro de um construto especial:
let program := assemble!(
-- assembly code here
)
Esse construto deve produzir um programa, que simula uma função que executa o código assembly e retorna um valor.
Esse programa é então invocado com a seguinte sintaxe, em que a, b, c etc. são argumentos:
let result := program(a, b, c, ...)
Todos os argumentos e o valor de retorno são inteiros com sinal de 64 bits. É permitido um máximo de 6 argumentos.
O valor de retorno e todos os argumentos são passados em registradores. Sua linguagem deve suportar os seguintes registradores:
| registrador | papel |
|---|---|
rdi |
1º argumento |
rsi |
2º argumento |
rdx |
3º argumento |
rcx |
4º argumento |
r8 |
5º argumento |
r9 |
6º argumento |
rax |
valor de retorno |
Os registradores começam com um valor padrão de 0, a menos que sejam usados para passar argumentos para a função.
Por exemplo, no programa a seguir, todos os registradores têm o valor 0, com exceção de rdi, que é inicializado com 10, e rsi, que é inicializado com -20:
program(10, -20)
O valor de retorno do programa é sempre armazenado em rax.
Os nomes dos registradores não diferenciam maiúsculas de minúsculas.
Isso significa que rax, RAX e rAx se referem todos ao mesmo registrador.
Cada linha do código assembly pode ter uma de duas formas:
A menos que alguma instrução modifique isso, o fluxo de execução do programa é linear. Ou seja, uma linha anterior é totalmente executada antes de a linha seguinte ser executada.
Os rótulos têm a seguinte sintaxe:
<label>:
Eles não alteram o valor de nenhum registrador nem têm qualquer efeito sobre o programa além de marcar pontos específicos do código para que possam ser usados por instruções.
Os rótulos diferenciam maiúsculas de minúsculas.
Isso significa que Start, start e sTart são todos rótulos diferentes.
A maioria das instruções tem a seguinte sintaxe:
<opcode> <destination>, <source>
O opcode indica a operação que está sendo executada, usando os valores dos operandos destination e source.
O resultado da operação é armazenado no operando destination.
Por exemplo, a instrução add soma os valores dos operandos destination e source e armazena esse resultado no operando destination.
O operando destination é sempre um registrador, enquanto o operando source pode ser um registrador ou um número literal.
Esta é a lista de instruções que o seu programa deve suportar:
| opcode | operação 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 |
Além dessas, o seu programa deve suportar a instrução cmp de dois operandos.
Essa instrução não modifica nenhum registrador. Em vez disso, ela compara os operandos destination e source e define um estado interno do programa como uma de três opções:
destination > source
destination == source
destination < source
Como você mantém esse estado internamente fica a seu critério.
Existem algumas instruções que alteram o fluxo do programa, transferindo a execução para outro ponto do código. Elas são chamadas de instruções de salto.
Todas elas recebem apenas um operando, que é um rótulo que indica o destino do salto, ou seja, o ponto do código onde a execução vai continuar:
<opcode> <label>
A instrução jmp sempre faz o salto para o rótulo de destino.
As outras instruções de salto só fazem o salto se o estado interno do programa estiver definido com um valor específico.
Isso significa que elas geralmente são precedidas por uma instrução cmp.
Estas são as instruções de salto que o seu programa deve suportar:
| instrução | operação realizada |
|---|---|
jmp |
salto incondicional. O salto é sempre realizado |
je |
salta se o estado interno for igual |
jl |
salta se o estado interno for menor que |
jg |
salta se o estado interno for maior que |
Os opcodes das instruções, sejam de dois operandos ou de um operando, não diferenciam maiúsculas de minúsculas.
Isso significa que add, ADD e aDd se referem todos à mesma instrução.
Crie sua conta no Exercism para aprender e dominar Lean com 100 exercícios e mentoria humana de verdade, tudo de graça.