Машинный код 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 для этого, поскольку он не реализует вывод на последовательные порты. Вам понадобится настоящее железо или воображение.