Z80Golf , 53 36 34 bayt
@Neil sayesinde @Lynn -2 byte sayesinde -16 bayt
Bu sadece Z80 makine kodu olduğu için, bunda yazdırılamaz bir çok şey var, bu nedenle geri xxd -r
dönüşlü bir hexdump var :
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
Çevrimiçi deneyin! (Python'da ayrıntılı test)
açıklama
z80golf Anarşi Golf'ün varsayımsal Z80 makinesi vardır call $8000
, bir putchar olan call $8003
bir getchar olduğunu halt
programınızı yerleştirilir, tercüman çıkış yapar $0000
ve tüm diğer bellek sıfır ile doldurulur. Programların montajda radyasyon geçirmez hale getirilmesi oldukça zordur, ancak genel olarak kullanışlı bir teknik, bir baytlık bağımsız komutlar kullanıyor. Örneğin,
or c ; b1 ; a = a | c
sadece bir bayttır ve a | c | c == a | c
sadece talimatı tekrarlayarak radyasyon geçirmez hale getirilebilir. Z80'de, 8 bitlik bir anlık yük iki bayttır (hemen sonraki ikinci bayttadır), böylece bazı değerleri güvenilir bir şekilde yazmaçlara da yükleyebilirsiniz . Programın başında ilk başta yaptığım şey buydu, bu nedenle yanıtın altında arşivlediğim daha uzun varyantları analiz edebilirsiniz, ancak daha sonra daha basit bir yol olduğunu anladım.
Program, birinin radyasyondan zarar görebileceği iki bağımsız yükten oluşuyor. Mutlak bellek adreslerinin değerlerini kontrol ederek bir baytın kaldırılıp kaldırılmadığını ve kaldırılan baytın yükün ikinci kopyasından önce olup olmadığını kontrol ederim.
İlk olarak, radyasyon gözlenmediyse çıkmamız gerekir:
or a, (ix+endbyte) ; dd b6 21 ; a |= memory[ix+0x0021]
jr nz, midbyte ; 20 10 ; jump to a halt instruction if not zero
Herhangi bir bayt kaldırıldıysa, tüm baytlar kayacak ve $0020
sonuncuyu içerecektir 76
, bu yüzden $0021
sıfır olacaktır. Hemen hemen hiçbir fazlalık olmasa da, programın başlangıcını yaymayı göze alabiliriz:
- Atlama ofseti
$10
kaldırılırsa, radyasyon doğru bir şekilde algılanır, atlama alınmaz ve ofset önemli olmaz. Bir sonraki komutun ilk baytı tüketilecektir, ancak bayt çıkartmalarına karşı dayanıklı olması için tasarlandığından, bunun önemi yoktur.
- Atlama işlem kodu ise
$20
çıkarılır, sonra ofset atlama $10
olarak deşifre edecek djnz $ffe4
(bir sonraki komut bayt alıcı ofset - yukarıya bakınız) - sonuç sıfır değilse eksiltme B ve atlama bir döngü talimat olan. ffe4-ffff
Sıfır ( nop
lar) ile dolu olduğundan ve program sayacı etrafına dolandığından, bu programın başlangıcını 256 kez çalıştırır ve sonra devam eder. Bu işleri hayrete düşürüyorum.
- İsteğin kaldırılması
$dd
parçacığın geri kalanının kodunu çözmesini sağlar or (hl) / ld ($1020), hl
ve ardından programın bir sonraki bölümüne kaydırılır. or
Herhangi önemli kayıtlarını değişmeyecek ve HL bu noktada sıfır olduğu için, yazma da iptal edecektir.
$b6
İstirahati kaldırmak istirahatin kodunu çözmesini sağlar ld ($1020), ix
ve yukarıdaki gibi devam eder.
- İradenin kaldırılması
$21
kod çözücünün yemesini $20
ve djnz
davranışını tetiklemesini sağlar .
or a, (ix+*)
Sıfırdaki ld a, (**) / and a / and a
bütünleşik kontrol sayesinde kullanımın iki byte tasarrufu sağladığını unutmayın .
Şimdi yükün iki kopyasından hangisinin yürütüleceğine karar vermemiz gerekiyor:
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
İki kopya bir nop ile ayrılır, çünkü aralarında seçim yapmak için nispi bir sıçrama kullanılır ve radyasyon, atlamanın hedeften sonraki ilk baytı atlamasını sağlayacak şekilde kaydırmış olabilir. Ek olarak, nop sıfır olarak kodlanmıştır, bu da değişen baytların tespit edilmesini kolaylaştırır. Anahtarın kendisi bozuksa hangi yükün seçildiği önemli değildir, çünkü her iki kopya da güvenlidir. Yine de, başlatılmamış belleğe atlamayacağından emin olalım:
- Silme
$dd
, sonraki iki baytın şifresini çözmesini sağlar or (hl) / dec d
. Clobbers D. Hiç önemli değil.
- Silme
$b6
, belgelenmemiş daha uzun bir kodlama oluşturur dec d
. Yukarıdaki gibi.
- Silme yerine ofset olarak
$15
okuyacak $28
ve yürütme $0c
aşağıdaki gibi devam edecektir .
- Ne zaman
$28
kaybolur, $0c
olarak deşifre edilir inc c
. Yükü umursamıyor c
.
- Silme
$0c
- Nop bunun için. Aksi takdirde, yükün ilk baytı atlama kayması olarak okunur ve program başlatılmamış belleğe atlanırdı.
Yükün kendisi oldukça basittir. İpin küçük boyutunun bu yaklaşımı bir döngüden daha küçük hale getirdiğini düşünüyorum ve bu şekilde pozisyondan bağımsız hale getirmek daha kolay. e
İçinde beep
tekrarlar, bu yüzden bir tane kapalı tıraş edebilirsiniz ld a
. Arasındaki tüm bellek çünkü Ayrıca, $0038
ve $8000
sıfırlanır, bunun üzerinden düşmesine ve daha kısa kullanabilir rst
varyantını call
sadece çalışır talimat, $0
, $8
, $10
ve benzeri, en fazla $38
.
Daha eski yaklaşımlar
64 bayt
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 bayt
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 bayt
Bunun düzenleme geçmişinde bir açıklaması var, ancak çok da farklı değil.
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
Ya eğer: boş olmayan herhangi bir çıkış bip yerine iyi oldu
1 bayt
v
halt
Normalde program, ancak radyasyon kaldırırsa, bellek sıfırla dolu olacaktır, böylece $8000
sonsuz sayıda defa çalıştırılır, çok sayıda boş bayt yazdırılır.