x86-64 मशीन कोड, 30 बाइट्स
31 C0 99 8B 4C B7 FC F6 C1 01 74 04 01 CA EB 02 01 C8 FF CE 75 ED 29 D0 99 31 D0 29 D0 C3
उपरोक्त कोड एक फ़ंक्शन को परिभाषित करता है जो पूर्णांक अंकों की सूची / सरणी को स्वीकार करता है और इसके सम अंकों के योग और इसके विषम अंकों के योग के बीच पूर्ण अंतर लौटाता है।
जैसा कि सी में , असेंबली भाषा प्रथम श्रेणी के प्रकारों के रूप में सूचियों या सरणियों को लागू नहीं करती है, बल्कि एक पॉइंटर और लंबाई के संयोजन के रूप में उनका प्रतिनिधित्व करती है। इसलिए, मैंने इस फ़ंक्शन को दो मापदंडों को स्वीकार करने के लिए व्यवस्थित किया है: पहला अंक की सूची की शुरुआत के लिए एक संकेतक है, और दूसरा एक पूर्णांक है जो सूची की कुल लंबाई (अंकों की कुल संख्या, एक-अनुक्रमित) को निर्दिष्ट करता है। ।
फ़ंक्शन सिस्टम V AMD64 कॉलिंग कन्वेंशन के अनुरूप है , जो कि Gnu / UNIX सिस्टम पर मानक है। विशेष रूप से, पहला पैरामीटर (सूची की शुरुआत के लिए सूचक) में पारित किया जाता है RDI(जैसा कि यह 64-बिट कोड है, यह 64-बिट सूचक है), और दूसरा पैरामीटर (सूची की लंबाई) में पारित किया गया है ESI( यह केवल 32-बिट मान है, क्योंकि यह खेलने के लिए पर्याप्त अंकों से अधिक है, और स्वाभाविक रूप से इसे गैर-शून्य माना जाता है)। परिणाम EAXरजिस्टर में वापस कर दिया जाता है ।
यदि यह कोई स्पष्ट है, तो यह सी प्रोटोटाइप होगा (और आप इसे C से फ़ंक्शन को कॉल करने के लिए उपयोग कर सकते हैं):
int OddsAndEvens(int *ptrDigits, int length);
असेंबली असेंबली mnemonics:
; parameter 1 (RDI) == pointer to list of integer digits
; parameter 2 (ESI) == number of integer digits in list (assumes non-zero, of course)
OddsAndEvens:
xor eax, eax ; EAX = 0 (accumulator for evens)
cdq ; EDX = 0 (accumulator for odds)
.IterateDigits:
mov ecx, [rdi+rsi*4-4] ; load next digit from list
test cl, 1 ; test last bit to see if even or odd
jz .IsEven ; jump if last bit == 0 (even)
.IsOdd: ; fall through if last bit != 0 (odd)
add edx, ecx ; add value to odds accumulator
jmp .Continue ; keep looping
.IsEven:
add eax, ecx ; add value to evens accumulator
.Continue: ; fall through
dec esi ; decrement count of digits in list
jnz .IterateDigits ; keep looping as long as there are digits left
sub eax, edx ; subtract odds accumulator from evens accumulator
; abs
cdq ; sign-extend EAX into EDX
xor eax, edx ; XOR sign bit in with the number
sub eax, edx ; subtract sign bit
ret ; return with final result in EAX
यहाँ कोड के बारे में संक्षिप्त जानकारी दी गई है:
- सबसे पहले, हम
EAXऔर EDXरजिस्टरों को शून्य करते हैं , जिसका उपयोग सम और विषम अंकों के योग को पकड़ने के लिए किया जाएगा। EAXरजिस्टर से हटा दिया गया है XORही (2 बाइट्स) के साथ यह ing, और फिर EDXरजिस्टर साइन-विस्तार में EAX (द्वारा मंजूरी दे दी है CDQ, 1 बाइट)।
फिर, हम उस लूप में जाते हैं जो सरणी में पारित सभी अंकों से गुजरता है। यह एक अंक प्राप्त करता है, यह देखने के लिए परीक्षण करता है कि क्या यह सम या विषम है (कम से कम महत्वपूर्ण बिट का परीक्षण करके, जो 0 होगा यदि मान सम है या 1 यदि यह विषम है), और फिर उसके अनुसार कूदता या गिरता है, उचित संचायक के लिए मूल्य। लूप के निचले भाग में, हम डिजिट काउंटर ( ESI) को घटाते हैं और तब तक लूपिंग जारी रखते हैं, जब तक वह नॉन-जीरो न हो (यानी, जब तक लिस्ट रिजेक्ट होने के लिहाज से ज्यादा अंक बचे हैं)।
यहां केवल एक चीज मुश्किल है प्रारंभिक MOV निर्देश, जो x86 पर संभव सबसे जटिल पते मोड का उपयोग करता है। * यह लेता हैRDI आधार रजिस्टर (सूची की शुरुआत का सूचक), तराजू RSI(लंबाई काउंटर, जो सूचकांक के रूप में कार्य करता है) को 4 (बाइट में एक पूर्णांक का आकार) के रूप में , और आधार में जोड़ता है, और फिर कुल से 4 घटाते हैं (क्योंकि लंबाई काउंटर एक-आधारित है और हमें शून्य-आधारित होने के लिए ऑफसेट की आवश्यकता होती है)। यह सरणी में अंक का पता देता है, जिसे बाद में ECXरजिस्टर में लोड किया जाता है ।
लूप समाप्त होने के बाद, हम बुराइयों से बाधाओं को घटाते हैं (EAX -= EDX ) ।
अंत में, हम एक कॉमन ट्रिक का उपयोग करते हुए निरपेक्ष मान की गणना करते हैं - वही जिसका उपयोग सी कंपाइलर द्वारा किया जाता है abs फ़ंक्शन के । मैं इस बारे में विवरण में नहीं जाऊँगा कि यह चाल यहाँ कैसे काम करती है; संकेत के लिए कोड टिप्पणियाँ देखें, या एक वेब खोज करें।
__
* कोड को सरल एड्रेसिंग मोड्स का उपयोग करने के लिए फिर से लिखा जा सकता है, लेकिन यह इसे छोटा नहीं बनाता है। मैं एक वैकल्पिक कार्यान्वयन के साथ आने में सक्षम था RDIजो लूप के माध्यम से प्रत्येक बार 8 से घटाया और बढ़ाया गया था, लेकिन क्योंकि आपको अभी भी काउंटर में कमी करनी है ESI, यह वही 30 बाइट्स निकला। मुझे शुरू में जो उम्मीद थी कि add eax, DWORD PTR [rdi]वह केवल 2 बाइट्स है, दो अपंजीकृत मूल्यों को जोड़ने के समान है। यहाँ वह कार्यान्वयन है, अगर केवल किसी को बचाने के प्रयास में किसी को बचाने के लिए मुझे कुछ प्रयास :-)
OddsAndEvens_Alt:
31 C0 xor eax, eax
99 cdq
.IterateDigits:
F6 07 01 test BYTE PTR [rdi], 1
74 04 je .IsEven
.IsOdd:
03 17 add edx, DWORD PTR [rdi]
EB 02 jmp .Continue
.IsEven:
03 07 add eax, DWORD PTR [rdi]
.Continue:
48 83 C7 08 add rdi, 8
FF CE dec esi
75 ED jne .IterateDigits
29 D0 sub eax, edx
99 cdq
31 D0 xor eax, edx
29 D0 sub eax, edx
C3 ret