L'objectif est d'écrire un programme complet qui émule la machine universelle d'ICFP 2006 avec le code le plus court. La machine universelle a un jeu d'instructions très simple expliqué ici . L'émulateur doit lire un nom de fichier à partir de l'argument de ligne de commande et exécuter le fichier en tant que programme, de sorte que votre langue doit prendre en charge les arguments de ligne de commande et stdin / out d'une manière ou d'une autre. L'émulateur doit terminer le point de repère dans un délai raisonnable (pas des décennies). Voici une courte explication du jeu d'instructions:
La machine possède huit registres contenant chacun un entier non signé 32 bits.
La machine contient un ensemble indexé de tableaux de cellules entières non signées 32 bits.
En bref, l'instruction d'allocation renvoie une uint 32 bits opaque qui est le descripteur du tableau créé, qui a une taille statique et contient des éléments uint 32 bits.
Le 0ème tableau indique le programme. Il est chargé à partir d'un fichier big-endian au démarrage.
Il existe également un pointeur d'instructions qui pointe vers une cellule du tableau 0.
À chaque étape, une instruction est lue dans la cellule vers laquelle pointe le pointeur et le pointeur est inceréré avant que quoi que ce soit soit fait.
Les 4 bits les plus significatifs représentent l'opcode.
Si l'opcode est 13, alors les 3 bits suivants représentent le registre, et les 25 autres représentent le nombre qui est écrit dans ledit registre.
Sinon, les 9 bits les moins significatifs représentent trois registres, disons A, B et C, où C est représenté par les 3 bits les moins significatifs.
Ensuite, en fonction de l'opcode, les événements suivants se produisent:
0. A = B sauf 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. L'émulateur se termine
8. B = allouer (C)
9. désallouer (C)
10. sortir un caractère de C vers stdout
11. entrer un caractère de stdin en C
12. copiez le tableau B dans le tableau 0 et réglez le pointeur sur C
J'ai écrit une implémentation (ab) inutilement complexe mais totalement rapide en utilisant l'assemblage jitted x86_64 (le plaisir commence dans emit ()) , ce qui vous aiderait à coup sûr si vous comprenez mal certains aspects de la machine.
la source
Réponses:
PHP:
443 416384 octets* Remanié à nouveau *. Il est aussi petit que possible. J'ai gardé quelques variables à l'extrémité de l'alphabet afin que l'expression régulière qui insère les signes $ ne modifie pas la constante STDIN, alors voici un petit glossaire:
unpack()
tableaux sont renvoyés)La division non signée est une nuisance subtile (elle
*1
est nécessaire pour garantir que les grands nombres sont restitués au bon int), mais le reste de l'arithmétique est facile à conserver sur 32 bits en effectuant une opération OU sur le registre arithmétique avec 0 (A|=0
) après chaque instruction.J'ai trouvé ce projet vraiment intéressant mais en s'efforçant de minimiser le nombre de caractères, il était lent et inélégant, j'ai donc également fait une version Java simple (non jouée au golf), qui peut compléter le point de repère en quelques minutes au lieu de prendre toute la journée:
la source
Perl, 407
Il semble que la question puisse sembler trop complexe, en fait c'est très simple.
Je suis encore très nouveau pour Perl, de toute façon ici c'est
Il fonctionne très lentement, probablement 800 fois plus lentement que celui JITed x86_64.
En outre, un de mes amis a fait une implémentation de référence C
la source
if(((Memory[++PC]>>28)&15) == 13) { Registers[(Memory[PC]>>25)&7] = (Memory[PC]&0x01ffffff);
L'instruction n'est pas mise en cache, donc tout opcode non 13 pré-exécutera l'instruction suivante, non?C,
924838825696646623Je stocke un "pointeur" (décalage d'octet) dans le registre désigné
b
dans l'instruction, et j'utilise tout registre qui désigne un tableau dans le pseudocode de la même manière (ou inversement, plutôt, pour reconstituer un pointeur) pour accéder à ce tableau plus tard. Encore faut-il essayer le programme de test ...Modifier: ajout de commentaires.
Edit: instruction fixe 12. changer le pointeur, pas l'instruction en mémoire. Le décompte est supprimé de tous les commentaires, retraits et nouvelles lignes.
Edit: Il semble fonctionner maintenant, en supposant que j'interprète correctement les résultats. :) La réalisation finale a été que le tableau 0 est en effet référencé par le handle 0, qui peut être trouvé dans un registre non initialisé. Une petite machine très tordue! :)
Edit: réécrit l'appareil de débogage à utiliser
write
au lieu deprintf
.... L'idée ici est de supprimer les bugs. :) Edit:putchar()
etgetchar()
sont également no-nos avecsbrk
. Cela fonctionne maintenant et apparaît assez rapidement.Pour le petit endian uniquement, il existe une version à 611 caractères.
Indenté et commenté, avec un appareil de débogage commenté (étendu).
la source
lbreak
et comment pouvez-vous unary-*
anint
d000108f c0000030
puis se