एक खास रजिस्टर होता है जिसे rflags कहते हैं।
इसके बिट अलग-अलग शर्तों के लिए फ्लैग की तरह काम करते हैं।
इनमें से कुछ नीचे दिए गए हैं:
| नाम | प्रतीक | बिट |
|---|---|---|
| कैरी | CF | 0 |
| ज़ीरो | ZF | 6 |
| साइन | SF | 7 |
| ओवरफ्लो | OF | 11 |
पूरी सूची के लिए Intel की मैनुअल देखिए।
rflags के फ्लैग सीधे नहीं बदले जाते।
इसके बजाय, इन्हें कई अलग-अलग निर्देश सेट करते हैं।
जैसे, जब किसी अंकगणितीय या बिटवाइज़ ऑपरेशन का परिणाम शून्य होता है, तो ZF सेट हो जाता है।
शर्तें जाँचने के लिए सबसे ज़्यादा इस्तेमाल होने वाले निर्देशों में से एक है cmp।
यह दो ऑपरेंड लेता है और फ्लैग अपडेट करता है, लेकिन अपने ऑपरेंड नहीं बदलता।
cmp निर्देश पहले ऑपरेंड से दूसरे ऑपरेंड को घटाता है और परिणाम के अनुसार फ्लैग सेट करता है।
अगर A पहला ऑपरेंड है और B दूसरा, तो:
| फ्लैग | कब सेट होता है |
|---|---|
| CF | A < B (बिना साइन) |
| ZF | A == B |
| SF | A < B (साइन सहित, ओवरफ्लो के बिना) |
| OF | साइन सहित घटाव में ओवरफ्लो |
डिफ़ॉल्ट रूप से x86-64 में कोड ऊपर से नीचे के क्रम में चलता है।
लेकिन कई मौके ऐसे होते हैं जब इस व्यवहार को बदलना ज़रूरी होता है। जैसे, किसी शर्त के जवाब में निर्देशों का कोई और समूह चलाना।
ऊँचे स्तर की भाषाओं में यह काम आम तौर पर if...else जैसी अमूर्तनों से होता है।
लेकिन x86-64 में ये मौजूद नहीं हैं।
इसके बजाय, x86-64 ऐसे निर्देश देता है जो असल में निष्पादन को कोड के किसी दूसरी जगह पर भेज देते हैं।
इसे branching कहते हैं।
हम ऐसे दो निर्देश पहले ही देख चुके हैं: call और ret।
जब किसी फंक्शन को कॉल किया जाता है, तो निष्पादन कॉल करने वाले से कॉल किए गए फंक्शन को चला जाता है। और लौटते समय निष्पादन वापस कॉल करने वाले के पास आ जाता है।
अगर कोई ret नहीं मिलता, तो निष्पादन एक फंक्शन से फिसलकर अगले फंक्शन में चला जाता है।
इसका इस्तेमाल कभी-कभी कोड के प्रवाह को बेहतर बनाने के लिए किया जा सकता है।
jmp निर्देश प्रोग्राम के निष्पादन को बिना किसी शर्त के कोड के किसी दूसरे बिंदु पर भेज देता है। इसका एकमात्र ऑपरेंड एक लेबल होता है, जिसमें उस बिंदु का पता होता है जहाँ से निष्पादन आगे चलेगा।
उदाहरण के लिए, इस फंक्शन को देखिए:
fn:
mov rax, 5
jmp end
add rax, 10
end:
ret
जब fn को कॉल किया जाता है, तो निष्पादन mov rax, 5 से शुरू होता है।
इससे उस समय rax की वैल्यू 5 हो जाती है।
अगला निर्देश jmp end है, जो निष्पादन को end लेबल पर भेज देता है।
end के बाद अगला निर्देश ret है, जो निष्पादन को वापस कॉल करने वाले फंक्शन के पास भेज देता है।
ध्यान दीजिए कि चूँकि add rax, 10 jmp end के बाद और end से पहले है, यह कभी नहीं चलता।
जब fn लौटता है, तब rax की वैल्यू 5 होती है।
jcc निर्देशों का परिवार प्रोग्राम के निष्पादन को किसी दूसरे बिंदु पर तभी भेजता है जब कोई खास शर्त पूरी होती है। वरना, निष्पादन क्रम से आगे चलता रहता है।
हर शर्त rflags के एक या अधिक फ्लैग से जुड़ी होती है।
jcc के कुछ रूप यह जाँचते हैं कि कोई फ्लैग सेट है, और कुछ यह कि वह सेट नहीं है।
jcc में cc असली अक्षर नहीं है, बल्कि उस खास सफिक्स की ओर इशारा करता है जो जाँचे जाने वाले फ्लैग से जुड़ा होता है।
सफिक्स कई हैं, और इनमें से कई वही शर्त जाँचते हैं जो कोई दूसरा जाँचता है। इनमें से कुछ सीधे किसी फ्लैग की ओर इशारा करते हैं, यानी अगर वह खास फ्लैग सेट है तो निर्देश किसी लेबल पर जंप करता है:
| सफिक्स | कब जंप करता है |
|---|---|
| z | ZF == 1 |
| c | CF == 1 |
| s | SF == 1 |
| o | OF == 1 |
कई और सफिक्स इसलिए चुने गए हैं ताकि वे cmp निर्देश में अपने अर्थ की ओर इशारा करें।
जैसे:
| निर्देश | सफिक्स | कब जंप करता है |
|---|---|---|
| cmp A, B | e | A == B |
| cmp A, B | l | A < B (साइन सहित) |
| cmp A, B | b | A < B (बिना साइन) |
| cmp A, B | g | A > B (साइन सहित) |
| cmp A, B | a | A > B (बिना साइन) |
l, b, g या a के बाद e जोड़कर शर्त में बराबरी को भी शामिल किया जा सकता है:
cmp rcx, r8
jge two ; this jumps to 'two' if rcx is greater than, or equal to, r8 in a signed comparison
jbe two ; this jumps to 'two' if rcx is lesser than, or equal to, r8 in an unsigned comparison
हर सफिक्स के लिए ऐसे रूप भी हैं जो उलटा व्यवहार जाँचते हैं।
इनका सिंटैक्स वही होता है, बस सफिक्स से पहले एक n लगा होता है।
जैसे, jnz तब जंप करता है जब ZF सेट नहीं होता।
इसी तरह, jnae तब जंप करता है जब A, B से नहीं >= होता (A और B को बिना साइन पूर्णांक मानने पर)।
कुछ सफिक्स एक ही शर्त के दूसरे नाम हैं।
जैसे, jz (सफिक्स z, ZF के लिए) और je (सफिक्स e, बराबरी के लिए) दोनों तब जंप करते हैं जब ZF सेट होता है।
इसकी वजह यह है कि cmp में जब घटाव का परिणाम शून्य होता है तब ZF सेट होता है, और इसका मतलब है कि दोनों ऑपरेंड बराबर हैं।
लेकिन कुछ दूसरे सफिक्स फ्लैग के किसी संयोजन को जाँचते हैं और उनकी जगह सीधे किसी एक फ्लैग वाला सफिक्स नहीं लगाया जा सकता।
उसी सफिक्स को चुनिए जो आपकी तुलना का अर्थ सबसे अच्छी तरह बताता है।
लेबल पूरी सोर्स फाइल में दिखते हैं, वे किसी फंक्शन तक सीमित नहीं होते। इसलिए एक ही लेबल नाम दोबारा इस्तेमाल नहीं किया जा सकता।
लोकल लेबल जैसा व्यवहार पाने के लिए NASM में ऐसे लेबल लिखने का खास तरीका है जिसके आगे बिंदु (.) लगाया जाता है।
इस तरीके से बना लेबल अपने आप में उस पिछले लेबल का नाम भी शामिल कर लेता है जिसके आगे बिंदु नहीं लगा था:
section .text
fn1:
...
.example: ; this is fn1.example
...
ret
fn2:
...
.example: ; this is fn2.example
...
ret
फिर भी कोड में कहीं से भी पूरा लेबल नाम लिखकर इस लेबल पर जंप किया जा सकता है, जैसे jmp fn1.example।
लेकिन अगर जंप में लेबल का वह हिस्सा इस्तेमाल हो जो बिंदु से शुरू होता है, तो जंप ऊपर वाले फंक्शन के अंदर के लेबल पर होगा।
जैसे, .example ऐसे व्यवहार करता है जैसे वह उस फंक्शन का लोकल लेबल हो:
section .text
fn1:
...
.example:
...
jmp .example ; this jumps to fn1.example
fn2:
...
.example:
...
jmp .example ; this jumps to fn2.example
ध्यान दीजिए कि किसी फंक्शन के अंदर बिना बिंदु वाला लेबल असल में एक और फंक्शन बना देता है:
section .text
fn1:
...
.example: ; this is 'fn1.example'
...
non_dotted:
...
.example: ; this is 'non_dotted.example'
...
ret