x86 16/32/64-бітний код машини: 11 байт, оцінка = 3,66
Ця функція повертає поточний режим (розмір операнду за замовчуванням) як ціле число в AL. Зателефонуйте з C з підписомuint8_t modedetect(void);
Машинний код NASM + список джерел (показує, як він працює в 16-бітному режимі, оскільки BITS 16
говорить NASM зібрати мнемоніку джерела для 16-бітного режиму.)
1 machine global modedetect
2 code modedetect:
3 addr hex BITS 16
5 00000000 B040 mov al, 64
6 00000002 B90000 mov cx, 0 ; 3B in 16-bit. 5B in 32/64, consuming 2 more bytes as the immediate
7 00000005 FEC1 inc cl ; always 2 bytes. The 2B encoding of inc cx would work, too.
8
9 ; want: 16-bit cl=1. 32-bit: cl=0
10 00000007 41 inc cx ; 64-bit: REX prefix
11 00000008 D2E8 shr al, cl ; 64-bit: shr r8b, cl doesn't affect AL at all. 32-bit cl=1. 16-bit cl=2
12 0000000A C3 ret
# end-of-function address is 0xB, length = 0xB = 11
Виправдання :
У машинному коді x86 офіційно немає номерів версій, але я думаю, що це задовольняє наміри питання за рахунок створення конкретних номерів, а не вибору того, що найзручніше (що займає лише 7 байт, див. нижче).
Оригінальний процесор x86, Intel 8086, підтримував лише 16-розрядний машинний код. 80386 ввів 32-розрядний машинний код (придатний для використання в 32-бітному захищеному режимі, а пізніше в режимі compat в 64-розрядної ОС). AMD представила 64-розрядний машинний код, який можна використовувати в тривалому режимі. Це версії машинної мови x86 в тому ж сенсі, що Python2 та Python3 - це різні мовні версії. Вони в основному сумісні, але з навмисними змінами. Ви можете запускати 32 або 64-бітні виконувані файли безпосередньо під 64-бітним ядром ОС так само, як ви могли запускати програми Python2 та Python3.
Як це працює:
Почніть з al=64
. Перемістіть його вправо на 1 (32-бітний режим) або 2 (16-бітний режим).
16/32 проти 64-бітових: 1-байт inc
/ dec
кодування - це префікси REX у 64-бітовій версії ( http://wiki.osdev.org/X86-64_Instruction_Encoding#REX_prefix ). REX.W взагалі не впливає на деякі вказівки (наприклад, a jmp
або jcc
), але в цьому випадку отримати 16/32/64 я хотів ecx
скоріше включити або зменшити eax
. Це також встановлює REX.B
, що змінює регістр призначення. Але, на щастя, ми можемо зробити це спрацьованим, але налаштування настільки настільки, що 64-розрядні не потрібно змінювати al
.
Інструкції, які виконуються лише в 16-бітному режимі, можуть включати в себе ret
, але я не вважаю це необхідним чи корисним. (І зробило б неможливим вбудовувати його як фрагмент коду, якщо ви цього хочете зробити). Це також може бути jmp
функцією.
16-бітний проти 32/64: безпосередні - 16-бітні, а не 32-бітні. Зміна режимів може змінити довжину інструкції, тому 32/64 бітові режими декодують наступні два байти як частину безпосередньої, а не окремої інструкції. Я просто спрощував речі, використовуючи 2-байтну інструкцію тут, замість того, щоб не декодувати синхронізацію, щоб 16-бітний режим декодувався з інших меж інструкцій, ніж 32/64.
Пов’язано: Префікс розміру операнду змінює довжину безпосереднього (якщо тільки це 8-бітний негайний розширений знак), як і різниця між 16-бітним та 32/64-бітним режимами. Це ускладнює декодування довжини інструкцій паралельно; Процесори Intel мають стійку декодування LCP .
Більшість конвенцій, що викликають (включаючи системи x86-32 та x86-64 System V psABI), дозволяють вузьким значенням повернення містити сміття у високих бітах реєстру. Вони також дозволяють клобувати CX / ECX / RCX (і R8 для 64-розрядних). IDK, якби це було часто в 16-бітних конвенціях про дзвінки, але це код гольфу, тому я завжди можу просто сказати, що це звичайна конвенція про дзвінки.
32-розрядна розбирання :
08048070 <modedetect>:
8048070: b0 40 mov al,0x40
8048072: b9 00 00 fe c1 mov ecx,0xc1fe0000 # fe c1 is the inc cl
8048077: 41 inc ecx # cl=1
8048078: d2 e8 shr al,cl
804807a: c3 ret
64-розрядна розбирання ( спробуйте в Інтернеті! ):
0000000000400090 <modedetect>:
400090: b0 40 mov al,0x40
400092: b9 00 00 fe c1 mov ecx,0xc1fe0000
400097: 41 d2 e8 shr r8b,cl # cl=0, and doesn't affect al anyway!
40009a: c3 ret
Пов’язані дані: мій поліглот x86-32 / x86-64 код-код Q&A на ТАК.
Ще одна відмінність між 16-розрядною і 32/64 полягає в тому, що режими адресації кодуються по-різному. наприклад lea eax, [rax+2]
( 8D 40 02
) декодує, як lea ax, [bx+si+0x2]
у 16-бітному режимі. Це, очевидно , важко використовувати для коду-гольфу, особливо з тих пір e/rbx
і e/rsi
які називають збереженими в багатьох угодах про виклики.
Я також розглядав можливість використання 10-байтового mov r64, imm64
, що є REX + mov r32,imm32
. Але оскільки я вже мав рішення в 11 байт, це було б у кращому випадку рівним (10 байт + 1 за ret
).
Тестовий код для 32 та 64-бітного режиму. (Я насправді не виконував його в 16-бітному режимі, але розбирання повідомляє вам, як він буде розшифровуватися. У мене немає 16-бітного емулятора.)
; CPU p6 ; YASM directive to make the ALIGN padding tidier
global _start
_start:
call modedetect
movzx ebx, al
mov eax, 1
int 0x80 ; sys_exit(modedetect());
align 16
modedetect:
BITS 16
mov al, 64
mov cx, 0 ; 3B in 16-bit. 5B in 32/64, consuming 2 more bytes as the immediate
inc cl ; always 2 bytes. The 2B encoding of inc cx would work, too.
; want: 16-bit cl=1. 32-bit: cl=0
inc cx ; 64-bit: REX prefix
shr al, cl ; 64-bit: shr r8b, cl doesn't affect AL at all. 32-bit cl=1. 16-bit cl=2
ret
Ця програма для Linux виходить із статусом exit = status = modedetect()
, тому запустіть її як ./a.out; echo $?
. Зберіть і зв’яжіть його в статичний двійковий файл, наприклад
$ asm-link -m32 x86-modedetect-polyglot.asm && ./x86-modedetect-polyglot; echo $?
+ yasm -felf32 -Worphan-labels -gdwarf2 x86-modedetect-polyglot.asm
+ ld -melf_i386 -o x86-modedetect-polyglot x86-modedetect-polyglot.o
32
$ asm-link -m64 x86-modedetect-polyglot.asm && ./x86-modedetect-polyglot; echo $?
+ yasm -felf64 -Worphan-labels -gdwarf2 x86-modedetect-polyglot.asm
+ ld -o x86-modedetect-polyglot x86-modedetect-polyglot.o
64
## maybe test 16-bit with BOCHS somehow if you really want to.
7 байт (бал = 2,33), якщо я можу пронумерувати версії 1, 2, 3
Офіційних номерів версій для різних режимів x86 немає. Мені просто подобається писати відповіді на Asm. Я думаю, що це порушило б намір питання, якби я просто назвав режими 1,2,3 або 0,1,2, оскільки справа в тому, щоб змусити вас генерувати незручне число. Але якщо це було дозволено:
# 16-bit mode:
42 detect123:
43 00000020 B80300 mov ax,3
44 00000023 FEC8 dec al
45
46 00000025 48 dec ax
47 00000026 C3 ret
Який декодується в 32-бітному режимі як
08048080 <detect123>:
8048080: b8 03 00 fe c8 mov eax,0xc8fe0003
8048085: 48 dec eax
8048086: c3 ret
і 64-розрядні як
00000000004000a0 <detect123>:
4000a0: b8 03 00 fe c8 mov eax,0xc8fe0003
4000a5: 48 c3 rex.W ret