x86-64 Kod maszynowy, 8 bajtów
Zainspirowany rozwiązaniem Bruce'a Forte'a , ale nieco poniżej par. :-)
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
Pojedynczy parametr liczby całkowitej jest brany do EDI
rejestru, zgodnie z konwencją wywoływania AMD64 Systemu V.
Kopia tej wartości jest początkowo tworzona i umieszczana, EAX
aby w razie potrzeby mogła zostać zwrócona. (LEA
jest używany zamiast normalnego, MOV
ponieważ potrzebujemy instrukcji z nieparzystymi bajtami).
Następnie wartość w EDI
jest przesuwana w prawo o 1, co umieszcza przesunięty bit w chorągiewce przenoszenia (CF). Ten bit będzie wynosił 0, jeśli liczba była parzysta, lub 1, jeśli był nieparzysty.
Następnie testujemy CF za pomocą JNC
instrukcji, która rozgałęzia się tylko wtedy, gdy CF ma wartość 0 (tzn. Liczba była parzysta). Oznacza to, że przejdziemy do nieskończonej pętli dla parzystych wartości. W przypadku wartości nieparzystych wypadamy i wartość oryginalna (wEAX
).
Jest jednak trochę sztuczki z JNC
instrukcją - ma REP
prefiks! Zwykle REP
przedrostki są używane tylko z instrukcjami łańcuchowymi, ale ponieważ zarówno instrukcje Intel, jak i AMD zgadzają się, że nieistotne / zbędne / nadmiarowe REP
prefiksy są ignorowane, rzucamy tutaj jedną z instrukcji rozgałęzienia, aby miała długość 3 bajtów. W ten sposób względne przesunięcie zakodowane w instrukcji skoku jest również nieparzyste. (I oczywiście,REP
sam w sobie jest prefiksem nieparzystym).
Dzięki Bogu RET
jest kodowany za pomocą nieparzystego bajtu!
Wypróbuj online!
W przypadku, gdy nie myślisz, że zwrócenie wartości, jeśli jest nieparzysta, lub przejście do nieskończonej pętli, jeśli jest ona parzysta (abyś nigdy nie powrócił), spełnia wymagania „wyjściowe” wyzwania, lub po prostu chcesz czegoś bardziej interesującego, oto funkcja który wypisuje wartość na port szeregowy (ale tylko jeśli jest nieparzysty, oczywiście).
x86-64 Kod maszynowy (wyjście do portu szeregowego), 17 bajtów
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
To, co sprawia, że jest to trochę bardziej interesujące, to fakt, że kod robi więcej , co oznacza, że trudniej było to wszystko zrobić za pomocą instrukcji zakodowanych przy użyciu tylko nieparzystych bajtów. Oczywiście oznacza to również, że zawodzi w golfie kodowym, więc jest to swego rodzaju kompromis - czy chcesz interesujących i wymagających, czy może krótkich?
W każdym razie używa OUT
instrukcji x86 do zapisu na porcie we / wy 0x3F8, który jest standardowym portem szeregowym COM1 na komputerze. Zabawne jest oczywiście to, że wszystkie standardowe porty I / O (szeregowe i równoległe) mają parzyste adresy, więc nie można ich po prostu zakodować jako bezpośrednich OUT
instrukcji ani przenieść bezpośrednio do rejestru. Musisz zainicjować wartość o jedną mniejszą niż wartość rzeczywista, a następnie zwiększyć wartość wartość w rejestrze. Ograniczasz się również do używania niektórych rejestrów do manipulacji, ponieważ potrzebujesz rejestrów, które są zakodowane przy użyciu nieparzystych bajtów w instrukcji, gdy są używane jako operandy.
Musiałem także zainicjować DX
rejestr (za pośrednictwem CX
rejestru) na górze pętli, chociaż jest to potrzebne tylko wtedy, gdy wartość jest nieparzysta, aby zapewnić, że JNC
instrukcja będzie miała nieparzyste przesunięcie. Ponieważ jednak pomijamy OUT
instrukcję, wszystko, co robi ten kod, to cykle marnotrawstwa i rejestry scratchowania clobbera; tak naprawdę nie wyprowadza nic , więc nie łamie zasad.
Wreszcie funkcja ta powróci (po wykonaniu lub nie wykonaniu danych wyjściowych do portu szeregowego) z pozostawioną wartością wejściową EAX
. Ale to tak naprawdę nie łamie żadnych zasad; wszystkie funkcje w języku asemblera powrócą z wartością w EAX
- pytaniem jest tylko, czy jest to wartość znacząca, czy wartość śmieciowa . Jest to określone w dokumentacji funkcji (w zasadzie, czy zwraca wartość, czy zwraca)void
), aw tym przypadku dokumentuję ją jako nie zwracającą wartości. :-)
Brak linku TIO dla tego, ponieważ nie implementuje on wyjścia do portów szeregowych. Potrzebujesz prawdziwego żelaza lub wyobraźni.