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
halt
s програма нормально, але якщо випромінювання її видаляє, то пам'ять буде заповнена нулями, завдяки чому $8000
виконується нескінченна кількість разів, друкуючи багато нульових байтів.