x86 16/32/64-bit kode mesin: 11 byte, skor = 3,66
Fungsi ini mengembalikan mode saat ini (ukuran operan default) sebagai integer dalam AL. Sebut saja dari C dengan tanda tanganuint8_t modedetect(void);
Daftar kode mesin + sumber mesin NASM (menunjukkan cara kerjanya dalam mode 16-bit, karena BITS 16
memberitahu NASM untuk merakit source mnemonics untuk mode 16-bit.)
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
Pembenaran :
kode mesin x86 tidak secara resmi memiliki nomor versi, tetapi saya pikir ini memuaskan maksud pertanyaan dengan harus menghasilkan angka-angka tertentu, daripada memilih apa yang paling nyaman (yang hanya memakan waktu 7 byte, lihat di bawah).
CPU x86 asli, Intel 8086, hanya mendukung kode mesin 16-bit. 80386 memperkenalkan kode mesin 32-bit (dapat digunakan dalam mode dilindungi 32-bit, dan kemudian dalam mode compat di bawah OS 64-bit). AMD memperkenalkan kode mesin 64-bit, dapat digunakan dalam mode panjang. Ini adalah versi bahasa mesin x86 dalam arti yang sama bahwa Python2 dan Python3 adalah versi bahasa yang berbeda. Mereka sebagian besar kompatibel, tetapi dengan perubahan yang disengaja. Anda dapat menjalankan 32 atau 64-bit executable langsung di bawah kernel OS 64-bit dengan cara yang sama Anda bisa menjalankan program Python2 dan Python3.
Bagaimana itu bekerja:
Mulai dengan al=64
. Geser ke kanan dengan 1 (mode 32-bit) atau 2 (mode 16-bit).
16/32 vs. 64-bit: 1-byte inc
/ dec
encodings adalah awalan REX dalam 64-bit ( http://wiki.osdev.org/X86-64_Instruction_Encoding#REX_prefix ). REX.W sama sekali tidak memengaruhi beberapa instruksi (misalnya a jmp
atau jcc
), tetapi dalam hal ini untuk mendapatkan 16/32/64 saya ingin memasukkan atau memutuskan ecx
daripada eax
. Itu juga set REX.B
, yang mengubah register tujuan. Tapi untungnya kita bisa melakukan itu tetapi mengatur semuanya agar 64-bit tidak perlu bergeser al
.
Instruksi (s) yang dijalankan hanya dalam mode 16-bit dapat menyertakan ret
, tetapi saya tidak menemukan itu perlu atau membantu. (Dan akan membuatnya mustahil untuk sebaris sebagai sebuah fragmen kode, jika Anda ingin melakukan itu). Bisa juga jmp
dalam fungsi.
16-bit vs 32/64: segera adalah 16-bit, bukan 32-bit. Mengubah mode dapat mengubah panjang instruksi, jadi mode 32/64 bit mendekodekan dua byte berikutnya sebagai bagian dari instruksi langsung, bukan instruksi terpisah. Saya menjaga hal-hal sederhana dengan menggunakan instruksi 2-byte di sini, daripada mendapatkan decode dari sinkronisasi sehingga mode 16-bit akan memecahkan kode dari batas instruksi yang berbeda dari 32/64.
Terkait: Awalan ukuran operan mengubah panjang langsung (kecuali jika itu langsung diperpanjang 8-bit), sama seperti perbedaan antara mode 16-bit dan 32/64-bit. Ini membuat decoding panjang instruksi sulit dilakukan secara paralel; CPU Intel memiliki warung decoding LCP .
Kebanyakan konvensi pemanggilan (termasuk psABI Sistem V x86-32 dan x86-64) memungkinkan nilai pengembalian yang sempit memiliki sampah di bit register yang tinggi. Mereka juga memungkinkan clobbering CX / ECX / RCX (dan R8 untuk 64-bit). IDK jika itu biasa dalam konvensi pemanggilan 16-bit, tapi ini kode golf jadi saya selalu bisa mengatakan itu konvensi pemanggilan kustom.
Pembongkaran 32-bit :
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
Pembongkaran 64-bit ( Coba online! ):
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
Terkait: kode mesin x86-32 / x86-64 saya T&J di SO.
Perbedaan lain antara 16-bit dan 32/64 adalah bahwa mode pengalamatan dikodekan secara berbeda. eg lea eax, [rax+2]
( 8D 40 02
) menerjemahkan seperti lea ax, [bx+si+0x2]
dalam mode 16-bit. Ini jelas sulit digunakan untuk kode-golf, terutama karena e/rbx
dane/rsi
dilindungi panggilan dalam banyak konvensi pemanggilan.
Saya juga mempertimbangkan untuk menggunakan 10-byte mov r64, imm64
, yaitu REX + mov r32,imm32
. Tetapi karena saya sudah memiliki solusi 11 byte, ini akan menjadi yang terbaik sama (10 byte +1 untuk ret
).
Kode uji untuk mode 32 dan 64-bit. (Saya belum benar-benar menjalankannya dalam mode 16-bit, tetapi pembongkaran memberitahu Anda bagaimana itu akan memecahkan kode. Saya tidak memiliki emulator 16-bit yang diatur.)
; 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
Program Linux ini keluar dengan status keluar = modedetect()
, jadi jalankan sebagai ./a.out; echo $?
. Merakit dan menautkannya ke biner statis, misalnya
$ 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 byte (skor = 2.33) jika saya dapat memberi nomor versi 1, 2, 3
Tidak ada nomor versi resmi untuk mode x86 berbeda. Saya hanya suka menulis jawaban asm. Saya pikir itu akan melanggar maksud pertanyaan jika saya hanya memanggil mode 1,2,3, atau 0,1,2, karena intinya adalah memaksa Anda untuk menghasilkan angka yang merepotkan. Tetapi jika itu diizinkan:
# 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
Yang menerjemahkan dalam mode 32-bit sebagai
08048080 <detect123>:
8048080: b8 03 00 fe c8 mov eax,0xc8fe0003
8048085: 48 dec eax
8048086: c3 ret
dan 64-bit sebagai
00000000004000a0 <detect123>:
4000a0: b8 03 00 fe c8 mov eax,0xc8fe0003
4000a5: 48 c3 rex.W ret