8086 machine code (MS-DOS .COM), 83 bytes
Runnable in DOSBox or your favourite steam-powered computing engine. The string to irradiate is given as a command line argument.
Binary:
00000000 : EB 28 28 8A 0E 80 00 49 BD 83 00 B4 02 51 8A 0E : .((....I.....Q..
00000010 : 80 00 BE 82 00 AC 39 EE 74 04 88 C2 CD 21 E2 F5 : ......9.t....!..
00000020 : 59 45 B2 0A CD 21 E2 E5 C3 90 EB D7 D7 8A 0E 80 : YE...!..........
00000030 : 00 49 BD 83 00 B4 02 51 8A 0E 80 00 BE 82 00 AC : .I.....Q........
00000040 : 39 EE 74 04 88 C2 CD 21 E2 F5 59 45 B2 0A CD 21 : 9.t....!..YE...!
00000050 : E2 E5 C3 : ...
Readable:
cpu 8086
org 0x100
jmp part2
db 0x28
part1:
mov cl, [0x80]
dec cx
mov bp, 0x83
mov ah, 0x02
.l:
push cx
mov cl, [0x80]
mov si, 0x82
.k:
lodsb
cmp si, bp
je .skip
mov dl, al
int 0x21
.skip:
loop .k
pop cx
inc bp
mov dl, 10
int 0x21
loop .l
ret
nop
part2:
jmp part1
db 0xd7
mov cl, [0x80]
dec cx
mov bp, 0x83
mov ah, 0x02
.l:
push cx
mov cl, [0x80]
mov si, 0x82
.k:
lodsb
cmp si, bp
je .skip
mov dl, al
int 0x21
.skip:
loop .k
pop cx
inc bp
mov dl, 10
int 0x21
loop .l
ret
Rundown
The active part is duplicated so that there is always one untouched by radiation. We select the healthy version by way of jumps. Each jump is a short jump, and so is only two bytes long, where the second byte is the displacement (i.e. distance to jump, with sign determining direction).
We can divide the code into four parts which could be irradiated: jump 1, code 1, jump 2, and code 2. The idea is to make sure a clean code part is always used. If one of the code parts is irradiated, the other must be chosen, but if one of the jumps is irradiated, both code parts will be clean, so it will not matter which one is chosen.
The reason for having two jump parts is to detect irradiation in the first part by jumping over it. If the first code part is irradiated, it means we will arrive one byte off the mark. If we make sure that such a botched landing selects code 2, and a proper landing selects code 1, we're golden.
For both jumps, we duplicate the displacement byte, making each jump part 3 bytes long. This ensures that irradiation in one of the two last bytes will still make the jump valid. Irradiation in the first byte will stop the jump from happening at all, since the last two bytes will form a completely different instruction.
Take the first jump:
EB 28 28 jmp +0x28 / db 0x28
If either of the 0x28
bytes are removed, it will still jump to the same place. If the 0xEB
byte is removed, we will instead end up with
28 28 sub [bx + si], ch
which is a benign instruction on MS-DOS (other flavours might disagree), and then we fall through to code 1, which must be clean, since the damage was in jump 1.
If the jump is taken, we land at the second jump:
EB D7 D7 jmp -0x29 / db 0xd7
If this byte sequence is intact, and we land right on the mark, that means that code 1 was clean, and this instruction jumps back to that part. The duplicated displacement byte guarantees this, even if it is one of these displacement bytes that were damaged. If we either land one byte off (because of a damaged code 1 or jump 1) or the 0xEB
byte is the damaged one, the two remaining bytes will also here be benign:
D7 D7 xlatb / xlatb
Whichever the case, if we end up executing those two instructions, we know that either jump 1, code 1, or jump 2 were irradiated, which makes a fall-through to code 2 safe.
Testing
The following program was used to automatically create all versions of the .COM file. It also creates a BAT file that can be run in the target environment, which runs each irradiated binary, and pipes their outputs to separate text files. Comparing the output files to validate is easy enough, but DOSBox does not have fc
, so it was not added to the BAT file.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
FILE *fin, *fout, *fbat;
int fsize;
char *data;
if (!(fin = fopen(argv[1], "rb")))
{
fprintf(stderr, "Could not open input file \"%s\".\n", argv[1]);
exit(1);
}
if (!(fbat = fopen("tester.bat", "w")))
{
fprintf(stderr, "Could not create BAT test file.\n");
exit(2);
}
fseek(fin, 0L, SEEK_END);
fsize = ftell(fin);
fseek(fin, 0L, SEEK_SET);
if (!(data = malloc(fsize)))
{
fprintf(stderr, "Could not allocate memory.\n");
exit(3);
}
fread(data, 1, fsize, fin);
fprintf(fbat, "@echo off\n");
for (int i = 0; i < fsize; i++)
{
char fname[512];
sprintf(fname, "%03d.com", i);
fprintf(fbat, "%s Hello, world! > %03d.txt\n", fname, i);
fout = fopen(fname, "wb");
fwrite(data, 1, i, fout);
fwrite(data + i + 1, 1, fsize - i - 1, fout);
fclose(fout);
}
free(data);
fclose(fin);
fclose(fbat);
}
...or comma perhaps? – Jonathan Allan – 2019-08-18T21:26:35.803
4this one is really favouring golfing languages, because C program with
v
invoid
removed won't compile – Krzysztof Szewczyk – 2019-08-19T09:51:51.8503@Krzysztof tbh I think most if not all practical languages won't survive radiation hardening because of the verbosity and the syntaxes. Not only this challenge, but ALL RH challenges. – Shieru Asakoto – 2019-08-19T13:34:35.083