Машинный код x86-64, 8 байт
Вдохновленный решением Брюса Форте , но немного под пар. :-)
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.
Изначально создается копия этого значения и помещается в EAXнее, чтобы при необходимости ее можно было вернуть. ( LEAиспользуется вместо обычного, MOVпотому что нам нужна инструкция с нечетными байтами.)
Затем значение in EDIсдвигается вправо на 1, что помещает сдвинутый бит в флаг переноса (CF). Этот бит будет 0, если число было четным, или 1, если оно было нечетным.
Затем мы проверяем CF, используя JNCинструкцию, которая будет разветвляться, только если CF равен 0 (т. Е. Число было четным). Это означает, что мы войдем в бесконечный цикл для четных значений. Для нечетных значений мы проваливаемся и EAXвозвращаем исходное значение (in ).
Однако с JNCинструкцией есть небольшая хитрость - у нее есть REPпрефикс! Обычно REPпрефиксы используются только со строковыми инструкциями, но поскольку оба руководства Intel и AMD согласны с тем, что нерелевантные / лишние / избыточные REPпрефиксы игнорируются, мы добавляем один из них в инструкцию перехода, чтобы сделать ее длиной 3 байта. Таким образом, относительное смещение, которое кодируется в инструкции перехода, также является нечетным. (И, конечно, REPсам по себе префикс нечетного байта.)
Слава Богу RET, кодируется нечетным байтом!
Попробуйте онлайн!
В случае, если вы не думаете, возвращать ли значение, если оно нечетное, или входить в бесконечный цикл, если оно четное (чтобы вы никогда не возвращали), удовлетворяет требованиям «вывода» задачи, или вы просто хотите что-то более интересное, вот функция который выводит значение на последовательный порт (но, конечно, только если он нечетный).
Машинный код x86-64 (вывод на последовательный порт), 17 байт
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
Что делает это немного более интересным, так это то, что код делает больше , а это значит, что было сложнее сделать все это, используя инструкции, которые кодируются с использованием нечетных байтов. Конечно, это также означает, что в коде гольф это терпит неудачу, так что это своего рода компромисс: хотите интересного и сложного или короткого?
В любом случае, для записи в порт ввода-вывода 0x3F8, который является стандартным последовательным портом COM1 на ПК , используется OUTинструкция x86 . Самое интересное, конечно, в том, что все стандартные порты ввода / вывода (последовательные и параллельные) имеют четные адреса, поэтому их нельзя просто закодировать как непосредственные для инструкции или перенести непосредственно в регистр. Вы должны инициализировать на единицу меньше действительного значения, а затем увеличить значение в регистре. Вы также ограничены использованием определенных регистров для манипуляции, потому что вам нужны регистры, которые кодируются с использованием нечетных байтов в инструкции, когда используются в качестве операндов.OUT
Кроме того, мне пришлось инициализировать DXрегистр (через CXрегистр) в верхней части цикла, хотя это необходимо только в том случае, если значение нечетное, чтобы JNCкоманда имела нечетное смещение. Тем не менее, поскольку мы пропускаем OUTинструкцию, все, что делает этот код - это пустые циклы и пустые регистры; на самом деле ничего не выводится , поэтому не нарушает правила.
Наконец, эта функция вернет (после того, как вы сделали или не сделали вывод на последовательный порт) с оставленным входным значением EAX. Но это на самом деле не нарушает никаких правил; все функции на ассемблере будут возвращаться со значением в EAX- вопрос только в том, является ли это значительным значением или значением мусора . Это определяется документацией функции (по сути, возвращает ли она значение или возвращает void), и в этом случае я документирую ее как не возвращающую значение. :-)
Нет ссылки TIO для этого, поскольку он не реализует вывод на последовательные порты. Вам понадобится настоящее железо или воображение.