Z80Golf , 53 36 34 байт
-16 байт завдяки @Lynn
-2 байт завдяки @Neil
Оскільки це лише машинний код Z80, у цьому є багато недрукованих матеріалів, тому маємо xxd -r-реверсивний шестигранник:
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
Спробуйте в Інтернеті! (вичерпний тестер в Python)
Пояснення
z80golf - гіпотетична машина Z80 Anarchy Golf, де call $8000є путчар, гетчар call $8003, haltробить інтерпретатор вихід, ваша програма розміщена на $0000, а вся інша пам'ять заповнена нулями. Зробити програми, захищені від радіаційного монтажу, досить складно, але загальноприйнятною методикою є використання однобайтових інструкцій безвідмовних дій. Наприклад,
or c ; b1 ; a = a | c
просто один байт, і a | c | c == a | c, таким чином, можна зробити радіаційно-захисним, просто повторивши інструкцію. На Z80 8-бітове негайне навантаження - це два байти (де безпосереднє знаходиться у другому байті), тому ви можете надійно завантажувати деякі значення в регістри. Це те, що я спочатку робив на початку програми, тож ви можете проаналізувати більш довгі варіанти, які я архівував у нижній частині відповіді, але потім я зрозумів, що існує більш простий спосіб.
Програма складається з двох незалежних корисних навантажень, де одна з них могла бути пошкоджена радіацією. Я перевіряю, чи був видалений байт і чи був видалений байт до другої копії корисного навантаження, перевіряючи значення деяких абсолютних адрес пам'яті.
По-перше, нам потрібно вийти, якщо не спостерігалося випромінювання:
or a, (ix+endbyte) ; dd b6 21 ; a |= memory[ix+0x0021]
jr nz, midbyte ; 20 10 ; jump to a halt instruction if not zero
Якщо будь-який байт був видалений, то всі байти зміщаться і $0020будуть містити останній 76, тому $0021буде нуль. Ми можемо дозволити собі випромінювати початок програми, навіть якщо надмірності практично немає:
- Якщо зміщення стрибка
$10буде знято, то випромінювання буде правильно виявлено, стрибок не буде здійснено, а зміщення не матиме значення. Перший байт наступної інструкції буде використано, але оскільки він розроблений, щоб бути стійким до видалення байтів, це не має значення.
- Якщо
$20вилучити код кодування , то зсув стрибка $10буде декодувати як djnz $ffe4(споживаючи наступний байт інструкції як зміщення - див. Вище), що є командою циклу - декремент B, і стрибок, якщо результат не дорівнює нулю. Тому що ffe4-ffffзаповнений нулями (nop ями), і лічильник програм обгортається, це запустить початок програми 256 разів, а потім, нарешті, продовжить. Я вражений цим твором.
- Якщо вилучити заголовок
$dd, решта фрагменту фрагменту зробіть як or (hl) / ld ($1020), hl, а потім перейдіть до наступної частини програми. orЧи не буде змінювати будь - які важливі регістри, а також тому , що HL дорівнює нулю в цій точці, то запис буде також скорочуються.
- Вилучення
$b6волі зробить решта декодування якld ($1020), ix і далі, як описано вище.
- Видалення
$21волі змусить декодер з'їсти $20, викликаючи djnzповедінку.
Зауважте, що використання or a, (ix+*)економить два байти за ld a, (**) / and a / and aрахунок інтегрованої перевірки нуля.
Тепер нам потрібно вирішити, яку з двох примірників корисного вантажу виконати:
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
Дві копії розділені точкою, оскільки для вибору між ними використовується відносний стрибок, і випромінювання могло змістити програму таким чином, щоб перехід пропустив перший байт після пункту призначення. Крім того, nop кодується як нуль, що полегшує виявлення зміщених байтів. Зауважте, що не має значення, яку корисну навантаження обрано, якщо сам комутатор пошкоджений, оскільки тоді обидві копії є безпечними. Давайте переконаємось, що вона не вскочить у неініціалізовану пам'ять, хоча:
- Видалення
$ddзробить наступні два байти декодування якor (hl) / dec d . Клобери Д. Немає нічого.
- Видалення
$b6створить бездокументоване довше кодування dec d. Само, як і вище.
- Видалення
$15буде $28замість цього зчитуватися як зсув, а виконання триватиметься в $0c, як показано нижче.
- Коли
$28зникає, $0cрозшифровується як inc c. Корисне навантаження не хвилює c.
- Видалення
$0c- саме для цього потрібен nop. Інакше перший байт корисного навантаження був би прочитаний як зміщення стрибка, і програма перескочила б у неініціалізовану пам'ять.
Сама корисна навантаження досить проста. Я думаю, що невеликий розмір рядка робить цей підхід меншим, ніж цикл, і простіше зробити цей спосіб незалежним від позиції. eВ beepповторах, так що я можу збрити один ld a. Крім того , оскільки вся пам'ять між $0038і $8000обнуляється, я можу впасти через нього і використовувати більш короткий rstваріант з callінструкції, яка працює тільки $0, $8,$10 і так далі, аж до$38 .
Старіші підходи
64 байти
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 байт
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 байти
Це має пояснення в історії редагування, але воно не надто відрізняється.
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
Що робити: будь-який не порожній вихід був чудовим, а не звуковий сигнал
1 байт
v
halts програма нормально, але якщо випромінювання її видаляє, то пам'ять буде заповнена нулями, завдяки чому $8000виконується нескінченна кількість разів, друкуючи багато нульових байтів.