लसाग्ना

लसाग्ना

सीखने का अभ्यास

परिचय

बुनियादी बातें

कमेंट

Exercism के x86-64 ट्रैक में असेम्बलर के तौर पर NASM (The Netwide Assembler) इस्तेमाल होता है। NASM में कमेंट से पहले एक सेमीकोलन (;) लगाया जाता है।

कमेंट प्रोग्राम में कहीं भी रखे जा सकते हैं, और ; के बाद आने वाली हर चीज़ को असेम्बलर अनदेखा कर देता है।

कॉन्स्टेंट

NASM में equ की मदद से असेम्बलर-टाइम कॉन्स्टेंट परिभाषित किया जा सकता है। जैसे यहाँ UNIVERSE नाम का एक कॉन्स्टेंट परिभाषित किया गया है, जिसकी वैल्यू 42 है:

UNIVERSE equ 42

कॉन्स्टेंट की गणना परिभाषित करते समय एक ही बार होती है, और बाद में उसे दोबारा परिभाषित नहीं किया जा सकता।

जनरल परपस रजिस्टर (GPRs)

असेम्बली में हम आम तौर पर वैल्यू रखने के लिए वेरिएबल के बजाय रजिस्टर इस्तेमाल करते हैं। 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।

Caution

सिलेबस में आगे आप सीखेंगे कि रजिस्टरों को सुरक्षित कैसे रखा जाता है। अभी के लिए सिर्फ दूसरे तरह के रजिस्टर इस्तेमाल कीजिए: 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 में रखे जाते हैं।

फंक्शन की घोषणा में ये चीज़ें शामिल होती हैं:

  1. फंक्शन के नाम वाला एक लेबल, जिसके बाद : लगता है।
  2. वे इंस्ट्रक्शन जो उस फंक्शन को बनाते हैं।
  3. रिटर्न इंस्ट्रक्शन, 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

निर्देश

इस अभ्यास में आप कुछ कोड लिखेंगे जो आपको अपनी पसंदीदा कुकबुक से एक शानदार लसानिया पकाने में मदद करेगा।

आपके सामने चार काम हैं, और सब लसानिया पकाने में लगने वाले समय से जुड़े हैं।

Note

ये वे निर्देश हैं जिनका उल्लेख इस कॉन्सेप्ट में किया गया है:

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

1. ओवन में लगने वाले अपेक्षित समय को मिनटों में तय कीजिए

expected_minutes_in_oven फंक्शन बनाइए जो कोई पैरामीटर नहीं लेता और लौटाता है कि लसानिया को ओवन में कितने मिनट रहना चाहिए। कुकबुक के अनुसार, ओवन में लगने वाला अपेक्षित समय 40 मिनट है:

expected_minutes_in_oven();
// => 40

2. ओवन में बाकी बचने वाला समय मिनटों में निकालिए

remaining_minutes_in_oven फंक्शन बनाइए जो लसानिया के ओवन में रहने के वास्तविक मिनटों को पैरामीटर के रूप में लेता है और पिछले काम में बताए गए ओवन के अपेक्षित समय के आधार पर लौटाता है कि लसानिया को ओवन में अभी कितने मिनट और रहना है।

remaining_minutes_in_oven(30);
// => 10

3. तैयारी का समय मिनटों में निकालिए

preparation_time_in_minutes फंक्शन बनाइए जो लसानिया में लगाई गई परतों की संख्या को पैरामीटर के रूप में लेता है और लौटाता है कि लसानिया तैयार करने में आपने कितने मिनट लगाए। यह मानते हुए कि हर परत तैयार करने में आपको 2 मिनट लगते हैं।

preparation_time_in_minutes(2);
// => 4

4. बीता हुआ समय मिनटों में निकालिए

elapsed_time_in_minutes फंक्शन बनाइए जो दो पैरामीटर लेता है, इसी क्रम में:

  1. लसानिया में लगाई गई परतों की संख्या।
  2. लसानिया के ओवन में रहने के मिनटों की संख्या।

यह फंक्शन लौटाता है कि लसानिया पकाने में आपने कितने मिनट काम किया। यह तैयारी में लगे मिनटों और अब तक लसानिया के ओवन में बिताए मिनटों का जोड़ है।

elapsed_time_in_minutes(3, 20);
// => 26
GitHub के ज़रिए संपादित करें यह लिंक एक नई विंडो या टैब में खुलता है
x86-64 Assembly Exercism

लसाग्ना शुरू करने के लिए तैयार हैं?

Exercism पर साइन अप कीजिए और x86-64 Assembly को 22 कॉन्सेप्ट130 अभ्यास तथा असली इंसानों से मिलने वाली मेंटरिंग के साथ सीखिए और उसमें महारत हासिल कीजिए, वह भी बिल्कुल मुफ्त।