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 -r
hexdump -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, halt
faz com que o intérprete saia, seu programa é colocado $0000
e 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 $0020
conterão o último 76
, assim $0021
será 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
$10
for 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
$20
for removido, o deslocamento do salto $10
será 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
$dd
fará com que o restante do trecho decodifique como or (hl) / ld ($1020), hl
e deslize para a próxima parte do programa. A or
não mudará nenhum registros importantes, e porque HL é zero neste ponto, a gravação também vai cancelar.
- A remoção do
$b6
decodificador fará com que o restante decodifique ld ($1020), ix
e prossiga como acima.
- Remover o
$21
fará com que o decodificador coma $20
, desencadeando o djnz
comportamento.
Observe que o uso or a, (ix+*)
economiza dois bytes, ld a, (**) / and a / and a
graç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
$dd
fará com que os próximos dois bytes decodifiquem como or (hl) / dec d
. Clobbers D. Não é grande coisa.
- A exclusão
$b6
criará uma codificação mais longa não documentada para dec d
. O mesmo que acima.
- A exclusão
$15
lerá o $28
deslocamento como deslocamento e a execução prosseguirá no $0c
, como abaixo.
- Quando
$28
desaparece, 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 e
in se beep
repete, para que eu possa raspar um ld a
. Também, porque toda a memória entre $0038
e $8000
é zerado, eu posso cair através dele e usar um mais curto rst
variante da call
instrução, que só funciona para $0
, $8
, $10
e 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 $8000
executar um número infinito de vezes, imprimindo muitos bytes nulos.