Z80Golf , 53 36 34 bajtów
-16 bajtów dzięki @Lynn
-2 bajtów dzięki @Neil
Ponieważ jest to tylko kod maszynowy Z80, jest w nim wiele niedrukowalnych elementów, więc należy mieć xxd -r
odwracalny zrzut heksowy:
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
Wypróbuj online! (wyczerpujący tester w Pythonie)
Wyjaśnienie
z80golf to hipotetyczna maszyna Z80 firmy Anarchy Golf, w której call $8000
jest putchar, call $8003
jest getchar, halt
powoduje wyjście interpretera, program jest umieszczony $0000
, a cała inna pamięć jest wypełniona zerami. Uczynienie programów odpornymi na promieniowanie w asemblerze jest dość trudne, ale ogólnie użyteczną techniką jest stosowanie jednobajtowych idempotentnych instrukcji. Na przykład,
or c ; b1 ; a = a | c
jest tylko jednym bajtem, a a | c | c == a | c
więc można go zabezpieczyć przed promieniowaniem poprzez powtórzenie instrukcji. W przypadku Z80 8-bitowe natychmiastowe ładowanie to dwa bajty (gdzie natychmiastowe jest w drugim bajcie), więc możesz pewnie ładować niektóre wartości do rejestrów. Tak właśnie zrobiłem na początku programu, więc możesz przeanalizować dłuższe warianty, które zarchiwizowałem na dole odpowiedzi, ale potem zrozumiałem, że istnieje prostszy sposób.
Program składa się z dwóch niezależnych ładunków, z których jeden mógł zostać uszkodzony przez promieniowanie. Sprawdzam, czy bajt został usunięty i czy usunięty bajt był przed drugą kopią ładunku, sprawdzając wartości niektórych bezwzględnych adresów pamięci.
Najpierw musimy wyjść, jeśli nie zaobserwowano promieniowania:
or a, (ix+endbyte) ; dd b6 21 ; a |= memory[ix+0x0021]
jr nz, midbyte ; 20 10 ; jump to a halt instruction if not zero
Jeśli jakikolwiek bajt został usunięty, wszystkie bajty zostaną przesunięte i $0020
będą zawierać ostatni 76
, więc $0021
będzie to zero. Możemy sobie pozwolić na promieniowanie początku programu, mimo że praktycznie nie ma redundancji:
- Jeśli przesunięcie skoku
$10
zostanie usunięte, wówczas promieniowanie zostanie poprawnie wykryte, skok nie zostanie wykonany, a przesunięcie nie będzie miało znaczenia. Pierwszy bajt następnej instrukcji zostanie zużyty, ale ponieważ ma on być odporny na usuwanie bajtów, nie ma to znaczenia.
- Jeśli kod operacji skoku
$20
zostanie usunięty, wówczas przesunięcie skoku $10
zostanie zdekodowane jako djnz $ffe4
(pochłanianie następnego bajtu instrukcji jako przesunięcia - patrz wyżej), który jest instrukcją pętli - zmniejszenie B, i przeskoczy, jeśli wynik nie będzie równy zero. Ponieważ ffe4-ffff
jest wypełniony zerami nop
, a licznik programu się zawija, spowoduje to uruchomienie programu 256 razy, a następnie kontynuację. Dziwi mnie, że to działa.
- Usunięcie
$dd
spowoduje, że reszta kodu zostanie zdekodowana jako or (hl) / ld ($1020), hl
, a następnie przejdzie do następnej części programu. or
Nie zmieni żadnych ważnych rejestrów, a ponieważ HL wynosi zero w tym momencie zapisu będzie również anulować.
- Usunięcie
$b6
spowoduje, że reszta dekoduje jako ld ($1020), ix
i będzie postępować jak wyżej.
- Usunięcie
$21
spowoduje, że dekoder zje $20
, wywołując djnz
zachowanie.
Zauważ, że użycie or a, (ix+*)
pozwala zaoszczędzić dwa bajty ld a, (**) / and a / and a
dzięki zintegrowanemu sprawdzeniu zera.
Teraz musimy zdecydować, którą z dwóch kopii ładunku wykonać:
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
Dwie kopie są oddzielone znakiem nop, ponieważ do wyboru między nimi używany jest skok względny, a promieniowanie mogło przesunąć program w taki sposób, że skok przeskoczyłby o pierwszy bajt za miejscem docelowym. Dodatkowo nop jest kodowany jako zero, co ułatwia wykrycie przesuniętych bajtów. Zauważ, że nie ma znaczenia, który ładunek zostanie wybrany, jeśli sam przełącznik jest uszkodzony, ponieważ wtedy obie kopie są bezpieczne. Upewnijmy się jednak, że nie wskoczy do niezainicjowanej pamięci:
- Usunięcie
$dd
spowoduje dekodowanie kolejnych dwóch bajtów jako or (hl) / dec d
. Clobbers D. Nic wielkiego.
- Usunięcie
$b6
spowoduje utworzenie nieudokumentowanego dłuższego kodowania dec d
. Tak samo jak powyżej.
- Usunięcie
$15
odczyta $28
zamiast tego przesunięcie, a wykonanie będzie kontynuowane od $0c
, jak poniżej.
- Kiedy
$28
znika, $0c
dekodowany jest jako inc c
. Ładowność nie ma znaczenia c
.
- Usuwanie
$0c
- po to jest nop. W przeciwnym razie pierwszy bajt ładunku zostałby odczytany jako przesunięcie skoku, a program wskoczyłby do niezainicjowanej pamięci.
Sama ładowność jest dość prosta. Myślę, że mały rozmiar sznurka sprawia, że to podejście jest mniejsze niż pętla i w ten sposób łatwiej jest uniezależnić pozycję. e
W beep
powtórzeń, więc mogę zgolić jedną ld a
. Również dlatego, że cała pamięć pomiędzy $0038
i $8000
jest wyzerowany, mogę spaść przez niego i używać krótszy rst
wariant call
instrukcji, który działa tylko na $0
, $8
, $10
i tak dalej, aż do $38
.
Starsze podejścia
64 bajty
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 bajtów
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 bajty
Ten ma wyjaśnienie w historii edycji, ale nie jest inaczej.
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
Co jeśli: każde niepuste wyjście jest w porządku zamiast sygnału dźwiękowego
1 bajt
v
halt
s program normalnie, ale jeśli promieniowanie go usunie, pamięć zostanie zapełniona zerami, co spowoduje $8000
wykonanie nieskończonej liczby razy i wydrukowanie dużej liczby bajtów zerowych.