Z80Golf , 53 36 34 Bytes
-16 Bytes dank @Lynn
-2 Bytes dank @Neil
Da es sich nur um Z80-Maschinencode handelt, sind in diesem Code viele nicht druckbare Dateien enthalten. Verwenden Sie also einen xxd -r
umkehrbaren Hexdump:
00000000: ddb6 2120 10dd b615 280c 003e 62ff 3e65 ..! ....(..>b.>e
00000010: ffff 3e70 ff76 003e 62ff 3e65 ffff 3e70 ..>p.v.>b.>e..>p
00000020: ff76 .v
Probieren Sie es online! (ausführlicher Tester in Python)
Erläuterung
z80golf ist Anarchy Golfs hypothetische Z80-Maschine, bei der call $8000
es sich um einen Putchar handelt.call $8003
einen Getchar handelt, halt
der den Interpreter beendet, Ihr Programm abgelegt $0000
wird und der gesamte andere Speicher mit Nullen gefüllt ist. Es ist ziemlich schwierig, Programme in der Assembly strahlungssicher zu machen, aber eine allgemein nützliche Technik ist die Verwendung von Anweisungen mit einem Byte im Idempotenten. Beispielsweise,
or c ; b1 ; a = a | c
ist nur ein Byte und a | c | c == a | c
kann daher durch einfaches Wiederholen des Befehls strahlungssicher gemacht werden. Beim Z80 besteht ein 8-Bit-Sofortladevorgang aus zwei Bytes (wobei der Sofortladevorgang sich im zweiten Byte befindet), sodass Sie auch einige Werte zuverlässig in Register laden können. Das habe ich ursprünglich zu Beginn des Programms getan, damit Sie die längeren Varianten analysieren können, die ich am Ende der Antwort archiviert habe, aber dann habe ich festgestellt, dass es einen einfacheren Weg gibt.
Das Programm besteht aus zwei unabhängigen Nutzlasten, von denen eine durch Strahlung beschädigt worden sein könnte. Ich überprüfe, ob ein Byte entfernt wurde und ob das entfernte Byte vor der zweiten Kopie der Nutzdaten lag, indem ich die Werte einiger absoluter Speicheradressen überprüfe.
Zuerst müssen wir gehen, wenn keine Strahlung beobachtet wurde:
or a, (ix+endbyte) ; dd b6 21 ; a |= memory[ix+0x0021]
jr nz, midbyte ; 20 10 ; jump to a halt instruction if not zero
Wenn irgendein Byte entfernt wurde, dann alle werden die Bytes verschieben und $0020
die letzte enthalten 76
, so$0021
eine Null. Wir können es uns leisten, den Beginn des Programms zu strahlen, obwohl es praktisch keine Redundanz gibt:
- Ist der Sprung versetzt
$10
entfernt wird, wird die Strahlung korrekt erfasst, der Sprung wird nicht ausgeführt, und der Versatz spielt keine Rolle. Das erste Byte des nächsten Befehls wird verbraucht, aber da es so konzipiert ist, dass es resistent gegen das Entfernen von Bytes ist, spielt dies keine Rolle.
- Wenn der Sprung - Opcode
$20
entfernt wird, dann versetzte der Sprung $10
wird als dekodieren djnz $ffe4
(raubend das nächste Befehlsbyte als Offset - siehe oben), die eine Schleifenanweisung ist - Dekrement - B, und zu springen , wenn das Ergebnis nicht null ist. Weil ffe4-ffff
mit Nullen gefüllt ist (nop
ist und der Programmzähler einen Zeilenumbruch durchführt, wird der Programmanfang 256-mal ausgeführt und anschließend fortgesetzt. Ich bin erstaunt, dass dies funktioniert.
- Wenn Sie das entfernen,
$dd
wird der Rest des Snippets als dekodiert or (hl) / ld ($1020), hl
und dann in den nächsten Teil des Programms verschoben. Das or
ändert keine wichtigen Register, und da HL zu diesem Zeitpunkt Null ist, wird das Schreiben ebenfalls abgebrochen.
- Wenn Sie das entfernen,
$b6
wird der Rest ld ($1020), ix
wie oben beschrieben dekodiert .
- Wenn Sie den entfernen
$21
, frisst der Decoder den $20
und löst das djnz
Verhalten aus.
Beachten Sie, dass durch die Verwendung von or a, (ix+*)
zwei Bytes ld a, (**) / and a / and a
durch die integrierte Prüfung auf Null gespart wird .
Wir müssen jetzt entscheiden, welche der beiden Kopien der Nutzlast ausgeführt werden soll:
or (ix+midbyte) ; dd b6 15
jr z, otherimpl ; 28 0c
nop ; 00
; first payload
ld a, 'b' ; 3e 62
rst $0038 ; ff
ld a, 'e' ; 3e 65
rst $0038 ; ff
rst $0038 ; ff
ld a, 'p' ; 3e 70
rst $0038 ; ff
midbyte:
halt ; 76
otherimpl:
nop ; 00
ld a, 'b' ; 3e 62
; ... ; ...
rst $0038 ; ff
endbyte:
halt ; 76
Die zwei Kopien sind durch ein NOP getrennt, da ein relativer Sprung verwendet wird, um zwischen ihnen zu wählen, und Strahlung das Programm auf eine Weise hätte verschieben können, die den Sprung zum Überspringen des ersten Bytes nach dem Ziel führen würde. Zusätzlich wird der NOP als Null codiert, wodurch es einfach ist, verschobene Bytes zu erkennen. Beachten Sie, dass es keine Rolle spielt, welche Nutzdaten ausgewählt werden, wenn der Switch selbst beschädigt ist, da dann beide Kopien sicher sind. Stellen wir aber sicher, dass es nicht in den nicht initialisierten Speicher springt:
- Durch Löschen
$dd
werden die nächsten zwei Bytes als dekodiertor (hl) / dec d
. Clobbers D. Keine große Sache.
- Durch das Löschen
$b6
wird eine undokumentierte längere Codierung für erstellt dec d
. Das gleiche wie oben.
- Beim Löschen
$15
wird $28
stattdessen der Wert als Versatz gelesen , und die Ausführung erfolgt $0c
wie unten beschrieben.
- Wenn
$28
verschwindet, wird der $0c
als dekodiert inc c
. Die Nutzlast kümmert sich nicht darum c
.
- Löschen
$0c
- dafür ist der NOP da. Andernfalls wäre das erste Byte der Nutzlast als Sprungoffset gelesen worden, und das Programm würde in den nicht initialisierten Speicher springen.
Die Nutzlast selbst ist ziemlich einfach. Ich denke, die geringe Größe der Zeichenfolge macht diesen Ansatz kleiner als eine Schleife, und es ist einfacher, auf diese Weise positionsunabhängig zu machen. Die e
in beep
Wiederholungen, so kann ich ein abrasieren ld a
. Auch, weil alle Speicher zwischen $0038
und auf $8000
Null gesetzt wird, kann ich durch sie fallen und eine kürzere verwenden rst
Variante der call
Anweisung, die nur funktioniert für $0
, $8
, $10
und so weiter, bis zu$38
.
Ältere Ansätze
64 Bytes
00000000: 2e3f 3f2e 3f3f 7e7e a7a7 201f 1e2b 2b1e .??.??~~.. ..++.
00000010: 2b2b 6b00 7ea7 2814 003e 62cd 0080 3e65 ++k.~.(..>b...>e
00000020: cd00 80cd 0080 3e70 cd00 8076 003e 62cd ......>p...v.>b.
00000030: 0080 3e65 cd00 80cd 0080 3e70 cd00 8076 ..>e......>p...v
58 Bytes
00000000: 2e39 392e 3939 7e7e a7a7 2019 3a25 00a7 .99.99~~.. .:%..
00000010: 2814 003e 62cd 0080 3e65 cd00 80cd 0080 (..>b...>e......
00000020: 3e70 cd00 8076 003e 62cd 0080 3e65 cd00 >p...v.>b...>e..
00000030: 80cd 0080 3e70 cd00 8076 ....>p...v
53 Bytes
Dieser hat eine Erklärung in der Bearbeitungshistorie, aber es ist nicht zu unterschiedlich.
00000000: 3a34 00a7 a720 193a 2000 a728 1400 3e62 :4... .: ..(..>b
00000010: cd00 803e 65cd 0080 cd00 803e 70cd 0080 ...>e......>p...
00000020: 7600 3e62 cd00 803e 65cd 0080 cd00 803e v.>b...>e......>
00000030: 70cd 0080 76 p...v
Was ist, wenn: eine nicht leere Ausgabe in Ordnung war anstelle eines Pieptons
1 Byte
v
halt
s das Programm normalerweise, aber wenn Strahlung es entfernt, dann ist der Speicher voll von Nullen, so $8000
dass unendlich oft ausgeführt wird und viele Null-Bytes gedruckt werden.