x86-64 รหัสเครื่อง 24 ไบต์
6A 0A 5E 31 C9 89 F8 99 F7 F6 01 D1 85 C0 75 F7 8D 04 09 99 F7 F7 92 C3
รหัสด้านบนจะกำหนดฟังก์ชั่นในรหัสเครื่อง 64 บิต x86 ที่กำหนดว่าค่าอินพุตถูกหารด้วยผลรวมของตัวเลขสองหลักหรือไม่ ฟังก์ชั่นนี้เป็นไปตามข้อกำหนดการเรียกใช้ System V AMD64 เพื่อให้สามารถเรียกใช้ได้จากแทบทุกภาษาเหมือนกับว่าเป็นฟังก์ชัน C
ใช้พารามิเตอร์เดียวเป็นอินพุตผ่านEDI
register ซึ่งเป็นไปตามระเบียบการเรียกซึ่งเป็นจำนวนเต็มเพื่อทดสอบ (นี่จะถือว่าเป็นจำนวนเต็มบวกสอดคล้องกับกฎท้าทายและจำเป็นสำหรับCDQ
คำสั่งที่เราใช้ในการทำงานอย่างถูกต้อง)
มันจะส่งคืนผลลัพธ์ในEAX
รีจิสเตอร์อีกครั้งตามระเบียบการโทร ผลลัพธ์จะเป็น 0 หากค่าอินพุตถูกหารด้วยผลรวมของตัวเลขและไม่ใช่ศูนย์มิฉะนั้น (โดยทั่วไปแล้วบูลีนผกผันเหมือนกับตัวอย่างที่ให้ไว้ในกฎการท้าทาย)
ต้นแบบ C มันจะเป็น:
int DivisibleByDoubleSumOfDigits(int value);
ต่อไปนี้เป็นคำแนะนำภาษาแอสเซมบลีที่ไม่ได้รับการบันทึกประกอบคำอธิบายโดยย่อเกี่ยวกับวัตถุประสงค์ของการเรียนการสอนแต่ละคำ:
; EDI == input value
DivisibleByDoubleSumOfDigits:
push 10
pop rsi ; ESI <= 10
xor ecx, ecx ; ECX <= 0
mov eax, edi ; EAX <= EDI (make copy of input)
SumDigits:
cdq ; EDX <= 0
div esi ; EDX:EAX / 10
add ecx, edx ; ECX += remainder (EDX)
test eax, eax
jnz SumDigits ; loop while EAX != 0
lea eax, [rcx+rcx] ; EAX <= (ECX * 2)
cdq ; EDX <= 0
div edi ; EDX:EAX / input
xchg edx, eax ; put remainder (EDX) in EAX
ret ; return, with result in EAX
ในบล็อกแรกเราทำการเริ่มต้นเบื้องต้นของการลงทะเบียน:
PUSH
+ POP
คำสั่งถูกใช้เป็นวิธีที่ช้า แต่สั้น ๆ ในการเริ่มต้นESI
ถึง 10 สิ่งนี้เป็นสิ่งจำเป็นเนื่องจากDIV
คำสั่งใน x86 ต้องการตัวถูกดำเนินการลงทะเบียน (ไม่มีรูปแบบที่หารด้วยค่าทันทีคือ, พูด, 10. )
XOR
ใช้เป็นวิธีสั้นและรวดเร็วในการล้างข้อมูลการECX
ลงทะเบียน การลงทะเบียนนี้จะทำหน้าที่เป็น "ตัวสะสม" ภายในวงที่กำลังจะมาถึง
- ในที่สุดก็มีการทำสำเนาของค่าอินพุต (จาก
EDI
) และเก็บไว้ในEAX
ซึ่งจะถูกอุดตันเมื่อเราผ่านลูป
จากนั้นเราเริ่มวนซ้ำและสรุปตัวเลขในค่าอินพุต นี้จะขึ้นอยู่กับ x86 DIV
คำแนะนำซึ่งแบ่งEDX:EAX
โดยถูกดำเนินการและผลตอบแทนที่ฉลาดในและที่เหลือในEAX
EDX
สิ่งที่เราจะทำที่นี่คือหารค่าอินพุตด้วย 10 ส่วนที่เหลือคือตัวเลขในตำแหน่งสุดท้าย (ซึ่งเราจะเพิ่มในการลงทะเบียนแอECX
คคิวมูเลเตอร์ของเรา) และผลหารคือตัวเลขที่เหลือ
CDQ
การเรียนการสอนเป็นวิธีที่สั้น ๆ ของการตั้งค่าEDX
เป็น 0 มันจริงลงชื่อเข้าใช้ขยายค่าในEAX
การEDX:EAX
ซึ่งเป็นสิ่งที่DIV
ใช้เป็นเงินปันผล เราไม่ต้องการส่วนขยายสัญญาณที่นี่จริง ๆ เนื่องจากค่าอินพุตนั้นไม่ได้ลงนาม แต่CDQ
เป็น 1 ไบต์ซึ่งต่างจากการใช้XOR
เพื่อล้างEDX
ซึ่งจะเป็น 2 ไบต์
- แล้วเรา
DIV
ide EDX:EAX
โดยESI
(10)
- ส่วนที่เหลือ (
EDX
) ถูกเพิ่มเข้าไปในแอคคิวมูเลเตอร์ ( ECX
)
EAX
ทะเบียน (ความฉลาด) มีการทดสอบเพื่อดูว่ามันจะมีค่าเท่ากับ 0 ถ้าเช่นนั้นเราได้ทำให้มันผ่านทั้งหมดของตัวเลขและเราตกผ่าน ถ้าไม่เรายังคงมีจำนวนผลรวมมากกว่าดังนั้นเรากลับไปที่ด้านบนของลูป
ในที่สุดหลังจากลูปเสร็จสิ้นเราจะใช้number % ((sum_of_digits)*2)
:
LEA
การเรียนการสอนจะใช้เป็นวิธีที่สั้นคูณECX
2 (หรือเท่ากันเพิ่มECX
กับตัวเอง) และจัดเก็บผลในการลงทะเบียนที่แตกต่างกัน (ในกรณีนี้EAX
)
(เราสามารถทำadd ecx, ecx
+ ได้xchg ecx, eax
ทั้งสองมี 3 ไบต์ แต่LEA
คำสั่งนั้นเร็วกว่าและทั่วไปกว่า)
- จากนั้นเราทำ
CDQ
อีกครั้งเพื่อเตรียมพร้อมสำหรับการหาร เนื่องจากEAX
จะเป็นค่าบวก (กล่าวคือไม่ได้ลงนาม) สิ่งนี้มีผลของการEDX
เป็นศูนย์เหมือนเมื่อก่อน
- ถัดไปคือการหารคราวนี้หาร
EDX:EAX
ด้วยค่าอินพุต (สำเนาที่ไม่ได้ทำลายซึ่งยังคงอยู่EDI
) EDX
นี้จะเทียบเท่ากับโมดูโลกับที่เหลือใน (ความฉลาดยังใส่อยู่EAX
แต่เราไม่ต้องการ)
- สุดท้ายเรา
XCHG
(แลกเปลี่ยน) เนื้อหาของและEAX
EDX
โดยปกติคุณจะทำMOV
ที่นี่ แต่XCHG
เพียง 1 ไบต์ (แม้ว่าช้ากว่า) เนื่องจากEDX
มีส่วนที่เหลือหลังการหารมันจะเป็น 0 ถ้าค่านั้นหารได้อย่างสม่ำเสมอหรือไม่เป็นศูนย์ ดังนั้นเมื่อเราRET
โกศEAX
(ผลลัพธ์) คือ 0 ถ้าค่าอินพุตถูกหารด้วยสองผลรวมของตัวเลขหรือไม่ใช่ศูนย์มิฉะนั้น
หวังว่าพอเพียงสำหรับคำอธิบาย
นี่ไม่ใช่รายการที่สั้นที่สุด แต่เดี๋ยวก่อนดูเหมือนว่ามันจะเป็นภาษาที่ไม่ใช่กอล์ฟเกือบทั้งหมด! :-)