@Lynnのおかげで-16バイト@Neilのおかげで
-2バイト
これは単なるZ80マシンコードであるため、このコードには多くの印刷できないものがあるため、xxd -r
-reversible hexdumpがあります。
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
オンラインでお試しください!(Pythonの網羅的なテスター)
説明
z80golfはAnarchy Golfの架空のZ80マシンでcall $8000
、putchar、call $8003
getchar、halt
インタープリターを終了し、プログラムはに配置され$0000
、他のすべてのメモリはゼロで埋められます。アセンブリでプログラムを耐放射線性にすることは非常に困難ですが、一般的に有用な手法は1バイトのべき等命令を使用することです。例えば、
or c ; b1 ; a = a | c
は1バイトでa | c | c == a | c
、命令を繰り返すだけで耐放射線性を実現できます。Z80では、8ビットの即時ロードは2バイト(即値が2番目のバイトにある)であるため、一部の値を確実にレジスターにロードすることもできます。これはもともとプログラムの最初に行ったことであるため、回答の最後にアーカイブした長い亜種を分析できますが、より簡単な方法があることに気付きました。
このプログラムは2つの独立したペイロードで構成されており、そのうちの1つは放射線によって損傷を受けた可能性があります。いくつかの絶対メモリアドレスの値をチェックすることにより、バイトが削除されたかどうか、および削除されたバイトがペイロードの2番目のコピーの前にあったかどうかを確認します。
最初に、放射線が観測されなかった場合は終了する必要があります。
or a, (ix+endbyte) ; dd b6 21 ; a |= memory[ix+0x0021]
jr nz, midbyte ; 20 10 ; jump to a halt instruction if not zero
任意のバイトが削除された場合は、すべてのバイトがシフトすると$0020
最後が含まれています76
ので、$0021
ゼロになります。実質的に冗長性はありませんが、プログラムの始まりを放射する余裕があります。
- ジャンプオフセット
$10
が削除されると、放射は正しく検出され、ジャンプは行われず、オフセットは問題になりません。次の命令の最初のバイトが消費されますが、バイトの削除に耐えるように設計されているため、これは重要ではありません。
- ジャンプオペコード
$20
が削除されると、ジャンプオフセット$10
はdjnz $ffe4
(次の命令バイトをオフセットとして消費します-上記を参照)、ループ命令-Bをデクリメントし、結果がゼロでない場合はジャンプします。のでffe4-ffff
ゼロ(充填されているnop
S)、およびプログラムカウンタはラップアラウンド、これはプログラムの冒頭256回を実行し、最終的には継続します。この作品には驚かされます。
- を削除する
$dd
と、スニペットの残りの部分がとしてデコードされor (hl) / ld ($1020), hl
、プログラムの次の部分にスライドします。or
すべての重要なレジスタを変更しないだろう、とHLは、この時点ではゼロであるので、書き込みも相殺されます。
- を削除する
$b6
と、残りがデコードされld ($1020), ix
、上記のように進みます。
- を削除する
$21
と、デコーダーがを食べ$20
、djnz
動作がトリガーされます。
ゼロの統合チェックにより、使用するとor a, (ix+*)
2バイト以上節約されることに注意してくださいld a, (**) / and a / and a
。
ここで、ペイロードの2つのコピーのどちらを実行するかを決定する必要があります。
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
2つのコピーは、相対ジャンプを使用して選択するため、nopで区切られています。また、放射線は、ジャンプ後の最初のバイトをスキップするようにプログラムをシフトしている可能性があります。さらに、nopはゼロとしてエンコードされるため、シフトされたバイトを簡単に検出できます。スイッチ自体が破損している場合、両方のコピーが安全であるため、どちらのペイロードを選択するかは重要ではありません。ただし、初期化されていないメモリにジャンプしないようにしましょう。
- 削除
$dd
すると、次の2バイトがとしてデコードされor (hl) / dec d
ます。Clobbers D.大したことはありません。
- 削除
$b6
すると、のドキュメント化されていない長いエンコードが作成されdec d
ます。同上。
- 削除
$15
すると、$28
代わりにオフセットとして読み込まれ、$0c
以下のように、実行が続行されます。
- が
$28
消えると、$0c
はとしてデコードされinc c
ます。ペイロードは気にしませんc
。
- 削除
$0c
-それがnopの目的です。そうしないと、ペイロードの最初のバイトがジャンプオフセットとして読み取られ、プログラムが初期化されていないメモリにジャンプします。
ペイロード自体は非常に単純です。文字列のサイズが小さいと、このアプローチはループよりも小さくなり、この方法で位置に依存しないようにする方が簡単だと思います。e
でbeep
繰り返しなので、私は1つをオフに剃ることができますld a
。また、間のすべてのメモリ理由$0038
とは$8000
ゼロにされ、私はそれを通って落下し、短い使用できるrst
のバリアントcall
だけのために働く命令を$0
、$8
、$10
というように、まで$38
。
古いアプローチ
64バイト
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バイト
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バイト
これは編集履歴に説明がありますが、それほど違いはありません。
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
What if:空ではない出力はビープ音ではなく正常でした
1バイト
v
halt
s通常はプログラムを実行しますが、放射によって削除された場合、メモリはゼロでいっぱいになり$8000
、無限の回数実行され、多くのヌルバイトが出力されます。