Montar

Montar

Difícil

Instruções

Neste exercício, vais definir a sintaxe e o modelo de execução de uma pequena linguagem inspirada na linguagem assembly x86-64.

O objetivo é formalizar a forma como o código é escrito, analisado e executado num ambiente restrito.

Modelo de execução

O teu código assembly será escrito dentro de uma construção especial:

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

Esta construção tem de produzir um programa, que simula uma função que executa o código assembly e devolve um valor. Este programa é depois 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 devolvido são inteiros com sinal de 64 bits. É permitido um máximo de 6 argumentos.

Registos

O valor devolvido e todos os argumentos são passados em registos. A tua linguagem tem de suportar os seguintes registos:

registo papel
rdi 1.º argumento
rsi 2.º argumento
rdx 3.º argumento
rcx 4.º argumento
r8 5.º argumento
r9 6.º argumento
rax valor devolvido

Os registos começam com um valor predefinido de 0, a menos que sejam usados para passar argumentos à função.

Por exemplo, no programa seguinte, todos os registos 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 devolvido do programa é sempre armazenado em rax.

Note

Os nomes dos registos não distinguem maiúsculas de minúsculas. Isto significa que rax, RAX e rAx se referem todos ao mesmo registo.

Código assembly

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

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

A menos que seja modificado por alguma instrução, o fluxo de execução do programa prossegue linearmente. Isto significa que uma linha anterior é totalmente executada antes de a linha seguinte ser executada.

Etiquetas

As etiquetas têm a seguinte sintaxe:

<label>:

Não alteram o valor de nenhum registo nem têm qualquer efeito sobre o programa, além de marcarem locais específicos do código para poderem ser usados por instruções.

Note

As etiquetas distinguem maiúsculas de minúsculas. Isto significa que Start, start e sTart são todas etiquetas 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á a ser 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 registo, enquanto o operando source pode ser um registo ou um número literal.

Esta é uma lista de instruções que o teu programa tem de suportar:

código de operação 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 destas, o teu programa tem de suportar a instrução cmp de dois operandos.

Esta instrução não modifica nenhum registo, mas 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

A forma como controlas internamente este estado é contigo.

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. São chamadas instruções de salto.

Todas recebem apenas um operando, que é uma etiqueta 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 faz sempre o salto para a etiqueta de destino.

As outras instruções de salto só fazem o salto se o estado interno do programa estiver definido como um valor específico. Isto significa que são normalmente precedidas por uma instrução cmp.

Estas são as instruções de salto que o teu programa tem de 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 códigos de operação das instruções, sejam de dois operandos ou de um operando, não distinguem maiúsculas de minúsculas. Isto significa que add, ADD e aDd se referem todos à mesma instrução.

Editar via GitHub A ligação abre numa nova janela ou separador
Lean Exercism

Estás pronto para começar Montar?

Inscreve-te no Exercism para aprenderes e dominares Lean com 100 exercícios, e mentoria humana real, tudo grátis.