Ziel ist es, ein vollständiges Programm zu schreiben, das die Universal Machine von ICFP 2006 mit dem kürzesten Code emuliert. Die Universalmaschine verfügt über einen sehr einfachen Befehlssatz, der hier erläutert wird . Der Emulator muss einen Dateinamen aus dem Befehlszeilenargument lesen und die Datei als Programm ausführen, sodass Ihre Sprache Befehlszeilenargumente und stdin / out in irgendeiner Weise unterstützen muss. Der Emulator muss die Sandmarkierung innerhalb einer angemessenen Zeit (nicht Jahrzehnten) abschließen . Hier ist eine kurze Erklärung des Befehlssatzes:
Die Maschine hat acht Register, die jeweils eine 32-Bit-Ganzzahl ohne Vorzeichen enthalten.
Die Maschine enthält einen indizierten Satz von Arrays von 32-Bit-Ganzzahlzellen ohne Vorzeichen.
Kurz gesagt, der Zuweisungsbefehl gibt eine undurchsichtige 32-Bit-Uint zurück, die das Handle für das erstellte Array ist, das eine statische Größe hat und 32-Bit-Uint-Elemente enthält.
Das 0. Array bezeichnet das Programm. Es wird beim Start aus einer Big-Endian-Datei geladen.
Es gibt auch einen Anweisungszeiger, der auf eine Zelle im Array 0 zeigt.
Bei jedem Schritt wird eine Anweisung aus der Zelle gelesen, auf die der Zeiger zeigt, und der Zeiger wird inkrementiert, bevor etwas getan wird.
Die 4 höchstwertigen Bits repräsentieren den Opcode.
Wenn der Opcode 13 ist, stellen die nächsten 3 Bits das Register dar, und die anderen 25 stellen die Nummer dar, die in das Register geschrieben wird.
Andernfalls repräsentieren die 9 niedrigstwertigen Bits drei Register, beispielsweise A, B und C, wobei C durch die 3 niedrigstwertigen Bits repräsentiert wird.
Dann passiert abhängig vom Opcode Folgendes:
0. A = B, außer 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. Der Emulator beendet
8. B = Zuweisen (C)
9. Freigeben (C)
10. Ein Zeichen von C an Standard ausgeben
11. Ein Zeichen eingeben von stdin nach c
12. Kopieren Sie das Array B in das Array 0 und setzen Sie den Zeiger auf C
Ich habe eine unnötig komplexe, aber total schnelle Implementierung (ab) mit x86_64-Jitted-Assembly geschrieben (der Spaß beginnt mit emit ()) , die Ihnen sicherlich helfen würde, wenn Sie einige Aspekte der Maschine falsch verstehen .
quelle
Antworten:
PHP:
443 416384 Bytes* Wieder überarbeitet *. Es ist so klein, wie ich es jetzt möglicherweise bekommen kann. Ich habe einige Variablen am anderen Ende des Alphabets beibehalten, damit die Regex, die die $ -Zeichen einfügt, die STDIN-Konstante nicht beeinträchtigt. Deshalb hier ein kleines Glossar:
unpack()
Arrays zurückgegeben werden)Eine Division ohne Vorzeichen ist ein subtiles Problem (dies
*1
ist erforderlich, um sicherzustellen, dass große Zahlen auf das richtige int zurückgesetzt werden), aber der Rest der Arithmetik lässt sich leicht auf 32 Bit beschränken, indem das ArithmetikregisterA|=0
nach jedem Befehl mit 0 ( ) ODER-verknüpft wird .Ich fand dieses Projekt wirklich interessant, aber das Bestreben, die Anzahl der Zeichen zu minimieren, machte es langsam und unelegant. Daher erstellte ich auch eine einfache (nicht golfene) Java-Version, mit der der Sandmark in wenigen Minuten fertiggestellt werden kann, anstatt den ganzen Tag in Anspruch zu nehmen:
quelle
Perl, 407
Es sieht so aus, als wäre die Frage zu komplex, eigentlich ist sie sehr einfach.
Ich bin immer noch sehr neu in Perl, hier ist es sowieso
Es läuft sehr langsam, wahrscheinlich 800x langsamer als das JITed x86_64.
Außerdem hat ein Freund von mir eine Referenz-C-Implementierung durchgeführt
quelle
if(((Memory[++PC]>>28)&15) == 13) { Registers[(Memory[PC]>>25)&7] = (Memory[PC]&0x01ffffff);
Der Befehl wird nicht zwischengespeichert, sodass Opcodes, die nicht 13 sind, den nächsten Befehl vorab ausführen würden, nicht wahr?C,
924838825696646623Ich speichere einen "Zeiger" (Byte-Offset) in dem
b
in der Anweisung angegebenen Register und verwende jedes Register, das ein Array im Pseudocode kennzeichnet, auf die gleiche Weise (oder umgekehrt, um einen Zeiger wiederherzustellen), um später auf dieses Array zuzugreifen. Muss noch das Testprogramm ausprobieren ...Bearbeiten: Kommentare hinzugefügt.
Edit: feste Anweisung 12. Ändere den Zeiger, nicht die Anweisung im Speicher. Bei der Zählung werden alle Kommentare, Einrückungen und Zeilenumbrüche entfernt.
Bearbeiten: Es scheint jetzt ausgeführt zu werden, vorausgesetzt, ich interpretiere die Ergebnisse richtig. :) Die letzte Erkenntnis war, dass das Array 0 tatsächlich durch das Handle 0 referenziert wird , das sich in einem nicht initialisierten Register befindet. Eine sehr verdrehte kleine Maschine! :)
Bearbeiten: Debugging-Apparat neu geschrieben, um ihn zu verwenden,
write
anstattprintf
... Die Idee hier ist , Fehler zu entfernen . :) Edit:putchar()
undgetchar()
sind auch keine mitsbrk
. Es funktioniert jetzt und erscheint ziemlich schnell.Nur für Little-Endian gibt es eine 611- Zeichen-Version.
Eingekerbt und kommentiert, mit (erweitertem) kommentiertem Debugging-Apparat.
quelle
lbreak
und wie Sie unary-*
einint
d000108f c0000030
und beendet dann