Mã máy 8086 (MS-DOS .COM), 83 byte
Có thể chạy trong DOSBox hoặc công cụ tính toán chạy bằng hơi nước yêu thích của bạn. Chuỗi để chiếu xạ được đưa ra dưới dạng đối số dòng lệnh.
Nhị phân:
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 : ...
Có thể đọc được
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
Chạy xuống
Phần hoạt động được nhân đôi để luôn luôn không bị ảnh hưởng bởi bức xạ. Chúng tôi chọn phiên bản lành mạnh bằng cách nhảy. Mỗi bước nhảy là một bước nhảy ngắn và do đó chỉ dài hai byte, trong đó byte thứ hai là độ dịch chuyển (tức là khoảng cách để nhảy, với hướng xác định dấu hiệu).
Chúng ta có thể chia mã thành bốn phần có thể được chiếu xạ: nhảy 1, mã 1, nhảy 2 và mã 2. Ý tưởng là đảm bảo một phần mã sạch luôn được sử dụng. Nếu một trong các phần mã được chiếu xạ, phần khác phải được chọn, nhưng nếu một trong các bước nhảy được chiếu xạ, cả hai phần mã sẽ sạch, vì vậy sẽ không có vấn đề nào được chọn.
Lý do để có hai phần nhảy là để phát hiện chiếu xạ trong phần đầu tiên bằng cách nhảy qua nó. Nếu phần mã đầu tiên được chiếu xạ, điều đó có nghĩa là chúng ta sẽ đến một byte khỏi dấu. Nếu chúng tôi đảm bảo rằng việc hạ cánh được khắc phục như vậy sẽ chọn mã 2 và hạ cánh phù hợp sẽ chọn mã 1, chúng tôi sẽ rất tuyệt vời.
Đối với cả hai lần nhảy, chúng tôi nhân đôi byte dịch chuyển, làm cho mỗi phần nhảy dài 3 byte. Điều này đảm bảo rằng chiếu xạ ở một trong hai byte cuối cùng vẫn sẽ khiến bước nhảy hợp lệ. Chiếu xạ trong byte đầu tiên sẽ ngăn chặn bước nhảy xảy ra, vì hai byte cuối cùng sẽ tạo thành một hướng dẫn hoàn toàn khác nhau.
Bước nhảy đầu tiên:
EB 28 28 jmp +0x28 / db 0x28
Nếu một trong hai 0x28
byte bị loại bỏ, nó vẫn sẽ nhảy đến cùng một vị trí. Nếu 0xEB
byte bị loại bỏ, thay vào đó chúng ta sẽ kết thúc bằng
28 28 sub [bx + si], ch
đó là một hướng dẫn lành tính trên MS-DOS (các hương vị khác có thể không đồng ý), và sau đó chúng tôi rơi vào mã 1, phải sạch, vì thiệt hại nằm ở bước 1.
Nếu bước nhảy được thực hiện, chúng ta sẽ hạ cánh ở lần nhảy thứ hai:
EB D7 D7 jmp -0x29 / db 0xd7
Nếu chuỗi byte này còn nguyên vẹn và chúng ta hạ cánh ngay trên nhãn hiệu, điều đó có nghĩa là mã 1 đã sạch và hướng dẫn này nhảy trở lại phần đó. Byte dịch chuyển trùng lặp đảm bảo điều này, ngay cả khi đó là một trong những byte dịch chuyển bị hỏng. Nếu chúng ta hoặc hạ một byte (vì mã 1 bị hỏng hoặc nhảy 1) hoặc 0xEB
byte là một byte bị hỏng, hai byte còn lại cũng sẽ ở đây là lành tính:
D7 D7 xlatb / xlatb
Bất kể trường hợp nào, nếu chúng ta kết thúc việc thực hiện hai hướng dẫn đó, chúng ta đều biết rằng bước nhảy 1, mã 1 hoặc bước 2 đã được chiếu xạ, điều này làm cho việc chuyển sang mã 2 trở nên an toàn.
Kiểm tra
Chương trình sau đây được sử dụng để tự động tạo tất cả các phiên bản của tệp .COM. Nó cũng tạo ra một tệp BAT có thể chạy trong môi trường đích, chạy từng tệp nhị phân được chiếu xạ và chuyển các kết quả đầu ra của chúng sang các tệp văn bản riêng biệt. So sánh các tệp đầu ra để xác thực là đủ dễ dàng, nhưng DOSBox không có fc
, vì vậy nó không được thêm vào tệp BAT.
#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);
}