Montar

Montar

Difícil

Instruções

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.

Modelo de execução

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.

Registradores

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.

Note

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.

Código assembly

Cada linha do código assembly pode ter uma de duas formas:

  • Rótulos marcam pontos específicos do programa que serão usados por algumas instruções.
  • Instruções representam operações executadas pelo programa.

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.

Rótulos

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.

Note

Os rótulos diferenciam maiúsculas de minúsculas. Isso significa que Start, start e sTart são todos rótulos diferentes.

Instruções de dois operandos

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:

  • maior que, se destination > source
  • igual, se destination == source
  • menor que, se destination < source

Como você mantém esse estado internamente fica a seu critério.

Instruções de um operando

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
Note

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.

Editar via GitHub O link abre em uma nova janela ou aba
Lean Exercism

Tudo pronto para começar Montar?

Crie sua conta no Exercism para aprender e dominar Lean com 100 exercícios e mentoria humana de verdade, tudo de graça.