Z80Golf , 53 36 34 байта
-16 байт благодаря @Lynn
-2 байт благодаря @Neil
Так как это всего лишь машинный код Z80, в нем много непечатаемых файлов, поэтому используйте xxd -r
-reversible 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
Попробуйте онлайн! (исчерпывающий тестер в 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, поскольку для выбора между ними используется относительный переход, и радиация могла бы сдвинуть программу таким образом, чтобы переход пропустил первый байт после места назначения. Кроме того, 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
Если программа работает нормально, но если радиация удаляет ее, то память будет заполнена нулями, что будет $8000
выполнять бесконечное число раз, печатая много нулевых байтов.