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повертаємо початкове значення (в ).
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
Що робить це трохи цікавіше, це те, що код робить більше , а це означає, що зробити це все складніше, використовуючи інструкції, кодовані за допомогою лише непарних байтів. Звичайно, це також означає, що він не спрацьовує при коді гольфу, тож це свого роду компроміс - хочеш цікавого та складного, чи хочеш короткого?
У будь-якому випадку для цього використовується OUTінструкція x86 для запису на порт вводу / виводу 0x3F8, який є стандартним послідовним портом COM1 на ПК. Забавна частина, звичайно, полягає в тому, що всі стандартні порти вводу / виводу (послідовний і паралельний) мають рівні адреси, тому їх не можна просто закодувати як безпосередні для OUTінструкції або перемістити безпосередньо в реєстр. Ви повинні ініціалізувати з одним меншим, ніж фактичне значення, а потім збільшити значення в регістрі. Ви також обмежені використанням певних регістрів для маніпуляції, оскільки вам потрібні регістри, кодовані за допомогою непарних байтів в інструкції, коли вони використовуються як операнди.
Крім того, мені довелося ініціалізувати DXреєстр (через CXрегістр) у верхній частині циклу, хоча це потрібно лише у випадку, якщо значення непарне, щоб переконатися, що JNCінструкція матиме непарне зміщення. Однак, оскільки те, що ми пропускаємо, - це OUTінструкція, все це робить код циклів відходів та реєстрів подряпин клоб; це насправді не виводить нічого , тому не порушує правила.
Нарешті, ця функція повернеться (після того, як буде зроблено або не зроблено вихід на послідовний порт) із залишеним вхідним значенням EAX. Але це фактично не порушує жодних правил; всі функції в мові збірки повертаються зі значенням у EAX- питання лише в тому, чи є це значне значення або значення сміття . Це визначається в документації функції (по суті, повертає вона значення чи повертає він)void ), і в цьому випадку я документую це як не повертає значення. :-)
Для цього немає посилання TIO, оскільки він не реалізує вихід до послідовних портів. Вам знадобиться справжнє залізо або фантазія.