Z80Golf , 53 36 34 bytes
-16 bytes graças a @Lynn
-2 bytes graças a @Neil
Como este é apenas o código de máquina Z80, há muitos imprimíveis neste, portanto, tenha um xxd -rhexdump -reversible:
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
Experimente online! (testador exaustivo em Python)
Explicação
z80golf é a hipotética máquina Z80 da Anarchy Golf, onde call $8000é um putchar, call $8003é um getchar, haltfaz com que o intérprete saia, seu programa é colocado $0000e todas as outras memórias são preenchidas com zeros. Tornar os programas à prova de radiação na montagem é bastante difícil, mas uma técnica genericamente útil é usar instruções idempotentes de um byte. Por exemplo,
or c ; b1 ; a = a | c
é apenas um byte e a | c | c == a | c, portanto, pode ser protegido contra radiação apenas repetindo a instrução. No Z80, uma carga imediata de 8 bits é de dois bytes (onde a imediata está no segundo byte), portanto, você também pode carregar alguns valores nos registros de maneira confiável. Foi o que fiz originalmente no início do programa, para que você possa analisar as variantes mais longas que arquivei na parte inferior da resposta, mas depois percebi que existe uma maneira mais simples.
O programa consiste em duas cargas independentes, onde uma delas pode ter sido danificada pela radiação. Verifico se um byte foi removido e se o byte removido foi anterior à segunda cópia da carga, verificando os valores de alguns endereços de memória absolutos.
Primeiro, precisamos sair se nenhuma radiação for observada:
or a, (ix+endbyte) ; dd b6 21 ; a |= memory[ix+0x0021]
jr nz, midbyte ; 20 10 ; jump to a halt instruction if not zero
Se algum byte foi removido, todos os bytes serão alterados e $0020conterão o último 76, assim $0021será um zero. Podemos nos dar ao luxo de irradiar o início do programa, mesmo que não haja praticamente redundância:
- Se o deslocamento do salto
$10for removido, a radiação será detectada corretamente, o salto não será realizado e o deslocamento não será importante. O primeiro byte da próxima instrução será consumido, mas como ele foi projetado para resistir à remoção de bytes, isso não importa.
- Se o código de operação do salto
$20for removido, o deslocamento do salto $10será decodificado como djnz $ffe4(consumindo o próximo byte de instrução como o deslocamento - veja acima), que é uma instrução de loop - decremento B, e saltará se o resultado não for zero. Como ffe4-ffffé preenchido com zeros nop, o contador do programa é executado, isso executará o início do programa 256 vezes e, finalmente, continuará. Estou surpreso que isso funcione.
- Remover o
$ddfará com que o restante do trecho decodifique como or (hl) / ld ($1020), hle deslize para a próxima parte do programa. A ornão mudará nenhum registros importantes, e porque HL é zero neste ponto, a gravação também vai cancelar.
- A remoção do
$b6decodificador fará com que o restante decodifique ld ($1020), ixe prossiga como acima.
- Remover o
$21fará com que o decodificador coma $20, desencadeando o djnzcomportamento.
Observe que o uso or a, (ix+*)economiza dois bytes, ld a, (**) / and a / and agraças à verificação integrada de zero.
Agora precisamos decidir qual das duas cópias da carga útil executar:
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
As duas cópias são separadas por um nop, pois é usado um salto relativo para escolher entre elas, e a radiação poderia ter mudado o programa de uma maneira que faria o salto pular o primeiro byte após o destino. Além disso, o nop é codificado como zero, o que facilita a detecção de bytes deslocados. Observe que não importa qual carga útil é escolhida se o switch estiver corrompido, porque as duas cópias estarão seguras. Vamos garantir que ele não entre na memória não inicializada:
- A exclusão
$ddfará com que os próximos dois bytes decodifiquem como or (hl) / dec d. Clobbers D. Não é grande coisa.
- A exclusão
$b6criará uma codificação mais longa não documentada para dec d. O mesmo que acima.
- A exclusão
$15lerá o $28deslocamento como deslocamento e a execução prosseguirá no $0c, como abaixo.
- Quando
$28desaparece, o $0cé decodificado como inc c. A carga útil não se importa c.
- Exclusão
$0c- é para isso que serve o nop. Caso contrário, o primeiro byte da carga útil teria sido lido como deslocamento de salto e o programa entraria na memória não inicializada.
A carga útil em si é bem simples. Eu acho que o tamanho pequeno da string torna essa abordagem menor que um loop, e é mais fácil tornar a posição independente dessa maneira. O ein se beeprepete, para que eu possa raspar um ld a. Também, porque toda a memória entre $0038e $8000é zerado, eu posso cair através dele e usar um mais curto rstvariante da callinstrução, que só funciona para $0, $8, $10e assim por diante, até $38.
Abordagens mais antigas
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
Este tem uma explicação no histórico de edições, mas não é muito diferente.
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
E se: qualquer saída não vazia foi boa em vez de emitir um sinal sonoro
1 byte
v
halté o programa normalmente, mas se a radiação o remover, a memória estará cheia de zeros, fazendo $8000executar um número infinito de vezes, imprimindo muitos bytes nulos.