O objetivo é escrever um programa completo que emule a Máquina Universal do ICFP 2006 com o código mais curto. A Universal Machine possui um conjunto de instruções muito simples, explicado aqui . O emulador precisa ler um nome de arquivo no argumento da linha de comando e executar o arquivo como o programa, para que seu idioma tenha suporte para argumentos da linha de comando e stdin / out de alguma forma. O emulador precisa concluir a marca de areia dentro de um tempo razoável (não décadas). Aqui está uma breve explicação do conjunto de instruções:
A máquina possui oito registros, cada um contendo um número inteiro não assinado de 32 bits.
A máquina contém um conjunto indexado de matrizes de células inteiras não assinadas de 32 bits.
Em poucas palavras, a instrução de alocação retorna um uint opaco de 32 bits, que é o identificador da matriz criada, que tem um tamanho estático e contém elementos uint de 32 bits.
A matriz 0 indica o programa. Ele é carregado de um arquivo big-endian na inicialização.
Há também um ponteiro de instrução que aponta para uma célula na matriz 0.
Em cada etapa, uma instrução é lida na célula para a qual o ponteiro aponta e o ponteiro é inserido antes de qualquer coisa.
Os 4 bits mais significativos representam o código de operação.
Se o opcode for 13, os próximos 3 bits representam o registro e os outros 25 representam o número que está escrito no referido registro.
Caso contrário, os 9 bits menos significativos representam três registros, digamos, A, B e C, onde C é representado pelos 3 bits menos significativos.
Então, dependendo do código de operação, acontece o seguinte:
0. A = B, a menos que C == 0
1. A = B [C]
2. A [B] = C
3. A = B + C
4. A = B * C
5. A = B / C
6. A = ~ (B & C)
7. O emulador sai
8. B = aloca (C)
9. desaloca (C)
10. gera um caractere de C para stdout
11. insere um caractere de stdin para C
12. copie a matriz B para a matriz 0 e defina o ponteiro para C
Eu escrevi uma implementação (ab) desnecessariamente complexa, mas totalmente rápida, usando o assembly jitted x86_64 (a diversão começa em emit ()) , que com certeza o ajudaria se você não entender alguns aspectos da máquina.
fonte
Respostas:
PHP:
443 416384 bytes* Renovado novamente *. É tão pequeno quanto eu posso conseguir agora. Eu mantive algumas variáveis no final do alfabeto para que o regex que insere os sinais $ não altere a constante STDIN, então aqui está um pequeno glossário:
unpack()
retorna as matrizes)A divisão não assinada é um incômodo sutil (
*1
é necessário para garantir que grandes números retornem ao int correto), mas o restante da aritmética é fácil de manter em 32 bits ORing o registro aritmético com 0 (A|=0
) após cada instrução.Achei esse projeto realmente interessante, mas o esforço para minimizar a contagem de caracteres tornou-o lento e deselegante. Por isso, também fiz uma versão Java simples (sem golf), que pode completar a marca de areia em alguns minutos, em vez de demorar o dia todo:
fonte
Perl, 407
Parece que a pergunta pode parecer muito complexa, na verdade é muito simples.
Eu ainda sou muito novo em perl, de qualquer maneira aqui está
Ele roda muito devagar, provavelmente 800x mais lento que o JITed x86_64.
Além disso, um amigo meu fez uma implementação de referência C
fonte
if(((Memory[++PC]>>28)&15) == 13) { Registers[(Memory[PC]>>25)&7] = (Memory[PC]&0x01ffffff);
a instrução não é armazenada em cache; portanto, quaisquer opcodes que não sejam 13 pré-executariam a próxima instrução, não?C,
924838825696646623Eu armazeno um "ponteiro" (deslocamento de byte) no registro designado
b
na instrução e uso qualquer registro que designe uma matriz no pseudocódigo da mesma maneira (ou inverter, em vez disso, para reconstituir um ponteiro) para acessar essa matriz posteriormente. Ainda precisa tentar o programa de teste ...Editar: comentários adicionados.
Editar: instrução fixa 12. mude o ponteiro, não a instrução na memória. A contagem é com todos os comentários, recuos e novas linhas removidos.
Editar: parece estar em execução agora, supondo que eu esteja interpretando os resultados corretamente. :) A conclusão final foi que a matriz 0 é realmente referenciada pelo identificador 0, que pode ser encontrado em um registro não inicializado. Uma pequena máquina muito distorcida! :)
Edit: reescreve o aparelho de depuração para usar em
write
vez deprintf
.... A idéia aqui é remover os bugs. :) Editar:putchar()
egetchar()
também não gostam desbrk
. Agora funciona e parece bastante rápido.Apenas para little-endian, há uma versão de 611 caracteres.
Recuado e comentado, com aparelhagem de depuração comentada (estendida).
fonte
lbreak
e como você pode unary-*
umint
d000108f c0000030
e então sai