रहस्य

रहस्य

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

परिचय

बिट मैनिपुलेशन

पूर्णांक के हर बिट का उपयोग एक बाइनरी वैल्यू संग्रहीत करने के लिए किया जा सकता है। चूँकि बहुत सी स्थितियों में बाइनरी जानकारी होती है, जैसे सही या गलत, शामिल होना या न होना, चालू या बंद, इसलिए N-बिट पूर्णांक का बाइनरी निरूपण N चीज़ों की बाइनरी स्थिति को एनकोड करने का एक संक्षिप्त तरीका देता है। इसलिए असेंबली में बिट और बाइट को मैनिपुलेट करने की क्षमता आवश्यक हो जाती है। x86-64 निर्देश सेट बिटवाइज़ मैनिपुलेशन के लिए कई तरह के निर्देश देता है।

एकल बिट मैनिपुलेशन

ये निर्देश ऑपरेंड के एकल बिट पर काम करते हैं।

इन सभी को दो ऑपरेंड चाहिए, जिनमें दूसरा यह इंडेक्स बताता है कि पहले ऑपरेंड में किस बिट पर काम किया जा रहा है। ये सभी चुने गए बिट को कैरी फ्लैग (CF) में कॉपी करते हैं।

नाम विवरण
bt बिट को CF में कॉपी करता है, बिना किसी ऑपरेंड को बदले
bts बिट को CF में कॉपी करता है और उसे डेस्टिनेशन ऑपरेंड में सेट करता है
btr बिट को CF में कॉपी करता है और उसे डेस्टिनेशन ऑपरेंड में क्लियर करता है
btc बिट को CF में कॉपी करता है और उसे डेस्टिनेशन ऑपरेंड में कॉम्प्लिमेंट (फ्लिप) करता है

बिटवाइज़ ऑपरेशन

बिटवाइज़ ऑपरेशन ऑपरेंड के सभी बिट पर किए जाते हैं।

हर निष्पादित बिटवाइज़ ऑपरेशन के लिए उसी नाम का एक निर्देश होता है:

नाम विवरण
and 1, अगर दोनों बिट 1 हैं
or 1, अगर कम से कम एक बिट 1 है
xor 1, अगर बिट अलग-अलग हैं
not 1, अगर बिट 0 था; 0, अगर बिट 1 था

इनमें से अधिकांश दो ऑपरेंड लेते हैं, दोनों पर बिटवाइज़ ऑपरेशन करते हैं और परिणाम को डेस्टिनेशन ऑपरेंड में संग्रहीत करते हैं। अपवाद not है, जो केवल एक डेस्टिनेशन ऑपरेंड लेता है।

मास्क

जब हम एक और शून्य को क्रमशः शामिल होना और न होना मानते हैं, तो पूर्णांक को बिटमास्क (या सिर्फ मास्क) कहा जाता है।

एक बिटमास्क चीज़ों को "मास्क आउट" करता है, क्योंकि i-वें बिट में शून्य होने का मतलब है कि i-वाँ आइटम शामिल नहीं है, जबकि एक होने का मतलब है कि वह शामिल है। हम आम तौर पर बिटमास्क का उपयोग पूर्णांक के कुछ बिट को शामिल करने और बाकी को बाहर रखने के लिए भी करते हैं।

उदाहरण के लिए, मान लीजिए A एक ऐसा पूर्णांक है जिसका बाइनरी निरूपण यह है:

इंडेक्स 7 6 5 4 3 2 1 0
बिट 1 0 0 1 0 1 0 1

साथ ही, मान लीजिए M एक ऐसा पूर्णांक है जिसका बाइनरी निरूपण यह है:

इंडेक्स 7 6 5 4 3 2 1 0
बिट 0 0 0 0 1 1 0 1

दोनों 8-बिट पूर्णांक हैं। इस स्थिति में, हम कह सकते हैं कि M A के बिट 0, 2 और 3 चुनता है, और बाकी को बाहर रखता है।

पहले बताए गए बिटवाइज़ निर्देश मास्क के साथ पूर्णांक को मैनिपुलेट करने में उपयोगी होते हैं। उदाहरण के लिए:

  • A के जो बिट M ने नहीं चुने हैं, उन्हें क्लियर करने के लिए बिटवाइज़ AND लीजिए: A AND M।
  • M द्वारा चुने गए A के बिट को सेट करने के लिए बिटवाइज़ OR लीजिए: A OR M।

TEST निर्देश

test निर्देश दोनों ऑपरेंड के बीच बिटवाइज़ AND करता है और परिणाम के अनुसार फ्लैग सेट करता है।

अगर A पहला ऑपरेंड है और B दूसरा, तो:

फ्लैग यह होने पर सेट होता है
CF हमेशा क्लियर रहता है
ZF A AND B == 0
SF A AND B का साइन बिट सेट है
OF हमेशा क्लियर रहता है

यह निर्देश दो ऑपरेंड लेता है और फ्लैग अपडेट करता है, लेकिन अपने ऑपरेंड को नहीं बदलता।

शिफ्ट ऑपरेशन

ये निर्देश डेस्टिनेशन ऑपरेंड के बिट को उतने स्थान तक खिसकाते हैं जितने दूसरा ऑपरेंड बताता है। दूसरा ऑपरेंड एक स्थिर संख्या (एक immediate) या रजिस्टर cl होना चाहिए (rcx के सबसे निचले 8 बिट)।

नाम विवरण
shl/sal बिट को बाएँ शिफ्ट करता है
shr/sar बिट को दाएँ शिफ्ट करता है

ध्यान दीजिए कि दूसरे ऑपरेंड की गिनती को 5 बिट तक मास्क कर दिया जाता है, और 64-बिट डेस्टिनेशन ऑपरेंड के साथ 6 बिट तक। इसके बाद का कोई भी बिट व्यावहारिक रूप से अनदेखा कर दिया जाता है। इसका मतलब है कि अधिकतम शिफ्ट 31 है, और 64-बिट ऑपरेंड के साथ 63।

Shl / Sal

shl और sal दोनों बिल्कुल एक ही काम करते हैं, एक दूसरे का एलियस है।

जब भी बाएँ शिफ्ट किया जाता है, तो सीक्वेंस के अंत की ओर के उतने बिट, जितनी शिफ्ट की लंबाई है, पहले CF में ले जाए जाते हैं और फिर हटा दिए जाते हैं। दूसरी ओर, शुरुआत में शिफ्ट की लंबाई के बराबर संख्या में नए क्लियर बिट जोड़ दिए जाते हैं।

चूँकि पूर्णांक का हर बिट 2 की एक घात दर्शाता है, n स्थान बाएँ शिफ्ट करने का असर पूर्णांक को 2ⁿ से गुणा करने जैसा होता है।

Shr / Sar

बिट को दाएँ खिसकाने के लिए दो निर्देश हैं: shr और sar।

जब भी इन दोनों में से कोई निर्देश इस्तेमाल होता है, तो सीक्वेंस के शुरू की ओर के उतने बिट, जितनी शिफ्ट की लंबाई है, पहले CF में ले जाए जाते हैं और फिर हटा दिए जाते हैं। दूसरी ओर, अंत में शिफ्ट की लंबाई के बराबर संख्या में नए बिट जोड़ दिए जाते हैं।

इनके बीच का अंतर यह है कि shr बाएँ सिरे पर 0 बिट डालता है, जबकि sar तब 1 डालता है जब सबसे महत्वपूर्ण बिट सेट हो, और वरना 0 डालता है। इसका मतलब है कि sar साइन्ड पूर्णांक की शिफ्ट में चिह्न बनाए रखता है।

चूँकि पूर्णांक का हर बिट 2 की एक घात दर्शाता है, shr का उपयोग करके n स्थान दाएँ शिफ्ट करने का असर 2ⁿ से अनसाइन्ड भाग देने जैसा होता है।

इसी तरह, sar का उपयोग करके n स्थान दाएँ शिफ्ट करने का असर 2ⁿ से साइन्ड भाग देने जैसा होता है।

रोटेशन ऑपरेशन

ये निर्देश डेस्टिनेशन ऑपरेंड के बिट को उतने स्थान तक घुमाते हैं जितने दूसरा ऑपरेंड बताता है। दूसरा ऑपरेंड एक स्थिर संख्या (एक immediate) या रजिस्टर cl होना चाहिए (rcx के सबसे निचले 8 बिट)।

रोटेशन और शिफ्ट में अंतर यह है कि रोटेशन कोई बिट न तो हटाता है और न ही जोड़ता है। जो बिट शिफ्ट में हटा दिए जाते, वे रोटेशन में इसके बजाय दूसरे सिरे पर पहुँचा दिए जाते हैं। इस तरह सारे बिट बने रहते हैं, बस उनकी जगहें बदल जाती हैं।

नाम विवरण
rol बिट को बाएँ घुमाता है
ror बिट को दाएँ घुमाता है

ध्यान दीजिए कि दूसरे ऑपरेंड की गिनती को 5 बिट तक मास्क कर दिया जाता है, और 64-बिट डेस्टिनेशन ऑपरेंड के साथ 6 बिट तक। इसके बाद का कोई भी बिट व्यावहारिक रूप से अनदेखा कर दिया जाता है। इसका मतलब है कि अधिकतम रोटेशन 31 है, और 64-बिट ऑपरेंड के साथ 63।

अन्य बिट मैनिपुलेशन निर्देश

बिट मैनिपुलेशन के कुछ और उपयोगी निर्देश भी हैं:

नाम विवरण
popcnt सेट बिट की संख्या गिनता है
bsr सबसे महत्वपूर्ण सेट बिट का इंडेक्स देता है। अगर कोई बिट सेट नहीं है, तो परिणाम अपरिभाषित होता है
bsf सबसे कम महत्वपूर्ण सेट बिट का इंडेक्स देता है। अगर कोई बिट सेट नहीं है, तो परिणाम अपरिभाषित होता है

ये सभी निर्देश दो 16-बिट, 32-बिट या 64-बिट ऑपरेंड के साथ काम करते हैं। इन्हें 8-बिट ऑपरेंड के साथ इस्तेमाल नहीं किया जा सकता।

निर्देश

आपके एक दोस्त ने अभी-अभी आपको एक ज़रूरी राज़ वाला संदेश भेजा है। दूसरों के लिए उसे पढ़ना आसान न हो, इसलिए संदेश को बिट्स पर कई तरह के ऑपरेशन करके एन्क्रिप्ट किया गया। संदेश को डिक्रिप्ट करने में मदद के लिए आपको मेथड लिखने होंगे।

Note

इस कॉन्सेप्ट में बताए गए एक बिट पर काम करने वाले निर्देश ये हैं:

नाम विवरण
bt किसी भी ऑपरेंड को बदले बिना बिट को CF में कॉपी करता है
bts बिट को CF में कॉपी करता है और उसे डेस्टिनेशन ऑपरेंड में सेट करता है
btr बिट को CF में कॉपी करता है और डेस्टिनेशन ऑपरेंड में उसे क्लियर करता है
btc बिट को CF में कॉपी करता है और डेस्टिनेशन ऑपरेंड में उसे कॉम्प्लिमेंट (उलट) करता है

इस कॉन्सेप्ट में बताए गए बिटवाइज़ निर्देश ये हैं:

नाम विवरण
and 1 अगर दोनों बिट 1 हैं
or 1 अगर कम से कम एक बिट 1 है
xor 1 अगर बिट अलग हैं
not 1 अगर बिट 0 था; 0 अगर बिट 1 था

इस कॉन्सेप्ट में बताए गए शिफ्ट निर्देश ये हैं:

नाम विवरण
shl/sal बिट्स को बाईं ओर शिफ्ट करता है
shr/sar बिट्स को दाईं ओर शिफ्ट करता है

इस कॉन्सेप्ट में बताए गए रोटेशन निर्देश ये हैं:

नाम विवरण
rol बिट्स को बाईं ओर घुमाता है
ror बिट्स को दाईं ओर घुमाता है

इस कॉन्सेप्ट में बताए गए विविध निर्देश ये हैं:

नाम विवरण
popcnt सेट बिट्स की संख्या गिनता है
bsr सबसे महत्वपूर्ण सेट बिट का इंडेक्स देता है। अगर कोई बिट सेट नहीं है, तो परिणाम अपरिभाषित होता है
bsf सबसे कम महत्वपूर्ण सेट बिट का इंडेक्स देता है। अगर कोई बिट सेट नहीं है, तो परिणाम अपरिभाषित होता है

1. मास्क निकालिए

संदेश एक 16-बिट पूर्णांक में एन्कोड किया गया है। लेकिन उसमें से ऊपर के 8 बिट असल में संदेश का हिस्सा नहीं हैं, बल्कि एक मास्क हैं जिसका इस्तेमाल डिक्रिप्शन में करना है।

extract_higher_bits फंक्शन बनाइए जो एक 16-बिट पूर्णांक लेता है और उसके ऊपर के 8 बिट लौटाता है।

extract_higher_bits(0b1010010011000101)
// => 0b10100100

2. संदेश निकालिए

सिर्फ मास्क निकाल पाना काफी नहीं है, आपको संदेश को भी अलग करना होगा।

extract_lower_bits फंक्शन बनाइए जो एक 16-बिट पूर्णांक लेता है और उसके नीचे के 8 बिट लौटाता है।

extract_lower_bits(0b1010010011000101);
// => 0b11000101

3. रिडंडेंट बिट निकालिए

कुछ बिट संदेश और मास्क दोनों में सेट होते हैं। यह बहुत ज़रूरी जानकारी है जिसका इस्तेमाल आगे किया जाएगा।

extract_redundant_bits फंक्शन बनाइए जो संदेश और मास्क दोनों को एन्कोड करने वाला 16-बिट पूर्णांक लेता है और एक ऐसा 8-बिट पूर्णांक लौटाता है जिसमें सिर्फ रिडंडेंट बिट सेट हों। लौटाई गई संख्या में कोई बिट तब 1 होना चाहिए जब वह संदेश और मास्क दोनों में 1 हो। बाकी सारे बिट क्लियर होने चाहिए।

extract_redundant_bits(0b1010010011000101);
// => 0b10000100

4. संदेश के सारे बिट सेट कीजिए

इसके बाद, मास्क के अनुसार संदेश में कुछ बिट को 1 पर सेट करना है।

set_message_bits फंक्शन बनाइए जो संदेश और मास्क दोनों को एन्कोड करने वाला 16-बिट पूर्णांक लेता है और संदेश के बिट को 1 पर सेट करने का परिणाम लौटाता है। संदेश का कोई बिट तब 1 होना चाहिए जब मास्क में वह बिट 1 हो। बाकी सारे बिट अपरिवर्तित रखे जाने चाहिए, यानी अगर वे पहले से सेट थे तो सेट रहें, और अगर पहले से क्लियर थे तो क्लियर रहें।

set_message_bits(0b1010010011000101);
// => 0b11100101

5. प्राइवेट की घुमाइए

पहेली का एक टुकड़ा ऐसा है जो संदेश में साफ़ तौर पर मौजूद नहीं है: 16-बिट संख्या 0b1011001100111100। यह संख्या आपकी साझा प्राइवेट की है और संदेश को डिक्रिप्ट करने में आपको इसका इस्तेमाल करना है।

ऐसा करने के लिए, सबसे पहले आपको अपनी प्राइवेट की के बिट को कुछ पोज़िशन बाईं ओर घुमाना होगा। पोज़िशन की संख्या उतनी ही है जितने रिडंडेंट बिट संदेश और मास्क दोनों में सेट हैं।

rotate_private_key फंक्शन बनाइए जो संदेश और मास्क दोनों को एन्कोड करने वाला 16-बिट पूर्णांक लेता है और आपकी प्राइवेट की घुमाने का परिणाम लौटाता है। यह परिणाम एक 16-बिट पूर्णांक होता है।

rotate_private_key(0b1010010011000101);
// => 0b1100110011110010
Note

NASM (The Netwide Assembler, यानी इस ट्रैक में इस्तेमाल होने वाला असेंबलर) में 0b लगाकर बाइनरी फॉर्मैट में कॉन्स्टेंट लिखने की सुविधा है। यह पढ़ने में आसानी के लिए कॉन्स्टेंट में अंडरस्कोर (_) को सेपरेटर की तरह इस्तेमाल करने की सुविधा भी देता है:

PRIVATE_KEY equ 0b1011_0011_0011_1100

6. प्राइवेट की फॉर्मैट कीजिए

डिक्रिप्शन में इस्तेमाल होने के लिए आपकी प्राइवेट की को फॉर्मैट करना ज़रूरी है ताकि उसके काम के बिट अलग किए जा सकें।

किसी प्राइवेट की को पूरी तरह फॉर्मैट करने के लिए आपको यह करना होगा:

  • उसे घुमाना है।
  • घुमाई गई प्राइवेट की के नीचे के 8 बिट वाले हिस्से को अलग करना है, जो बेस वैल्यू है।
  • घुमाई गई प्राइवेट की के ऊपर के 8 बिट वाले हिस्से को अलग करना है, जो बेस वैल्यू पर लगाया जाने वाला मास्क है।
  • बेस वैल्यू में उन बिट को पलटना है जो मास्क में भी सेट हैं।
  • परिणाम के सारे बिट पलटने हैं।

पलटा हुआ बिट 1 होता है अगर वह 0 था, और 0 होता है अगर वह 1 था।

format_private_key फंक्शन बनाइए जो संदेश और मास्क दोनों को एन्कोड करने वाला 16-बिट पूर्णांक लेता है और पूरी तरह फॉर्मैट की गई 8-बिट प्राइवेट की लौटाता है।

format_private_key(0b1010010011000101);
// => 0b11000001

7. डिक्रिप्शन पूरा कीजिए

जब आपके पास सारे काम के बिट सेट किया हुआ संदेश और फॉर्मैट की गई प्राइवेट की आ जाए, तो उन्हें जोड़कर अंतिम संदेश निकालने का समय आ जाता है।

अंतिम संदेश एक 16-बिट पूर्णांक होता है, जिसमें:

  • ऊपर के 8 बिट में फॉर्मैट की गई प्राइवेट की भरी जाती है।
  • नीचे के 8 बिट में सारे काम के बिट सेट करने के बाद वाला संदेश भरा जाता है।

decrypt_message फंक्शन बनाइए जो संदेश और मास्क दोनों को एन्कोड करने वाला 16-बिट पूर्णांक लेता है और पूरी तरह डिक्रिप्ट किए हुए संदेश वाला 16-बिट पूर्णांक लौटाता है।

इस फंक्शन में आपको format_private_key से बनाई गई फॉर्मैट की हुई प्राइवेट की इस्तेमाल करनी चाहिए, और set_message_bits से सारे काम के बिट सेट किया हुआ संदेश भी।

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

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

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