mã máy x86 16/32/64 bit: 11 byte, điểm = 3,66
Hàm này trả về chế độ hiện tại (kích thước toán hạng mặc định) dưới dạng một số nguyên trong AL. Gọi nó từ C có chữ kýuint8_t modedetect(void);
Danh sách mã + mã máy của NASM (hiển thị cách thức hoạt động ở chế độ 16 bit, kể từ khi BITS 16
yêu cầu NASM lắp ráp các bản ghi nhớ nguồn cho chế độ 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
Biện minh :
Mã máy x86 không chính thức có số phiên bản, nhưng tôi nghĩ rằng điều này thỏa mãn mục đích của câu hỏi bằng cách tạo ra các số cụ thể, thay vì chọn những gì thuận tiện nhất (chỉ mất 7 byte, xem bên dưới).
CPU x86 ban đầu, 8086 của Intel, chỉ hỗ trợ mã máy 16 bit. 80386 đã giới thiệu mã máy 32 bit (có thể sử dụng ở chế độ được bảo vệ 32 bit và sau đó ở chế độ tương thích trong HĐH 64 bit). AMD giới thiệu mã máy 64 bit, có thể sử dụng ở chế độ dài. Đây là các phiên bản của ngôn ngữ máy x86 theo cùng nghĩa rằng Python2 và Python3 là các phiên bản ngôn ngữ khác nhau. Chúng hầu hết tương thích, nhưng với những thay đổi có chủ ý. Bạn có thể chạy các tệp thực thi 32 hoặc 64 bit trực tiếp trong nhân hệ điều hành 64 bit giống như cách bạn có thể chạy các chương trình Python2 và Python3.
Làm thế nào nó hoạt động:
Bắt đầu với al=64
. Di chuyển nó sang phải 1 (chế độ 32 bit) hoặc 2 (chế độ 16 bit).
16/32 so với 64 bit: Mã hóa 1 byte inc
/ dec
mã hóa là tiền tố REX trong 64 bit ( http://wiki.osdev.org/X86-64_In cản_Encoding#REX_prefix ). REX.W hoàn toàn không ảnh hưởng đến một số hướng dẫn (ví dụ: a jmp
hoặc jcc
), nhưng trong trường hợp này để có được 16/32/64 tôi muốn inc hoặc dec ecx
thay vì eax
. Điều đó cũng thiết lập REX.B
, mà thay đổi đăng ký đích. Nhưng may mắn thay, chúng ta có thể thực hiện công việc đó nhưng thiết lập mọi thứ để 64-bit không cần phải thay đổi al
.
(Các) lệnh chỉ chạy ở chế độ 16 bit có thể bao gồm a ret
, nhưng tôi không thấy cần thiết hoặc hữu ích. (Và sẽ làm cho nó không thể nội tuyến như một đoạn mã, trong trường hợp bạn muốn làm điều đó). Nó cũng có thể là một jmp
trong chức năng.
16 bit so với 32/64: tức thì là 16 bit thay vì 32 bit. Việc thay đổi chế độ có thể thay đổi độ dài của một lệnh, vì vậy các chế độ 32/64 bit giải mã hai byte tiếp theo như một phần của lệnh ngay lập tức, thay vì một lệnh riêng biệt. Tôi giữ mọi thứ đơn giản bằng cách sử dụng lệnh 2 byte ở đây, thay vì giải mã không đồng bộ để chế độ 16 bit sẽ giải mã từ các ranh giới lệnh khác nhau hơn 32/64.
Liên quan: Tiền tố kích thước toán hạng thay đổi độ dài của tức thời (trừ khi đó là tiền tố 8 bit được mở rộng ký hiệu), giống như sự khác biệt giữa chế độ 16 bit và 32/64 bit. Điều này làm cho việc giải mã độ dài lệnh khó thực hiện song song; CPU Intel có quầy giải mã LCP .
Hầu hết các quy ước gọi (bao gồm các psABI Hệ thống x86-32 và x86-64) cho phép các giá trị trả về hẹp có rác trong các bit cao của thanh ghi. Chúng cũng cho phép ghi đè CX / ECX / RCX (và R8 cho 64 bit). IDK nếu điều đó là phổ biến trong các quy ước gọi 16 bit, nhưng đây là mã golf nên tôi luôn có thể nói đó là quy ước gọi tùy chỉnh.
Tháo gỡ 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
Tháo gỡ 64 bit ( Dùng thử trực tuyến! ):
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
Liên quan: Hỏi & Đáp mã máy x86-32 / x86-64 của tôi trên SO.
Một điểm khác biệt giữa 16 bit và 32/64 là chế độ địa chỉ được mã hóa khác nhau. ví dụ: lea eax, [rax+2]
( 8D 40 02
giải mã như lea ax, [bx+si+0x2]
trong chế độ 16 bit. Điều này rõ ràng là khó sử dụng cho môn đánh gôn, đặc biệt là từ e/rbx
vàe/rsi
được bảo toàn cuộc gọi trong nhiều quy ước gọi.
Tôi cũng đã cân nhắc sử dụng 10 byte mov r64, imm64
, đó là REX + mov r32,imm32
. Nhưng vì tôi đã có một giải pháp 11 byte, nên điều này sẽ tốt nhất bằng (10 byte + 1 cho ret
).
Mã kiểm tra cho chế độ 32 và 64 bit. (Tôi chưa thực sự thực hiện nó ở chế độ 16 bit, nhưng quá trình tháo gỡ cho bạn biết nó sẽ giải mã như thế nào. Tôi không có trình giả lập 16 bit được thiết lập.)
; 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
Chương trình Linux này thoát với exit-status = modedetect()
, vì vậy hãy chạy nó dưới dạng ./a.out; echo $?
. Lắp ráp và liên kết nó thành một nhị phân tĩnh, vd
$ 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 (điểm = 2,33) nếu tôi có thể đánh số các phiên bản 1, 2, 3
Không có số phiên bản chính thức cho các chế độ x86 khác nhau. Tôi chỉ thích viết câu trả lời asm. Tôi nghĩ rằng nó sẽ vi phạm ý định của câu hỏi nếu tôi chỉ gọi các chế độ 1,2,3 hoặc 0,1,2, vì vấn đề là buộc bạn phải tạo ra một số bất tiện. Nhưng nếu điều đó được cho phép:
# 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
Giải mã ở chế độ 32 bit là
08048080 <detect123>:
8048080: b8 03 00 fe c8 mov eax,0xc8fe0003
8048085: 48 dec eax
8048086: c3 ret
và 64 bit như
00000000004000a0 <detect123>:
4000a0: b8 03 00 fe c8 mov eax,0xc8fe0003
4000a5: 48 c3 rex.W ret