x86 16/32/64-Bit-Computercode: 11 Byte, Score = 3,66
Diese Funktion gibt den aktuellen Modus (Standardoperandengröße) als Ganzzahl in AL zurück. Nennen Sie es von C mit Unterschriftuint8_t modedetect(void);
NASM-Maschinencode + Quellenliste (zeigt, wie es im 16-Bit-Modus funktioniert, da BITS 16
NASM angewiesen wird, die Quellmnemoniken für den 16-Bit-Modus zusammenzustellen.)
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
Begründung :
Der x86-Maschinencode hat offiziell keine Versionsnummern, aber ich denke, dies befriedigt die Absicht der Frage, indem bestimmte Nummern erzeugt werden müssen, anstatt zu wählen, was am bequemsten ist (das dauert nur 7 Bytes, siehe unten).
Die ursprüngliche x86-CPU, Intels 8086, unterstützte nur 16-Bit-Maschinencode. 80386 führte den 32-Bit-Maschinencode ein (verwendbar im geschützten 32-Bit-Modus und später im Kompatibilitätsmodus unter einem 64-Bit-Betriebssystem). AMD hat 64-Bit-Maschinencode eingeführt, der im Langzeitmodus verwendet werden kann. Hierbei handelt es sich um Versionen der x86-Maschinensprache in demselben Sinne wie bei Python2 und Python3 um unterschiedliche Sprachversionen. Sie sind größtenteils kompatibel, aber mit absichtlichen Änderungen. Sie können ausführbare 32- oder 64-Bit-Dateien direkt unter einem 64-Bit-Betriebssystemkern ausführen, genauso wie Sie Python2- und Python3-Programme ausführen können.
Wie es funktioniert:
Beginnen Sie mit al=64
. Verschieben Sie ihn um 1 (32-Bit-Modus) oder 2 (16-Bit-Modus) nach rechts.
16/32 vs. 64-Bit: Die 1-Byte inc
/ dec
Codierungen sind REX-Präfixe in 64-Bit ( http://wiki.osdev.org/X86-64_Instruction_Encoding#REX_prefix ). REX.W beeinflusst einige Anweisungen überhaupt nicht (z. B. ein jmp
oder jcc
), aber in diesem Fall wollte ich, um 16/32/64 zu erhalten, eher inc oder dec ecx
als eax
. Das setzt auch REX.B
, was das Zielregister ändert. Aber zum Glück können wir das schaffen, aber die Einrichtung von 64-Bit muss nicht verschoben werden al
.
Die Anweisung (en), die nur im 16-Bit-Modus ausgeführt werden, können u. A. Enthalten ret
, ich fand dies jedoch nicht erforderlich oder hilfreich. (Und würde es unmöglich machen, als Code-Fragment inline zu gehen, falls Sie das wollten). Es könnte auch eine jmp
innerhalb der Funktion sein.
16-Bit vs. 32/64: Sofort sind 16-Bit statt 32-Bit. Durch Ändern der Modi kann die Länge eines Befehls geändert werden. Daher decodieren 32/64-Bit-Modi die nächsten zwei Bytes als Teil des unmittelbaren Befehls und nicht als separater Befehl. Ich habe die Dinge einfach gehalten, indem ich hier einen 2-Byte-Befehl verwendet habe, anstatt die Dekodierung nicht synchron zu machen, damit der 16-Bit-Modus aus anderen Befehlsgrenzen als 32/64 dekodiert.
Verwandte Themen: Das Präfix für die Operandengröße ändert die Länge des Direktzugriffs (sofern es sich nicht um einen vorzeichenerweiterten 8-Bit-Direktzugriff handelt), genau wie der Unterschied zwischen 16-Bit- und 32/64-Bit-Modus. Dies macht es schwierig, eine Befehlslängendecodierung parallel durchzuführen; Intel-CPUs haben LCP-Dekodierungsstopps .
Die meisten Aufrufkonventionen (einschließlich der x86-32- und x86-64-System-V-psABIs) ermöglichen enge Rückgabewerte, die in den hohen Bits des Registers fehlerhaft sind. Sie ermöglichen auch das Clobbern von CX / ECX / RCX (und R8 für 64-Bit). IDK, wenn das in 16-Bit-Aufrufkonventionen üblich war, aber das ist Codegolf, also kann ich immer sagen, dass es sowieso eine benutzerdefinierte Aufrufkonvention ist.
32-Bit-Demontage :
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-Bit-Demontage ( Online ausprobieren! ):
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
Verwandte: meine x86-32 / x86-64 polyglotten Maschinencode Q & A auf SO.
Ein weiterer Unterschied zwischen 16-Bit und 32/64 besteht darin, dass die Adressierungsmodi unterschiedlich codiert sind. zB lea eax, [rax+2]
( 8D 40 02
) dekodiert wie lea ax, [bx+si+0x2]
im 16-Bit-Modus. Dies ist offensichtlich schwierig für Code-Golf zu verwenden, zumal e/rbx
und e/rsi
in vielen Call-Konventionen die Call-Erhaltung gewährleistet ist.
Ich habe auch überlegt, das 10-Byte-Format zu verwenden mov r64, imm64
, das REX + ist mov r32,imm32
. Da ich aber bereits eine 11-Byte-Lösung hatte, wäre dies bestenfalls gleich (10 Byte + 1 für ret
).
Testcode für 32- und 64-Bit-Modus. (Ich habe es noch nicht im 16-Bit-Modus ausgeführt, aber die Disassembly gibt an, wie es dekodiert wird. Ich habe keinen 16-Bit-Emulator eingerichtet.)
; 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
Dieses Linux-Programm wird mit exit-status = beendet modedetect()
. Führen Sie es also aus als ./a.out; echo $?
. Bauen Sie es zusammen und verknüpfen Sie es in eine statische Binärdatei, z
$ 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 Bytes (Punktzahl = 2,33), wenn ich die Versionen 1, 2, 3 nummerieren kann
Es gibt keine offiziellen Versionsnummern für verschiedene x86-Modi. Ich schreibe einfach gerne asm Antworten. Ich denke, es würde die Absicht der Frage verletzen, wenn ich nur die Modi 1,2,3 oder 0,1,2 nenne, weil der Punkt darin besteht, Sie zu zwingen, eine unbequeme Zahl zu erzeugen. Aber wenn das erlaubt war:
# 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
Welches dekodiert im 32-Bit-Modus als
08048080 <detect123>:
8048080: b8 03 00 fe c8 mov eax,0xc8fe0003
8048085: 48 dec eax
8048086: c3 ret
und 64-Bit als
00000000004000a0 <detect123>:
4000a0: b8 03 00 fe c8 mov eax,0xc8fe0003
4000a5: 48 c3 rex.W ret