Z80Golf , 53 36 34 bytes
-16 bytes gracias a @Lynn
-2 bytes gracias a @Neil
Dado que este es solo el código de máquina Z80, hay muchos no imprimibles en este, así que tenga un 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
Pruébalo en línea! (probador exhaustivo en Python)
Explicación
z80golf es la máquina Z80 hipotética de Anarchy Golf, donde call $8000
es un putchar, call $8003
es un getchar, halt
hace que el intérprete salga, su programa se coloca en $0000
, y toda la otra memoria está llena de ceros. Hacer que los programas sean a prueba de radiación en el ensamblaje es bastante difícil, pero una técnica genéricamente útil es usar instrucciones idempotentes de un byte. Por ejemplo,
or c ; b1 ; a = a | c
es solo un byte y a | c | c == a | c
, por lo tanto, puede hacerse a prueba de radiación simplemente repitiendo las instrucciones. En el Z80, una carga inmediata de 8 bits es de dos bytes (donde el inmediato está en el segundo byte), por lo que también puede cargar algunos valores en registros de manera confiable. Esto es lo que hice originalmente al comienzo del programa, para que pueda analizar las variantes más largas que archivé al final de la respuesta, pero luego me di cuenta de que hay una manera más simple.
El programa consta de dos cargas útiles independientes, donde una de ellas podría haber sido dañada por la radiación. Compruebo si se eliminó un byte y si el byte eliminado se encontraba antes de la segunda copia de la carga útil, verificando los valores de algunas direcciones de memoria absolutas.
Primero, debemos salir si no se observó radiación:
or a, (ix+endbyte) ; dd b6 21 ; a |= memory[ix+0x0021]
jr nz, midbyte ; 20 10 ; jump to a halt instruction if not zero
Si se eliminó algún byte, todos los bytes se desplazarán y $0020
contendrán el último 76
, por $0021
lo que será un cero. Podemos permitirnos irradiar el comienzo del programa, a pesar de que prácticamente no hay redundancia:
- Si
$10
se elimina el desplazamiento del salto , se detectará correctamente la radiación, no se tomará el salto y el desplazamiento no importará. El primer byte de la próxima instrucción se consumirá, pero dado que está diseñado para ser resistente a la eliminación de bytes, esto no importa.
- Si
$20
se elimina el código de operación de salto , el desplazamiento de salto $10
se descodificará como djnz $ffe4
(consumiendo el siguiente byte de instrucción como el desplazamiento, ver arriba), que es una instrucción de bucle, decremento B, y saltará si el resultado no es cero. Debido a que ffe4-ffff
está lleno de ceros nop
, y el contador del programa se ajusta, esto ejecutará el comienzo del programa 256 veces, y finalmente continuará. Estoy sorprendido de que esto funcione.
- Al eliminarlo
$dd
, el resto del fragmento se decodificará como or (hl) / ld ($1020), hl
, y luego se deslizará a la siguiente parte del programa. El or
no va a cambiar cualquier registros importantes, y debido HL es cero en este punto, la escritura también se suprimirá.
- Si quita el
$b6
resto, el resto se decodificará como ld ($1020), ix
y proceda como se indicó anteriormente.
- Quitarlo
$21
hará que el decodificador se coma el $20
, desencadenando el djnz
comportamiento.
Tenga en cuenta que el uso or a, (ix+*)
ahorra dos bytes ld a, (**) / and a / and a
gracias a la verificación integrada de cero.
Ahora debemos decidir cuál de las dos copias de la carga útil ejecutar:
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
Las dos copias están separadas por un nop, ya que se usa un salto relativo para elegir entre ellas, y la radiación podría haber cambiado el programa de una manera que haría que el salto saltara el primer byte después del destino. Además, el nop está codificado como cero, lo que facilita la detección de bytes desplazados. Tenga en cuenta que no importa qué carga útil se elija si el interruptor en sí está dañado, porque ambas copias son seguras. Sin embargo, asegurémonos de que no salte a la memoria no inicializada:
- La eliminación
$dd
hará que los siguientes dos bytes se decodifiquen como or (hl) / dec d
. Clobbers D. No es gran cosa.
- La eliminación
$b6
creará una codificación más larga no documentada para dec d
. Lo mismo que arriba.
- La eliminación
$15
leerá en su $28
lugar como el desplazamiento, y la ejecución continuará en el $0c
, como a continuación.
- Cuando
$28
desaparece, $0c
se decodifica como inc c
. La carga útil no le importa c
.
- Eliminar
$0c
: para eso está el nop. De lo contrario, el primer byte de la carga útil se habría leído como el desplazamiento de salto, y el programa saltaría a la memoria no inicializada.
La carga útil en sí es bastante simple. Creo que el tamaño pequeño de la cadena hace que este enfoque sea más pequeño que un bucle, y es más fácil hacer que la posición sea independiente de esta manera. El e
de beep
repeticiones, por lo que se pueden reducir la uno ld a
. Además, debido a toda la memoria entre $0038
y $8000
se pone a cero, puedo caer a través de él y utilizar un corto rst
variante de la call
instrucción, que sólo funciona para $0
, $8
, $10
y así sucesivamente, hasta $38
.
Enfoques más antiguos
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 tiene una explicación en el historial de edición, pero no es muy 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
Qué pasa si: cualquier salida no vacía estaba bien en lugar de pitido
1 byte
v
halt
s el programa normalmente, pero si la radiación lo elimina, entonces la memoria estará llena de ceros, haciendo $8000
ejecutar un número infinito de veces, imprimiendo muchos bytes nulos.