زبان ماشین و اسمبلی جلسه پنجم
فصل پنجم
انشعاب و حلقه
پرشهاي غير شرطي
Jmp statement_label
که در آن statemaet_label متناظر با فيلد اسم دستور اسمبلي ديگري ميباشد.
دستور JMP شبيه به goto در پاسکال يا بيسيک است.
اگر شرايط مختلفي وجود داشته باشد که تحت آن شرايط برنامه بايد خاتمه پيدا کند، ميتوان به يک دستور اجرايي که به وسيله برچسب quit: مشخص شده است و محل اختتام برنامه ميباشد، پرش نمود.
فرمهاي درون سگمنتي وقتي توليد ميشوند که مقصد در درون سگمنت جاري قرار داشته باشد، اين فرمها در بين انواع ديگر از همه رايجتر ميباشند.
پرشهاي بين سگمنتي که به عبارتي به يک سگمنت کد ديگر پرش ميکنند، به ندرت مورد نياز ميباشند.
پرشهاي غيرمستقيم مقدار جابجايي خود از دستورالعمل مقصد را، از يک ثبات يا از يک کلمه در حافظه به دست ميآورند.
پرشهاي درون سگمنتي نسبي، از انواع ديگر معمولتر بوده و بيشتر مورد استفاده قرار ميگيرند.
هرکدام از اين دستورالعملهاي jump شامل يک جابجايي از مقصد نسبت به خود دستور ميباشند. اين مقدار جابجايي به آفست دستوربعدي اضافه ميشود تا آفست مقصد به دست ميآيد.
در زماني که پرش به قبل از دستور jmp باشد، MASM ميتواند تعيين کند که مقصد کجاست و در صورت امکان از يک مقدار جابجايي کوتاه استفاده ميکند.
زماني که پرش به بعد از دستور JMP باشد، ماکرواسمبلر مقدار جابجايي دستور مقصد را نميداند و بايد تصميم بگيرد که چه مقدار فضا براي JMP قرار دهد، بنابراين فضاي جابجايي 16 بيتي را براي آن در نظر ميگيرد.
اگر جابجايي، فقط به يک بايت نياز داشته باشد، به جاي بايت اضافي، دستورالعمل NOP را قرار ميدهد. اين دستور هيچ کاري انجام نميدهد و فقط يک بايت را اشغال ميکند.
زماني که بخواهيم به دستوري بعد از دستور JMP پرش کنيم و بدانيم که اين دستور داراي فاصله زيادي نميباشد ميتوانيم به وسيله عملوند SHORT به ماکرواسمبلر بگوييم که از مقدار جابجايي کوتاه استفاده کند. بدين ترتيب يک بايت در کد هدف صرفهجويي ميشود.
پرشهاي شرطي، دستورات مقايسه و ساختارهاي If
شکل کلي آنها به اين صورت است:
j--- target_statement
در آن آخرين قسمت دستور تعيينکننده وضعيتي است که تحت آن، پرش اجرا ميشود.
اگر شرط تحقق يابد، پرش صورت خوهد گرفت، در غيراينصورت دستورالعمل بعدي اجرا خواهد شد.
دستورالعملهاي پرش شرطي در وضعيت فلگها، تغييري ايجاد نميکنند و فقط نسبت به وضعيت آنها عکسالعمل نشان مي دهند.
JZ end_while
اين دستور به اين معني است که اگر فلک ZF برابر با يک است به دستورالعملي با برچسب end_while پرش کن و در غير اين صورت به دستور بعدي برو.
چندين برچسب ميتوانند دستورالعملهاي بعد از ساختار if را آدرسدهي نمايند. از آنجايي که برچسبها جزء کدهدف برنامه نيستند، برچسبهاي اضافي به طول کد هدف يا زمان اجراي برنامه چيزي را اضافه نميکنند.
براي پيادهسازي يک طرح غالباً از برچسبهايي مثل if ، then و else و endif استفاده ميشود.
هيچ کلمه رزرو شدهاي حاوي کاراکتر خط زيرين (underscore) نيست.
دستورCMP:
CMP operand_1 , operand-2
اين دستور عملوند operan_2 را از عملوند operand_1 تفريق ميکند، درست همانند دستورالعمل sub. مقدار تفاضل و چيزهايي که در اين عمل تفريق اتفاق ميافتند وضعيت فلگها را تعيين ميکنند.
تفاوت آن با sub اين است که بر خلاف sub عملوند operand_1 را تغيير نميدهد.
اين دستور دو عملوند را با يکديگر مقايسه ميکند و سپس فلگهاي AF,CF,OF,PF و ZF را برابر يک يا صفر مينمايد.
تنها وظيفه دستور CMP آن است که مقدار فلگها را تعيين کند. اين کار وظيفه اوليه اين دستورات است نه وظيفه جانبي.
- فلگ رقم نقلي يعني CF زماني برابر يک ميشود که در تفريق يک رقم عاريه وجود داشته باشد، چنانچه عاريه وجود نداشته باشد اين فلگ برابر صفر خواهد بود.
- فلگ سرريزي يا OF در صورتي يک خواهد بود که سرريزي وجود داشته باشد و در غير اينصورت برابر صفر خواهد بود.
- فلگ علامت يا SF زماني يک خواهد بود که حاصل تفريق نشان دهنده يک عدد منفي مکمل دو باشد و زماني برابر صفر خواهد بود که حاصل تفريق برابر صفر يا مثبت باشد.
- فلگ صفر يا ZF زماني يک خواهد بود که حاصل تفريق صفر باشد و در غير اينصورت برابر صفر خواهد بود.
راه تشخيص کوچکتر بودن يک عملوند از عملوند ديگر آن است که فلگ علامت سرريزي را مقايسه کنيم؛ در زماني که operand_1 کوچکتر از operan_2 باشد، اين فلگها متفاوت با يکديگر هستند و زماني که operand_1 بزرگتر يا مساوي operand_2 باشد، اين فلگها وضعيت مشابهي خواهند داشت.
در بسياري از اين پرشها رابطه بين دو عملوند دستورالعمل cmp موردنظر ميباشد و نه وضعيت فلگها.
وقتي اولين عملوند در حافظه باشد، دستورات cmp به پالسهاي ساعت کمتري نسبت به دستورالعملهاي sub مربوطه نياز دارند، زيرا نيازي به ذخيره کردن نتيجه نخواهد بود.
همه دستورات زير مجاز هستند:
Cmp ax,356
Cmp pattern,0D3a6h
cmp bh,'$'
توجه داشته باشيد که يک عملوند بلاواسطه بايد دومين عملوند باشد؛ به اين دليل، دستورالعمل زير مجاز نيست
Cmp 100,total ;illegal
براي تعيين نامساويها دوسري نام وجود دارد و اينها کدهاي ماشين متفاوتي را توليد ميکنند. يکسري از آنها بيشتر به فلگ رقم نقلي توجه دارند و بنابراين متناسب کار با اعداد بيعلامت ميباشند. مجموعه ديگر پرشهاي شرطي براي تعيين ترتيب به فلگ علامت و فلگ سرريزي توجه دارند.
هيچ شکل جايگزين ديگري براي دستورالعملهاي پرش شرطي جهت پرشهاي طولانيتر وجود ندارد. گاهي اوقات پرشهاي شرطي بايد با پرشهاي غيرشرطي ترکيب شوند تا به هدفهايي که در فواصل طولانيتر قرار دارند برسيم.
پيادهسازي حلقههاي while، until و for
While: ;code to check Boolean expression
.
.
Body: ;loop body
.
.
Jpm While ;go check condition again
End_While
شرط ادامه که يک عبارت بولي است در ابتدا بررسي ميگردد، چنانچه صحت داشته باشد، آنگاه بدنه حلقه اجرا ميشود و دوباره شرط ادامه بررسي ميشود. هر زماني که عبارت بولي صحت نداشته باشد، اجرا با دستورالعمل بعد از end_While ادامه پيدا ميکند.
البته اگر بدنه حلقه بيش از 127 بايت طول داشته باشد هيچکدام از اين عبارتها صحيح نخواهد بود زيرا پرش نسبي خارج از محدوده مجاز خواهد بود.
غالباً شرط ادامه در يک حلقه While ساده نبوده و داراي دو قسمت است که به وسيله عملگرهاي بولي and يا or با يکديگر ترکيب ميشوند. در عملگر and هر دو عملوند بايد صحت داشته باشند تا کل شرط صحت داشته باشد و در عملگر or فقط زماني نتيجه عملگر or صحت ندارد که هر دو عملوند آن صحت نداشته باشد.
گاهي اوقات پردازش يک حلقه تا زماني که با مقادير نرمال برخورد شود، بايد ادامه پيدا کند و وقتي با مقدار خاصي برخورد ميشود، متوقف ميگردد.
يک مزيت زبان اسمبلي آن است که قابليت انعطاف بيشتري در برنامهنويسي وجود دارد.
طرح زير ميتواند براي گرفتن دادهها از صفحه کليد به کار رود:
value)واردشده از صفحه کليد برابر مقدار موردنظر نيست)While
loop
. . . [بدنه حلقه]
End While;
در ماکروي atoi چنانچه کاراکتر اسکي به عدد منفي تبديل شود فلگ علامت (SF) يک ميشود و در غير اينصورت صفر ميگردد.
بدنه حلقه for، حلقهاي که به وسيله يک شمارنده کنترل ميشود، براي هر مقدار شاخص حلقه در يک محدوده معين يکبار تکرار ميشود.
براي حلقهاي for در زبان اسمبلي، شاخصهاي معمولاً اعدا صحيح هستند.
براي مواردي مطلوبست که تعداد دفعات تکرار از قبل معلوم باشد.
For shakhes = مقدار ابتدایی To مقدار انتهایی
{بدنه حلقه}. . .
End for:
بک حلقه for به راحتي ميتواند به ساختار while ترجمه گردد.
يک حلقه until ميتواند به صورت زير بيان شود:
loop شرط خاتمه until
. . . {بدنه حلقه}
End until;
بدنه حلقه حداقل يکبار اجرا شده و سپس شرط خاتمه بررسي ميشود. اگر شرط خاتمه برقرار نباشد بدنه حلقه دوباره اجرا ميشود و اگر برقرار باشد، اجرا برنامه از دستورات بعد از end until ادامه پيدا ميکند.
ساختارهاي حلقهاي ديگر نيز ميتوانند به زبان اسمبلي پيادهسازي گردند. حلقه forever اغلب مفيد است و در هر جايي که به کار رود، هميشه داراي يک جمله خروج براي انتقال کنترل به انتهاي حلقه ميباشد، اين جمله اغلب شرطي است مثل يک دستورالعمل if .
Forever loop
.
.
if (respons='s') or (response = 's')
then
exit loop
endif;
.
end loop;
حلقههاي for در زبان اسمبلي:
دستورالعمل loop داراي شکل زير ميباشد:
در اين دستور statement_label، برچسب دستورالعملي است که داراي يک جابجايي کوتاه نسبت به دستور loop است.
دستور loop باعث ميشود که اعمال زير صورت پذيرند:
مقدار ثبات CX کاهش پيدا ميکند.
اگر مقدار ثبات CX صفر باشد، آنگاه اجرا از اولين دستور بعد از دستورالعمل loop ادامه پيدا ميکند.
اگر مقدار ثبات CX صفر نباشد، آنگاه پرش به آدرسي که statement_label مشخص ميکند، صورت ميگيرد.
دستورالعمل loop به دو بايت کد هدف نياز دارد، بايت اول، بايت کد عمل است و دومين بايت مقدار جابجايي به دستورالعمل مقصد ميباشد.
دستور Loop وضعيت هيچ فلگي را تغيير نخواهد داد.
دراين دستورالعملها هيچ ثبات ديگري نميتواند به جاي CX به کار برود. در عمل اين به آن معني است که وقتي دستورالعمل LOOP به کار ميرود، ثبات CX نميتواند به منظورهاي ديگر به کار برده شود.
دستورالعمل پرش شرطي JCXZ به اين منظور به کار ميرود که اگر محتواي ثبات CX صفر باشد به مقصدي که براي آن تعيين ميکند پرش کند.
اين دستور همانند دستورالعملهاي پرشي ديگر به دو بايت کد هدف نياز دارد و بر روي فلگها هيچ اثري نميگذارد.
از آنجايي که دستورالعمل dec فلگ صفر (ZF) را يک يا صفر ميکند، دستورالعمل پرش شرطي زير که کمي سريعتر ميباشد، ميتواند به جاي دستورالعمل JCXZ به کار برود.
Jz end_for
اغلب راحتتر است که براي پيادهسازي يک حلقه for از دستورالعمل loop استفاده شود حتي زماني که شاخص حلقه افزايش پيدا ميکند و بايد در بدنه حلقه مورد استفاده قرار گيرد. دستورالعمل loop ازثبات CX براي کنترل تعداد تکرارهاي استفاده ميکند.
Loopz statement_label
چنانچه مقدار جديدي در ثبات CX صفر باشد و فلگ صفر، يک باشد، دستورالعمل loopz به دستورالعملي که در statement_label قرار دارد، پرش ميکند.
Loopnz statement_label
چنانچه مقدار جديد در ثبات CX صفر نباشد و فلگ صفر برابر صفر باشد، دستورالعمل loopnz به دستورالعملي که در statement_lable قرار دارد، پرش ميکند.
همانند دستورالعمل loop، دستورالعملهاي loopz و loonz بر روي هيچ فلگي اثر نميگذارند.
آرايهها
Lea destination,source
نام lea يعني «آدرس مؤثر را بار کن» ميباشد.
Destination بايد يک ثبات عمومي 16 بيتي بوده و source هرگونه رجوعي به حافظه است.
آدرس source در ثبات بار ميشود.
دستورالعمل lea نسبت به mov قابليت انعطاف بيشتري را با توجه به نوع عملوند منبع در اختيار ميگذارد.
با استفاده از آدرسدهي غيرمستقيم ثبات ميتوان به صورت تصادفي دسترسي پيدا نمود.
به طور مثال، عبارت array [count] را به مجموع اضافه کن» ميتواند به ترتيب زير پيادهسازي شود، البته با اين فرض که ثبات CX به عنوان شمارنده و ثبات AX بعنوان مجموع به کار برود:
Mov dx,cx ;count
Dec dx ;count-1
Add dx,dx ;2*(count-1)
Lea bx,nbr_array ;starting address of array
Add bx,dx ;address of nbr_array [count]
Add ax,,[bx] ;add array [count] to sum
تکنيک به کار رفته در اينجا آن است که تعداد بايتهاي قبل از عنصر موردنظر را جمع کرده و اين عدد را به آدرس شروع آرايه اضافه کرد.
پرشهاي غير شرطي
دستور JMP
این دستور مانند دستور GOTO در بیسیک و پاسکال است
JMP statement_label
Statement_label اشاره به برچسب دستوری دارد که قرار است پرش به آن صورت گیرد
پرشهاي غير شرطي مثال
پرش به اولین دستورالعمل خروج Jmp quit;
.
بازگشت مقدار صفر در رجیسترQuit : mov al ,0;
انواع پرش مستقیم
- نسبی درون سگمنت
- کوتاه نسبی درون سگمنتی
- غیر مستقیم درون سگمنتی
- مستقیم بین سگمنتی
- غیر مستقیم بین سگمنتی
If پرشهاي شرطي دستورات مقايسه و ساختارهاي
- برای شبیه سازی دستور if در زبان اسمبلی انواع پرش وجود دارد
حالت کلی دستور به شکل زیر است
J --- TARGET _ STATEMENT
به جز یک استثنا در بقیه موارد عبارت پس از j که معمولا به نتیجه ثبت شده در فلاگها اشاره میکند تعیین شرط پرش مینماید
نمونه های پرش شرطی
Jz - پرش در صورت صفر بودن
Jnz - پرش در صورت صفر نبودن
Js - پرش در صورت منفی بودن
Jns - پرش در صورت مثبت بودن
دستور مقایسه
دستور CMP جهت مقایسه دو مقدار استفاده میشود
برای تغییر حالت فلاگها که منجر به دستور پرش خواهد شد میباید عبارت شرط را با این دستور ارزیابی کنیم
شکل کلی دستور به صورت زیر است
Cmp operand_1 , operand_2
پياده سازي حلقه هاي for , until , while
- برای پیاده سازی حلقه ها در اسمبلی مانند دستور if از cmp و j… استفاده میشود
- در حالت کلی عبارت مقایسه در ابتدا و عبارت پرش در انتهای کد ظاهر میشود
- در حلقه for علاوه بر این اندیس حلقه در یک رجیستر با دستور inc یا dec افزایش یا کاهش می یابد
حلقه های for
حلقه های for در زبان اسمبلی با شبیه سازی حلقه while بوجود می آید
ابتدای شروع حلقه اندیس حلقه مقدار دهی شده
دستور مقایسه در ابتدای حلقه اندیس را با مقدار انتهایی مقایسه و درصورت رسیدن به مقدار نهایی به دستورالعما انتهایی پرش میکند
در داخل حلقه اندیس افزایش یا کاهش می یابد
یک پرش غیر شرطی به ابتدای حلقه
آرايه ها
در زبان اسمبلی رجیسترهای زیر برای عملیات آرایه ای در نظر گرفته شده اند
SI , DI , BP
ذخیره سازی آرایه ها با دستور dup در برنامه اسمبلر و با دستور DW در سگمنت داده صورت میگیرد