Exercism के x86-64 ट्रैक में असेम्बलर के तौर पर NASM (The Netwide Assembler) इस्तेमाल होता है।
NASM में कमेंट से पहले एक सेमीकोलन (;) लगाया जाता है।
कमेंट प्रोग्राम में कहीं भी रखे जा सकते हैं, और ; के बाद आने वाली हर चीज़ को असेम्बलर अनदेखा कर देता है।
NASM में equ की मदद से असेम्बलर-टाइम कॉन्स्टेंट परिभाषित किया जा सकता है।
जैसे यहाँ UNIVERSE नाम का एक कॉन्स्टेंट परिभाषित किया गया है, जिसकी वैल्यू 42 है:
UNIVERSE equ 42
कॉन्स्टेंट की गणना परिभाषित करते समय एक ही बार होती है, और बाद में उसे दोबारा परिभाषित नहीं किया जा सकता।
असेम्बली में हम आम तौर पर वैल्यू रखने के लिए वेरिएबल के बजाय रजिस्टर इस्तेमाल करते हैं। CPU रजिस्टर कंप्यूटर के प्रोसेसर के अंदर मौजूद तेज़ मेमोरी का एक टुकड़ा होता है। ज़्यादातर गणनाएँ CPU के रजिस्टरों में ही होती हैं।
कुछ रजिस्टर तरह-तरह की गणनाओं के लिए इस्तेमाल होते हैं और उन्हें जनरल परपस रजिस्टर (GPRs) कहा जाता है। बाकी रजिस्टरों का कोई खास या तय काम होता है।
x86-64 में 16 जनरल परपस रजिस्टर होते हैं, जिनमें से हर एक 64 बिट का होता है। इन्हें 32 बिट, 16 बिट या 8 बिट के रूप में भी इस्तेमाल किया जा सकता है।
GPRs नीचे दिए गए हैं, जहाँ rn में n का मान 8 से 15 तक जाता है: r8, r9, r10, r11, r12, r13, r14 और r15.
| 64-बिट | 32-बिट | 16-बिट | 8-बिट |
|---|---|---|---|
| rax | eax | ax | ah/al |
| rbx | ebx | bx | bh/bl |
| rcx | ecx | cx | ch/cl |
| rdx | edx | dx | dh/dl |
| rsi | esi | si | sil |
| rdi | edi | di | dil |
| rbp | ebp | bp | bpl |
| rsp | esp | sp | spl |
| rn | rnd | rnw | rnb |
जब 64 बिट से कम इस्तेमाल होते हैं, तो आम तौर पर रजिस्टर के निचले हिस्से के बिट इस्तेमाल होते हैं।
इस नियम का अपवाद ah, bh, ch और dh हैं, जो रजिस्टर के 16 बिट वाले हिस्से के ऊपरी 8 बिट इस्तेमाल करते हैं।
यह चित्र दिखाता है कि rax रजिस्टर के बिट किस तरह इस्तेमाल होते हैं:
+--------+---------------------------------------+
| 64-bit | rax |
+--------+-------------------+-------------------+
| 32-bit | | eax |
+--------+-------------------+---------+---------+
| 16-bit | | ax |
+--------+-----------------------------+----+----+
| 8-bit | | ah | al |
+--------+-----------------------------+----+----+
इनमें से कुछ रजिस्टरों को फंक्शन कॉल के दौरान सुरक्षित रखना ज़रूरी होता है: rbp, rsp, rbx, r12, r13, r14 और r15।
इन्हें सुरक्षित न रखने पर एरर हो सकती है या व्यवहार अपरिभाषित हो सकता है।
बाकी रजिस्टरों को सुरक्षित रखना ज़रूरी नहीं होता और इन्हें खुलकर इस्तेमाल किया जा सकता है: rax, rcx, rdx, rdi, rsi, r8, r9, r10 और r11।
सिलेबस में आगे आप सीखेंगे कि रजिस्टरों को सुरक्षित कैसे रखा जाता है।
अभी के लिए सिर्फ दूसरे तरह के रजिस्टर इस्तेमाल कीजिए: rax, rcx, rdx, rdi, rsi, r8, r9, r10 और r11।
इंस्ट्रक्शन ऐसी गणनाएँ हैं जो CPU कर सकता है। इनका रूप आम तौर पर ऐसा होता है:
name destination, source
यानी सबसे पहले इंस्ट्रक्शन का नाम आता है, फिर कम से कम एक खाली जगह, उसके बाद डेस्टिनेशन ऑपरेंड, फिर एक कॉमा (,) और आखिर में सोर्स ऑपरेंड।
किसी इंस्ट्रक्शन से आम तौर पर सोर्स ऑपरेंड नहीं बदलता, सिर्फ डेस्टिनेशन ऑपरेंड बदलता है।
जब तक अलग से कुछ न कहा जाए, दोनों ऑपरेंड का साइज़ एक जैसा होना चाहिए। जैसे अगर सोर्स ऑपरेंड 16 बिट का है, तो डेस्टिनेशन ऑपरेंड भी 16 बिट का होना चाहिए।
जैसे किसी रजिस्टर में वैल्यू रखने के लिए हम mov इंस्ट्रक्शन इस्तेमाल कर सकते हैं:
mov rax, rdx ; rax = rdx
; mov is the name, rax is the destination operand and rdx is the source operand
ऊपर दिया गया स्निपेट सोर्स ऑपरेंड rdx के सभी 64 बिट का कंटेंट डेस्टिनेशन ऑपरेंड rax के सभी 64 बिट में कॉपी कर देता है।
ज़्यादातर इंस्ट्रक्शन में पूर्णांक कॉन्स्टेंट को सोर्स ऑपरेंड के तौर पर इस्तेमाल किया जा सकता है (लेकिन डेस्टिनेशन ऑपरेंड के तौर पर नहीं):
mov rcx, 42 ; rcx = 42
; rcx is the destination operand and 42 is the source operand
ध्यान रखिए कि 32 बिट वाले रजिस्टर में लिखने पर ऊपरी बिट भी साफ हो जाते हैं, इसलिए mov eax, 42 और mov rax, 42 एक ही बात हैं।
16 बिट और 8 बिट वाले रजिस्टरों पर यह बात लागू नहीं होती।
जोड़, घटाव और गुणा जैसी अंकगणितीय क्रियाओं के लिए हम add, sub और imul इंस्ट्रक्शन इस्तेमाल कर सकते हैं:
add rax, rsi ; rax = rax + rsi
imul rax, rdi ; rax = rax * rdi
sub rax, r8 ; rax = rax - r8
इंस्ट्रक्शन फंक्शनों में व्यवस्थित रहते हैं। सभी फंक्शन सोर्स फाइल के section .text में रखे जाते हैं।
फंक्शन की घोषणा में ये चीज़ें शामिल होती हैं:
: लगता है।ret।यह ट्रैक System V AMD64 ABI कॉलिंग कन्वेंशन इस्तेमाल करता है, और किसी फंक्शन को पहले छह पूर्णांक आर्गुमेंट रजिस्टरों के ज़रिए पास किए जाते हैं।
इनका क्रम इस तरह होता है: rdi, rsi, rdx, rcx, r8 और r9।
फंक्शन से पूर्णांक वैल्यू rax रजिस्टर में लौटाई जाती है।
किसी फंक्शन को कॉल करने के लिए हम call इंस्ट्रक्शन इस्तेमाल करते हैं।
एक ही सोर्स फाइल में कहीं से भी किसी फंक्शन को कॉल किया जा सकता है।
किसी फंक्शन को दूसरी सोर्स फाइलों के लिए दिखाने के लिए global डायरेक्टिव इस्तेमाल करना ज़रूरी है।
जैसे यहाँ sum नाम का एक फंक्शन घोषित किया गया है:
section .text ; functions are placed here
global sum ; sum is visible to other source files
sum:
; first argument is passed in rdi
; second argument is passed in rsi
; return value is placed in rax
mov rax, rdi ; rax is now equal to rdi
add rax, rsi ; rax = rax + rsi
ret ; function return
और यह हमारे sum फंक्शन को 3 और 5 आर्गुमेंट के साथ कॉल करता है:
mov rdi, 3 ; First argument in rdi
mov rsi, 5 ; Second argument in rsi
call sum
; The rax register now contains the value 8 (3 + 5), after sum returns
इस अभ्यास में आप कुछ कोड लिखेंगे जो आपको अपनी पसंदीदा कुकबुक से एक शानदार लसानिया पकाने में मदद करेगा।
आपके सामने चार काम हैं, और सब लसानिया पकाने में लगने वाले समय से जुड़े हैं।
ये वे निर्देश हैं जिनका उल्लेख इस कॉन्सेप्ट में किया गया है:
| Instruction | Description |
|---|---|
| mov a, b | copies the contents from b to a |
| add a, b | a = a + b |
| sub a, b | a = a - b |
| imul a, b | a = a * b |
| call a | calls function a |
| ret | returns from a function |
expected_minutes_in_oven फंक्शन बनाइए जो कोई पैरामीटर नहीं लेता और लौटाता है कि लसानिया को ओवन में कितने मिनट रहना चाहिए।
कुकबुक के अनुसार, ओवन में लगने वाला अपेक्षित समय 40 मिनट है:
expected_minutes_in_oven();
// => 40
remaining_minutes_in_oven फंक्शन बनाइए जो लसानिया के ओवन में रहने के वास्तविक मिनटों को पैरामीटर के रूप में लेता है और पिछले काम में बताए गए ओवन के अपेक्षित समय के आधार पर लौटाता है कि लसानिया को ओवन में अभी कितने मिनट और रहना है।
remaining_minutes_in_oven(30);
// => 10
preparation_time_in_minutes फंक्शन बनाइए जो लसानिया में लगाई गई परतों की संख्या को पैरामीटर के रूप में लेता है और लौटाता है कि लसानिया तैयार करने में आपने कितने मिनट लगाए। यह मानते हुए कि हर परत तैयार करने में आपको 2 मिनट लगते हैं।
preparation_time_in_minutes(2);
// => 4
elapsed_time_in_minutes फंक्शन बनाइए जो दो पैरामीटर लेता है, इसी क्रम में:
यह फंक्शन लौटाता है कि लसानिया पकाने में आपने कितने मिनट काम किया। यह तैयारी में लगे मिनटों और अब तक लसानिया के ओवन में बिताए मिनटों का जोड़ है।
elapsed_time_in_minutes(3, 20);
// => 26
Exercism पर साइन अप कीजिए और x86-64 Assembly को 22 कॉन्सेप्ट130 अभ्यास तथा असली इंसानों से मिलने वाली मेंटरिंग के साथ सीखिए और उसमें महारत हासिल कीजिए, वह भी बिल्कुल मुफ्त।