x86-64 Makine Kodu, 8 bayt
Esinlenerek Bruce Forte çözümü , ama biraz par altında. :-)
8D 07 lea eax, [rdi] ; put copy of input parameter in EAX
D1 EF shr edi, 1 ; shift LSB into CF
InfiniteLoop:
F3 73 FD rep jnc InfiniteLoop ; test CF; infinite loop back here if input was even
C3 ret ; return with original input in EAX if it was odd
EDI
System V AMD64 çağrı kuralını izleyerek kayıt defterinde tek bir tamsayı parametresi alınır .
Bu değerin bir kopyası başlangıçta yapılır ve EAX
uygunsa geri döndürülebilmesi için koyar . ( LEA
normal yerine kullanılır, MOV
çünkü tek baytlık bir talimat gerekir.)
Daha sonra, içinde EDI
kayma bitini taşıma bayrağına (CF) yerleştiren 1 değeri sağa kaydırılır. Sayı bitse bu bit 0, çift ise 1 olur.
Daha sonra CF'yi JNC
sadece CF 0 ise dallanacak olan komutu kullanarak test ediyoruz (yani sayı çiftdi). Bu, eşit değerler için sonsuz bir döngüye gireceğimiz anlamına gelir. Tek değerler için düşüp orijinal değer (in EAX
) döndürülür.
Yine de JNC
talimat ile ilgili bir püf noktası var - bir REP
ön eki var! Normal olarak, REP
önekler yalnızca dize yönergeleriyle kullanılır, ancak Intel ve AMD kılavuzlarının her ikisi de alakasız / gereksiz / gereksiz REP
öneklerin göz ardı edilmesine karar verdiklerinden, 3 bayt uzunluğunu almak için buradaki dal komutunu atıyoruz. Bu şekilde, atlama komutunda kodlanan göreceli sapma da gariptir. (Ve tabii ki, REP
garip bir bayt önekidir.)
Çok şükür RET
garip bir bayt kullanarak kodlanmış!
Çevrimiçi deneyin!
Tuhafsa değeri döndürmeyi ya da eşit olmasa bile sonsuz bir döngüye girmeyi düşünmüyorsanız, (asla geri dönmemek için) zorluğun "çıktısı" gereksinimlerini karşılar veya daha ilginç bir şey istiyorsanız, işte size bir işlev bu değeri bir seri porta verir (ancak eğer tek ise elbette).
x86-64 Makine Kodu (seri bağlantı noktasına çıkış), 17 bayt
8D 07 lea eax, [rdi] ; put copy of input parameter (EDI) in EAX
B1 F7 mov cl, 0xf7 ; put 0xF7 into low-order bits of CX
B5 03 mov ch, 0x03 ; put 0x03 into high-order bits of CX
FE C1 inc cl ; increment low-order bits of CX to 0xF8 (so all together it's now 0x3F8)
0F B7 D1 movzx edx, cx ; move CX to DX ("MOV DX, CX" would have a 16-bit prefix of 0x66)
D1 EF shr edi, 1 ; shift LSB of input parameter into CF
73 01 jnc IsEven ; test CF: branch if 0 (even), fall through if 1 (odd)
EF out dx, eax ; output EAX (original input) to I/O port 0x3F8 (in DX)
IsEven:
C3 ret ; return
Bunu biraz daha ilginç hale getiren şey, kodun daha fazlasını yapmasıdır, yani sadece tek baytlar kullanılarak kodlanmış komutları kullanarak hepsini yapmak daha zordur. Tabii ki, bu aynı zamanda kod golfünde başarısız olduğu anlamına gelir, bu yüzden bir tür tradeoff - ilginç ve zorlu mu istiyorsun yoksa kısa mı istiyorsun?
Neyse, bu bir PC'deki standart COM1 seri portu olan G / Ç portu 0x3F8'e yazmak için x86 OUT
komutunu kullanır . Tabii ki eğlence kısmı, tüm standart I / O portlarının (seri ve paralel) eşit adreslere sahip olmalarıdır, bu nedenle komut için doğrudan giriş yaptıkları gibi kodlanamazlar veya doğrudan bir kayıt cihazına taşınamazlar. Gerçek değerden küçük bir değerle başlatmanız ve ardından kayıttaki değeri arttırmanız gerekir. Ayrıca, manipülasyon için belirli kayıtları kullanmakla da sınırlısınız çünkü işlenenler olarak kullanıldığında komuttaki tek baytları kullanarak kodlanmış kayıtlara ihtiyacınız var.OUT
Ayrıca, komutun bir tuhaf kaymaya sahip olmasını sağlamak için gerekli olsa bile, gerekli olsa bile, döngünün tepesinde yazmacıyı ( DX
yazmacı aracılığıyla CX
) başlatmam gerekiyordu JNC
. Bununla birlikte, atladığımız şey OUT
talimat olduğu için , bu kodun yaptığı tek şey atık döngüleri ve clobber çizik kayıtları; öyle değil aslında does çıktı o kaideyi bozmaz bu yüzden, herhangi bir şey.
Son olarak, bu işlev (girişini seri porta yaptıktan sonra veya yaptıktan sonra) giriş değeri bırakıldığında geri dönecektir EAX
. Fakat bu aslında herhangi bir kuralı ihlal etmiyor; Assembly dilinde tüm fonksiyonlar bir değerle döner EAX
- soru sadece önemli bir değer veya bir çöp değerdir. Bu, işlevin belgelerine göre belirlenir (temelde bir değer döndürür mü veya geri döndürür void
) ve bu durumda onu bir değer döndürmediğini belgeliyorum. :-)
Seri portlara çıkış uygulamadığı için bunun için TIO bağlantısı yok. Gerçek demire veya hayal gücüne ihtiyacınız olacak.