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, оскільки він не реалізує вихід до послідовних портів. Вам знадобиться справжнє залізо або фантазія.