تبلیغات شما تبلیغات شما

آمار سایت

    آمار مطالب
    کل مطالب : 3993
    آمار کاربران
    افراد آنلاین : 7

    کاربران آنلاین

    آمار بازدید
    بازدید امروز : 1,946
    باردید دیروز : 2,701
    گوگل امروز : 26
    گوگل دیروز : 35
    بازدید هفته : 21,251
    بازدید ماه : 52,386
    بازدید سال : 811,677
    بازدید کلی : 6,383,200

آخرین فروش های موفق

زير برنامه (procedure) مجموعه ای از دستورات است که يکبار تعريف و به دفعات استفاده می شود. با بکارگيری زيربرنامه خوانائی برنامه بالاتر رفته و از تکرار دستورات مشابه جلوگيری می شود. علاوه براين اشکال زدائی و تغيير برنامه آسان تر انجام گيرد.

وقتی يک زيربرنامه فراخوانی می شود کنترل اجرای برنامه به زيربرنامه هدايت می شود. آدرس دستورالعمل بعدی در پشته ذخيره می شود بنابراين هنگامی که زيربرنامه اجرا شد کنترل اجرا قادر خواهد بود به خط بعد از فراخوانی زيربرنامه بر می گردد.


تعريف زيربرنامه

تعريف زيربرنامه بايد در سگمنت کد انجام بگيرد. از دو راهنمای proc و endp برای تعيين بلاک زيربرنامه استفاده می شود.

ProcedureName PROC [NEAR|FAR]
     ...
     RET
ProcedureName ENDP

Procedurename نام زيربرنامه است که قبل از راهنماهای proc و endp قرار می گيرد و بايد يکسان باشد. عملوند near يا far اختياری است. کلمه near به اسمبلر می گويد که زيربرنامه از نوع داخلی است. برای تعريف يک زيربرنامه خارجی از کلمه far به جای near استفاده می شود.

دستور ret باعث خروج از زيربرنامه و برگشت به فراخواننده می شود.


نکته. اگر عملوندی مقابل تعريف زيربرنامه قرار نگيرد از نوع near در نظر گرفته می شود.
نکته. اگرچه تعريف يک زيربرنامه که عملا داخلی است به صورت خارجی اشکالی ايجاد نمی کند ولی بهتر است اين کار را نکنيد.


مثال. زيربرنامه جمع دو عدد در AH و AL و نگهداری مجموع در ثبات BX.

Adding PROC near
     mov BX, AL
     add BX, AH
     ret
Adding ENDP

مثال: زيربرنامه Putc برای نمايش کاراکتری که در ثبات al قرار دارد.

Putc PROC
     mov DL,AL
     mov AH,02
     int 21h
     ret
Putc ENDP


زيربرنامه های near و far

دو نوع زيربرنامه وجود دارد داخلی (intrasegment) و خارجی (intersegment).

• زيربرنامه های داخلی در همان سگمنتی که تعريف شده اند قابل فراخوانی هستند و در تعريف آنها از صفت near استفاده می شود.
• زيربرنامه های خارجی روال هائی که در سگمنت ديگری قرار دارند و از ساير سگمنت ها قابل فراخوانی می باشند و در تعريف آنها از صفت far استفاده می شود. زيربرنامه های خارجی درفايل جداگانه ای قرار دارد و هنگام لينک کردن بايد به برنامه پيوند داده شوند. نتيجه کار بعد از لينک مانند زيربرنامه داخلی است.

فراخوانی از نوع near کنترل را درون همان سگمنت کد جابجا می کند وتنها مقدار IP در پشته ذخيره می شود. فراخوانی far کنترل را بين سگمنت های مختلف عبور می دهد. هر دو مقادير CS و IP در پشته قرار می گيرند.

نکته. دستورات call و ret نوع فراخوانی را مشخص نمی کنند بلکه عملوند near|far راهنمای proc به اسمبلر می گويد فراخوانی از کدام نوع است.


دستورات فراخوانی و بازگشت زيربرنامه

دودستورالعمل که پشته را استفاده می کنند و فراخوانی و برگشت زيربرنامه را انجام می دهند call و ret هستند. برای هدايت کنترل اجرا به زيربرنامه بايد آنرا فراخوانی کرد. زيربرنامه ها در هر کجای برنامه که به آن نياز داريم با دستور call فراخوانی می شوند. دستور call به صورت زير است:

call ProcedureName

دستورالعمل call باعث يک پرش غير شرطی به زيربرنامه می شود و آدرس دستورالعمل بعدی را در پشته ذخيره می کند. CPU در برخورد با دستور call به آدرس شروع زيربرنامه رجوع می کند و دستورات آنرا اجرا می نمايد. با برخورد به دستور ret به برنامه فراخوان بر می گردد و دستورات بعد از call را اجرا می نمايد.


مثال. فراخوانی زيربرنامه Putc.

call Putc

CPU در برخورد با دستور Call عمليات زير را انجام می دهد:

فراخوانی از نوع داخلی

1. مقدار ثبات IP (که حاوی آدرس دستور بعد از call است ) را در پشته ذخيره می کند.
2. آدرس ذکر شده مقابل دستور call را در ثبات IP قرار می دهد.

فراخوانی از نوع خارجی

1. مقدار ثبات CS را در پشته ذخيره می کند.
2. بخش سگمنت آدرس ذکر شده مقابل دستور call را در ثبات CS قرار می دهد.
3. مقدار ثبات IP را در پشته ذخيره می کند.
4. بخش آفست آدرس ذکر شده در جلوی دستور call را در ثبات IP قرار می دهد.

دستورالعمل ret آدرس ذخيره شده IP را از پشته بر می دارد و به برنامه اصلی بر می گردد. CPU در برخورد با دستور Ret عمليات زير را انجام می دهد:

بازگشت از زيربرنامه داخلی

1. مقدار ذخيره شده در پشته را در داخل ثبات IP قرار می دهد.

بازگشت از زيربرنامه خارجی

1. مقدار ذخيره شده در پشته را در داخل ثبات IP قرار می دهد.
2. مقدار ذخيره شده در پشته را در داخل ثبات CS قرار می دهد.

نکته. اگر دستور ret در انتهای زيربرنامه حذف شود کنترل اجرای برنامه به زيربرنامه بعدی می رود نه دستورالعمل بعدی در برنامه اصلی.
نکته. معمولا در ابتدای هر زيربرنامه بهتر است مقادير ثبات هائی که تغيير می کنند را در پشته ذخيره نمائيم و در انتهای زيربرنامه و قبل از دستور ret مقادير آنها را از پشته بازيابی کنيم. بايد توجه کنيم که دستورات pop متناظر با دستورات push باشند و کليه داده هائی که در زيربرنامه در پشته push شده اند بايد pop شوند وگرنه به با دستور ret به آدرس درست پرش نمی کند.


مثال. زيربرنامه برای نمايش 40 کاراکتر space. توجه کنيد زيربرنامه Putc درون زيربرنامه PrintSpaces فراخوانی شده است.

PrintSpaces PROC near
                push AX
                push CX
                mov AL, ' '
                mov cx, 40
PSLoop:   call putc
                loop PSLoop
                pop CX
                pop AX
                ret
PrintSpaces ENDP

در ابتدای زيربرنامه ثبات های AX و CX در پشته قرار می گيرند و در انتها به ترتيب عکس بازيابی می شوند. زيربرنامه فوق به صورت زير فراخوانی می شود.

call PrintSpaces


ارسال و دريافت پارمترها

پارامترها مقاديری هستند که می توانيد به زيربرنامه بدهيد يا بگيريد. برای ارسال يا دريافت پارامترها معمولا از ثبات، متغيرهای سراسری يا پشته استفاده می شود.

ارسال پارامتر از طريق ثبات

مثال. زيربرنامه زير طول يک رشته را محاسبه و در ثبات CX برمیگرداند. آدرس شروع رشته در ثبات SI قرار دارد.

StrLen PROC
           push SI
           mov CX,0
Whl:   cmp Byte Ptr[SI],'$'
           jc EndW
           inc CX
           inc SI
           jmp Whl
EndW: pop SI
           ret
StrLen ENDP

ارسال پارامتر از طريق پشته

پارامترهائی که به زيربرنامه داده می شوند را می توان قبل از فراخوانی زيربرنامه در پشته اضافه کرد. پارامترها در زيربرنامه pop نمی شوند بلکه مستقيما از پشته دسترسی می شوند زيرا قبل از دستور call در پشته اضافه شده اند و آدرس برگشتی بعد از آن اضافه می شود. علاوه براين چون ممکن است در چندين جای زيربرنامه استفاده شوند معمولا درون ثبات نگهداری نمی شوند و بهتر است در حافظه پشته باقی بمانند.

يک برنامه خارجی که يک پارامتر از طريق پشته را ارسال می کند در نظربگيريد. وقتی زيربرنامه درخواست می شود پارامتر می تواند با آدرس دهی غيرمستقيم [SP+4] دسترسی شود. اگر پشته هم در زيربرنامه برای ذخيره داده استفاده شود عدد بيشتری بايد به SP اضافه شود. ثبات BP را برای ارجاع به داده های درون پشته می توان به کار برد. ثبات SP با هر push و pop تغيير می کند اما BP ابتدا برابر با SP می شود و سپس ثابت می ماند در انتهای زيربرنامه مقدار اوليه BP بايد برگردانده شود. بعد از اينکه زيربرنامه تمام شد پارامترهائی که در پشته اضافه شده اند بايد حذف شوند.


مثال. تابع زير طول رشته را محاسبه و آدرس شروع رشته از طريق پشته به زيربرنامه ارسال می شود.

StrLen PROC
           push BP
           mov BP,SP
           mov SI,[BP+4]
           sub CX,0
Whl:   cmp byte ptr [SI],'$'
           jc Endw
           inc CX
           inc SI
           jmp Whl
EndW: pop BP
           ret
StrLen ENDP

مثال. محاسبه مجموع سه عدد که از طريق پشته به زيربرنامه ارسال شده اند.

ثابت ها

ثابت مشابه متغير محلی برای ذخيره داده ای است که توسط برنامه استفاده می شود. اما برخلاف متغير، مقدار ذخيره شده در يک ثابت در طی اجرای برنامه قابل تغيير نيست.

++C دو نوع ثابت واقعی و سمبليک دارد.

ثابت های واقعی

ثابت واقعی (literal constant) مقداری است که مستقيما در کد برنامه تايپ می شود.


مثال. مقادير 20 و 0.28 در عبارات زير ثابت های واقعی هستند که در متغيرهای count و tax_rate ذخيره می شوند.

int count = 20;
float tax_rate = 0.28;


مقادير ثابت برای نوع های پيش ساخته می تواند به صورت های دسيمال، اکتال، هگزادسيمال، مميزشناور يا کاراکتر باشند.

نقطه اعشار نشان دهنده ثابت مميزشناور است. ثابت های مميزشناور را می توان به صورت نماد علمی هم بيان کرد.


مثال. اعداد زير همگی ثابت های مميزشناورهستند.

123.456
0.019
100.
1.23E2 or 123
4.08e6 or 4080000
0.85e-4 or 0.000085


حروف f يا F برای ثابت های float و l يا L برای ثابت های long double را می توان به عنوان پسوند اضافه کرد در غير اينصورت با ثابت مميزشناور بعنوان يک عدد double برخورد می شود.

ثابتی که نقطه اعشار ندارد بعنوان ثابت صحيح تفسير می شود. ثابت های صحيح را به 3 صورت می توان نشان داد:

• ثابت دهدهی. اگر ثابت با هر عدد غير صفری شروع شود بعنوان يک عدد صحيح مبنای 10 تفسير می شود.. ثابت های دهدهی شامل ارقام 0 تا 9 و علامت + يا – می توانند باشند. اگر علامت نوشته نشود ثابت مثبت درنظر گرفته می شود.
• ثابت اکتال. اگر ثابت با رقم 0 شروع شود بعنوان يک عدد صحيح مبنای 8 تفسير می شود. ثابت های اکتال می توانند شامل ارقام 0 تا 9 و علامت + يا – باشند.
• ثابت هگزادسيمال. اگر ثابت با 0x يا 0X شروع شود بعنوان يک ثابت مبنای 16 با آن برخورد می شود. ثابت های هگزادسيمال می توانند شامل ارقام 0 تا 9 و حروف A تا F و علامت + يا – باشند.

ثابت های کاراکتری بين علامت کوتيشن (') قرارمی گيرند، مانند کاراکترهای 'A' و '0'. کاراکترهای خاص با علامت () نشان داده می شوند مانند 'n' (newline)،'t' (tab)،'' (backslash)،'r' (carriage return)،''' (single quote)،'"' (double quotes)، و غيره.

ثابت های سمبليک

ثابت سمبليک (symbolic constant) ثابتی است که توسط يک نام در برنامه مشخص می شود. مقدار حقيقی ثابت سمبليک تنها يکبار هنگام تعريف آن وارد می شود و اين مقدار مشابه ثابت های واقعی در طول اجرای برنامه قابل تغيير نيست. هر زمان که به مقدار ثابت در برنامه نياز باشد نام آن ذکر می شود.


مثال. برای محاسبه مساحت و محيط دايره در يک برنامه به جای نوشتن عدد 3.14 می توان ثابت PI را تعريف و استفاده کرد.

circumference = PI * (2 * radius);
area = PI * (radius)*(radius);

در اين حالت هم نوشتن برنامه راحت تر است و برنامه دارای خوانائی بيشتر است هم اگر نياز به تغيير ثابت پی به مقدار 3.14159 برای بالا بردن دقت محاسبات باشد اصلاح تنها در يک نقطه برنامه که ثابت تعريف شده است صورت می گيرد نه کل برنامه.


تعريف ثابت سمبليک

دوروش برای تعريف ثابت های سمبليک وجود دارد: راهنمای define# و کلمه کليدی const.

راهنمای define# در قسمت پيش پردازنده ها بيشتر توضيح داده خواهد شد. فرم کلی آن به صورت زير است:

#define CONSTNAME literal

CONSTNAME نام ثابت و literal مقدار واقعی آن است. راهنمای define# به کامپايلر دستور می دهد که درسرتاسر کد برنامه CONSTNAME را با literal جايگزين کند.

نام ثابت از همان قواعد نامگذاری متغيرها تبعيت می کند. مرسوم است که کليه حروف ثابت را بزرگ می نويسند که باعث تشخيص راحت تر آن از متغيرها که با حروف کوچک هستند می شود.


مثال. ثابت PI با مقدار 3.14159 تعريف شده است.

#define PI 3.14159


دقت کنيد که خط define# به سميکولن(;) ختم نمی شود. define# می تواند در هر کجای برنامه باشد ولی معمولا همگی در قسمتی از ابتدای کد برنامه و قبل از تابع main() هستند.

روش دوم تعريف يک ثابت سمبليک استفاده از کلمه کليدی const است. const را می توان به اول هر اعلان متغيری اضافه کرد. در اين صورت متغير در طول اجرای برنامه قابل تغيير نخواهد بود. اگر برنامه سعی به تغيير آن کند خطای کامپايلر صادر خواهد شد.


مثال.

const int count = 100;
const float pi = 3.14159;
const long debt = 12000000, float tax_rate = 0.21;


const روی کليه متغيرهای خط اعلان تاثير می گذارد.

تفاوت ثابت هائی که با #define و const تعريف می شوند در حوزه آنهاست. وقتی از #define استفاده می کنيد کنترل ثابت ها را از حوزه کامپايلر خارج می کنيد؛ type checking روی اسم آن انجام نمی گيرد و آدرس آنرا قابل بازيابی نيست و اشاره گريا ارجاعی به آن ممکن نيست و نمی تواند از نوع user-defined باشد. اما اگر ثابت توسط const تعريف شده باشد می تواند از هر نوع داده استاندارد يا user-defined باشد. آدرس آن هم قابل بازيابی است و مانند يک متغير دارای حوزه دسترسی است بنابراين ثابتی که درون يک تابع تعريف می شود در ساير نقاط برنامه شناخته شده نيست.

مقداردهی اوليه متغيرهای عددی

هنگام اعلان يک متغير، با وجوديکه مقدار آن هنوز تعيين نشده است، به کامپايلر فرمان داده می شود تا فضای لازم برای متغير را کنار بگذارد. در اين حالت مقدار متغير می تواند صفر يا يک مقدار تصادفی باشد. قبل از استفاده از متغير هميشه بايد آن را با يک مقدار مشخص مقداردهی کرد. اين کار موقع اعلان متغير می تواند صورت بگيرد. برای اينکار بعد از اسم متغير علامت مساوی و مقدار اوليه موردنظر را ذکر کنيد.


مثال.

int count = 0;
double percent = 0.01, taxrate = 28.5;


علامت مساوی (=) عملگر واگذاری در زبان C است که در بخش عبارات توضيح داده می شود.

توجه کنيد مقداراوليه متغير خارج از محدوده مجاز نباشد. کامپايلر چنين خطائی را گير نمی اندازد و برنامه ممکن ا ست هنگام اجرا نتايج غيرمنتظره ای توليد کند.


نکته. با تعداد بايت هائی که نوع های متغير روی کامپيوتر شما اشغال می کند به خوبی آشنا شويد.
نکته. از typedef برای خوانا کردن برنامه خود استفاده کنيد.
نکته. تاحد ممکن هنگام تعريف متغير به آنها مقداراوليه بدهيد. استفاده از متغيری که مقداردهی نشده است ممکن است نتايج غيرقابل پيش بينی توليد کند.
نکته. استفاده از متغيرهای float و double برای ذخيره اعداد صحيح باعث پايين آمدن کارائی برنامه می شود.
نکته. مقادير بزرگ را در نوع متغيری که برای ذخيره آنها کوچک است ذخيره نکنيد.
نکته. اعداد منفی را در نوع های بدون علامت ذخيره نکنيد.

 

در زبان برنامه نويسی C هر متغير قبل از استفاده بايد اعلان شود. اعلان متغير کامپايلر را از نام، نوع و مقدار اوليه متغير آگاه می کند. اگر برنامه سعی کند از متغيری استفاده کند که قبلا اعلان نشده است کامپايلر پيغام خطا صادر می کند.

اعلام متغير به فرم کلی زير است:

typename varname;

typename نوع متغير را مشخص می کند که بايد يکی از نوع های داده ای زبان C باشد. varname نام متغير است که بايد از قواعد گفته شده تبعيت کند.

می توان در يک خط چند متغير را از يک نوع تعريف کرد.


مثال. متغيرهای count، number و start از نوع int و متغيرهای percent و total از نوع float اعلان شده اند.

int count, number, start;   /* three integer variables */
float percent, total;          /* two float variables */


در بخش توابع درقسمت حوزه متغيرها درباره محل اعلان متغير در برنامه توضيح داده شده است. فعلا کليه متغيرها را در آغاز تابع ()main اعلان کنيد.


کلمه کليدی typedef

کلمه کليدی typedef برای ايجاد نامی جديد به نوع داده موجود بکار می رود و در واقع يک مترادف برای آن نوع توليد می کند.


مثال. عبارت زير برای نوع داده ای int مترادف integer را ايجاد می کند. بنابراين در برنامه می توان از کلمه integer برای اعلان متغيرهای از نوع int استفاده کرد.

typedef int integer;
integer count;


توجه داشته باشيد که typedef نوع داده جديدی را توليد نمی کند بلکه تنها به شما اجازه نامگذاری نوع داده ای که قبلا تعريف شده را می دهد. کاربرد زياد آن در نوع های داده ای ترکيبی است که در بخش ساختمان مطالعه خواهيد کرد.

انواع داده عددی

دانستن انواع متغيرهای عددی که زبان برنامه نويسی در اختيار می گذارد لازم است زيرا مقادير عددی مختلف ميزان حافظه متفاوتی را اشغال می کنند و عمليات رياضی معينی روی آنها انجام می شود. نوع متغير بسته به طبيعت داده ای که ذخيره می کند می تواند يکی از اين انواع تعريف شده باشد. با انتخاب نوع مناسب برای متغير برنامه شما با بيشترين بازدهی ممکن اجرا می شود.

اعداد صحيح کوچک حافظه کمتری می خواهند و کامپيوتر می تواند عمليات رياضی مانند جمع و ضرب را به سرعت روی آنها انجام بدهد. درحاليکه مقادير صحيح بزرگ و اعداد مميز شناور به فضا و زمان بيشتری برای محاسبات نياز دارند.

انواع متغير عددی در دو دسته کلی قرار می گيرند:

• متغيرهای صحيح، مقاديری را نگه می دارند که بخش کسری ندارند. متغيرهای صحيح خود دو نوع هستند: علامتدار، که می توانند مقادير مثبت و منفی را ذخيره کنند و بدون علامت، که تنها مقادير مثبت را نگه می دارند.
• متغيرهای مميزشناور، مقاديری را که دارای بخش کسری هستند يعنی اعداد حقيقی را نگه می دارند.


عبارات signed، unsigned، short و long اصلاح کننده هائی هستند که برای تغيير و تبديل انواع پايه از قبيل char، int و double اضافه می شوند .

انواع داده ها وابسته به platform کامپيوتر است و ممکن است روی کامپيوترهای مختلف متفاوت باشد، اما C با توجه به استاندارد ANSI موارد زير را تضمين می دهد:

• يک کاراکتر هميشه يک بايت است.
• اندازه short کوچکتر يا مساوی اندازه int است.
• اندازه int کوچکتر يا مساوی اندازه long است.
• اندازه unsigned برابر با int است.
• اندازه float کوچکتر مساوی با اندازه double است.

 

يک متغير يک محل ذخيره داده نامگذاری شده در حافظه کامپيوتر است. شما در برنامه با استفاده از نام متغير به داده ذخيره شده در اين محل رجوع می کنيد.

نام متغير

هر متغير دارای يک نام است. در زبان C اسامی متغير ها بايد از قوانين زير پيروی کند:

• اسم می تواند شامل حروف، ارقام و کاراکتر زيرخط ( _ ) باشد.
• اولين حرف اسم متغير بايد يک حرف باشد. زير خط هم می تواند در ابتدای اسم متغير بکار برود ولی توصيه نمی شود.
• بزرگ و کوچک بودن حروف مهم است، بنابراين اسامی count و Count به دو متغير جداگانه اشاره دارند. برنامه نويسان اغلب از حروف کوچک برای متغير ها استفاده می کنند و اسامی با حروف بزرگ معمولا برا ثابت ها بکار می رود.
• کلمات کليدی نمی توانند به عنوان نام متغير استفاده شود.


مثال. چند نمونه از اسامی مجاز و غير مجاز در جدول زير ديده می شود:

مجازبودن نام متغير
مجاز Percent
مجاز y2x5__fg7h
مجاز annual_profit
مجاز، ولی توصيه نمی شود _1990_tax
غير مجاز، کاراکتر# savings#account
غيرمجاز، کلمه کليدی double
غيرمجاز، اولين کاراکتر رقمی 9winter

در بعضی کامپايلرها نام متغير می تواند 31 کاراکتر (در ++C استاندارد 1024 کاراکتر) طول داشته باشد؛ يعنی کامپايلر تنها 31 کاراکتر اول نام را درنظر می گيرد. با اين طول می توان نامی برای متغير انتخاب کرد که معنی داده ذخيره شده در آن را منعکس کند.

نام متغير کمک به روشن شدن کاربرد آن برای کسی که source برنامه را نگاه می کند .


مثال. در برنامه ای که پرداخت های وام را محاسبه می کند مقدار بهره را می توان در متغير interest_rate ذخيره کرد.


مرسوم است از زيرخط برای جدا کردن متغيرهای چند کلمه ای استفاده شود. يا ازروش نام گذاری کوهانی (camel notation) که حرف اول هر کلمه بزرگ است پيروی می شود.


مثال. متغيری که بهره بانکی را ذخيره می کنذد می تواند به دو صورت interest_rate يا InterestRate تعريف شود.


نکته. نامهای توصيفی برای متغيرها انتخاب کنيد.
نکته. به سبکی که برای نامگذاری متغير ها انتخاب کرده ايد در کل برنامه وفادار بمانيد.
نکته. شروع نام متغير با زيرخط غير ضروری است.
نکته. نامگذاری متغير همگی با حروف بزرگ غير ضروری است.

در اين صفحه دستورات حلقه و نحوه پياده سازی ساختارهای حلقه های تکرار توضيح داده می شود.

LOOP
LOOPE/LOOPZ
حلقه های تکرار ديگر


LOOP

در زبان اسمبلی از دستور loop برای ساختن حلقه های شمارشی کاهشی با بدنه کوچک استفاده می شود. فرم کلی آن به صورت زير است:

loop target

دستورالعمل loop ثبات CX را يک واحد کم می کند سپس اگر مقدار جديد CX مخالف با صفر باشد به آدرس target پرش می کند. اگر CX برابر با صفر باشد دستور بعدی اجرا می شود.

ثبات CX به عنوان شمارنده عمل می کند و تعداد تکرار حلقه بايد ابتدا در ثبات CX قرار بگيرد.


مثال. دستورات زير مجموع اعداد 1 تا 10 را محاسبه و در ثبات AX ذخيره می کند.

      mov AX, 0
      mov CX, 10
lbl:
      add AX, CX
      loop lbl


توجه کنيد چون اين دستور ابتدا از CX يک واحد کم می کند، اگر CX قبلا حاوی صفر باشد بعد از کاهش برابر با مقدار 65535 می شود بنابراين حلقه 65536 بار تکرار خواهد شد.

دستورالعمل loop مشابه دستورات پرش کوتاه محدوده به 128 بايت است و برای حلقه با بدنه کوچک مناسب است. برای حلقه های تکرار با بدنه بيشتر می توانيد از دستورات پرش برای ساختن حلقه تکرار استفاده نمائيد.

اگرچه از نام دستورالعمل loop اينطور برمی آيد که حلقه تکرار ايجاد می کند اما به خاطر داشته باشيد که تمام کاری که انجام می دهد اين است که از ثبات CX يک واحد کم کرده اگر بعد از کاهش CX حای صفر نبود به آدرس مقصد پرش می کند.

دستورالعمل loop روی هيچکدام از ثبات ها تاثير ندارد.


LOOPE/LOOPZ

دستورالعمل های loopz و loope مشابه دستور loop می باشند با اين تفاوت که پرش در صورتی انجام می گيرد که CX مخالف با صفر و فلگ Z مساوی با يک باشد.

اين دستورات برای استفاده بعد از دستور cmp مفيد است.


مثال. دستورات زير اولين عنصر غيرصفر را در آ رايه 15 تائی Array جستجو می کند. اگر کليه عناصر آرايه صفر باشد بعد از اجرای حلقه به آدرس AllZero پرش می کند.

      lea SI, Array
      dec SI
      mov cx, 15
Search:
      inc SI
      cmp byte ptr [SI], 0
      loope Search
      je AllZero


حلقه های تکرار ديگر

از دستورات هم پرش می توان برای ساختن حلقه های تکرار استفاده کرد. به مثال های زير توجه کنيد.

مثال. دستورات زير مجموع اعداد 1 تا 10 را محاسبه و در ثبات AX ذخيره می کند.

       mov AX, 0
       mov CX, 1
For:
      cmp CX, 10
      jle Repeat
      jmp EndFor
Repeat:
      add AX, CX
      inc CX
      jmp For
EndFor:

مثال. دستورات زير کاراکترهائی را از کاربر گرفته و در يک آرايه ذخيره می کند تا وقتی که کليد enter وارد شود.

      lea SI, Array
Readkey:
      mov AH, 1
      int 21h           ;Get a character
      cmp AL, 13     ;Carriage return ASCII code
      je EndLoop
      mov [SI],AL    ;Save input character in Array
      inc SI
      jmp Readkey
EndLoop:

ساختارهای کنترلی نظير عبارات شرطی و حلقه های تکرار توسط دستورات پرش ساخته می شود. 8086 چند نوع دستورالعمل پرش را در اختيار می گذارد.

دستور پرش بدون شرط
دستورات پرش شرطی
ساختار شرط


دستورات برنامه پشت سر هم اجرا می شوند يعنی پردازنده دستورات را به ترتيبی که در برنامه ظاهر شده اند اجرا می کند. ساختارهای کنترلی نظير عبارات شرطی، حلقه ها و فراخوانی زيربرنامه روال اجرای برنامه را تغيير می دهد. زبان های سطح بالا ساختارهای کنترلی سطح بالا مانند دستورات if و while را دراختيار می گذارند که اجرای برنامه را کنترل می کنند. زبان اسمبلی چنين ساختارهای پيچيده ای را ندارد در عوض از دستورات پرش برای پياده سازی اين ساختارهای کنترلی استفاده می شود(که البته استفاده نامناسب آن باعث کد اسپاگتی می شود).

دستورات پرش اجرای برنامه را به نقطه دلخواهی منتقل می کنند. دو نوع دستورالعمل پرش وجود دارد:

• دستورات پرش بدون شرط
• دستورات پرش شرطی

گونه های مختلفی از دستورات پرش وجود دارند:

• کوتاه (short). اين نوع پرش بسيار محدود است و تنها می تواند 128 بايت بالا يا پايين بپرد. مزيت آن در مصرف کمتر حافظه است. ميزان جابجائی تنها توسط يک بايت مشخص می شود که تعيين می کند چند بايت جلوتر يا عقب تر برود. اين فاصله به ثبات IP اضافه می شود.
• نزديک (near). اين نوع پرش می تواند به هر موقعيت درون يک سگمنت پرش کند.
• دور (far). اين نوع پرش اجازه حرکت به سگمنت های ديگر را می دهد.

دستور پرش بدون شرط

دستورالعمل (jump) jmp بدون هيچ شرطی کنترل را به نقطه ديگری در برنامه منتقل می کند و مشابه دستور goto در زبان های سطح بالا عمل می کند. فرم کلی آن به صورت زير است:

jmp target

target می تواند آدرسی درون همين سگمنت يا سگمنت کد ديگری باشد. معمولا آدرس مقصد توسط يک برچسب معين می شود. برچسب شناسه ای است که بدنبال آن علامت کلون (:) می آيد. اسمبلر با توجه به آفست دستور بعد از برچسب، فاصله پرش را به طور اتوماتيک محاسبه می کند.

دستورالعمل بعد از jmp هيچوقت اجرا نمی شود مگر اين که از دستور ديگری به آن پرش شده باشد.

دستور jmp به تنهائی در برنامه موثر نيست و برای ساختن ساختارهای کنترلی همراه با دستورات پرش شرطی استفاده می شود.


مثال. حلقه زير مرتب از پورت موازی داده را می خواند و بيت صفر آنرا عکس می کند. اين باعث توليد يک سيگنال مربعی روی يکی از خطوط پورت پرينتر شود.

               mov DX, 378h   ;Parallel printer port address.
Forever: in AL, DX           ;Read character from input port.
               xor AL, 1           ;Invert the L.O. bit.
               out DX, AL        ;Output data back to port.
               jmp Forever      ;Repeat forever.


دستورات پرش شرطی

دستورات پرش شرطی برای ساختن حلقه ها و عبارات شرطی مانند if بکار می روند. پرش های شرطی يک يا چند فلگ را بررسی می کنند و با توجه به وضعيت آنها کنترل را به آدرس معينی منتقل می کنند. اگر پرش انجام نشود اجرا از دستورالعمل بعد از ادامه پيدا می کند.

با توجه به اينکه دستورات پرش شرطی فلگ ها را بررسی می کنند قبل از دستور پرش بايد دستوری وجود داشته باشد که روی فلگ ها تاثير بگذارد. برای مثال بعد از اجرای دستور shl می توانيد فلگ Carry را تست کنيد تا ببينيد بيت 1 از سمت چپ عدد خارج شده است يا خير. يا بعد از دستورالعمل test می توانيد فلگ Zero را بررسی کنيد تا ببينيد بيت های مشخصی در يک عدد 1 بوده اند يا خير. البته در اکثر موارد ساختارهای کنترلی بر اساس مقايسه مقادير و توسط cmp پياده سازی می شوند. دستورالعمل cmp با توجه به حاصل تفريق دو عملوند خود فلگ ها را تنظيم می کند بنابراين می تواند برای بررسی بزرگتر، کوچکتر يا مساوی بودن مقادير استفاده شود. برای مقدارهای بدون علامت دو فلگ Carry و Zero از ثبات پرچم مهم هستند و برای اعداد علامتدار فلگ های Sign و Zero اهميت دارند. فلگ Zero در صورتی که مساوی بودن عملوند را نشان می دهد.

دستورات پرش تنها فلگ ها را بررسی می کنند و روی آنها تاثيری ندارند.

دستورالعمل های پرش شرطی انواع مختلفی دارند که همگی با حرف J شروع می شوند و بعد از آن حروف ديگر قرار می گيرند. بدنبال دستور يک آدرس يا برچسب ذکر می شود. اين دستورات را به سه دسته کلی می توان تقسيم کرد:

• دستورات پرش بر اساس فلگ ها
• دستورات پرش بعد از مقايسه عملوندهای بدون علامت
• دستورات پرش بعد از مقايسه عملوندهای علامتدار

توجه داشته باشيد که پرش های شرطی از نوع کوتاه هستند و طول پرش محدود به 128 بايت است برای غلبه براين محدوديت می توانيد ساختار شرط را توسط پرش متضاد بسازيد.

در 8086 دستورات مترادفی برای تعدادی از دستورات پرش شرطی وجود دارد. جداول زير مترادف های هر دستور را ليست می کنند. در اين جداول همچنين دستورات متضاد هر دستور شرطی نيز بيان شده است.

دستورات پرش بر اساس فلگ ها

دستورالعمل
شرط
شرح
مترادف
متضاد
jc Carry = 1 پرش در صورتی که رقم نقلی وجود دارد jb, jnae jnc
jnc Carry = 0 پرش در صورتی که رقم نقلی وجود ندارد jnb, jae jc
jz Zero = 1 پرش در صورتی که صفر است je jnz
jnz Zero = 0 پرش در صورتی که صفر نيست jne jz
js Sign = 1 پرش در صورتی که مثبت است - jns
jns Sign = 0 پرش در صورتی که منفی است - js
jo Overflow=1 پرش در صورتی که سرريزی وجود دارد - jno
jno Overflew=0 پرش در صورتی که سرريزی وجود ندارد - jo
jp Parity = 1 پرش در صورتی که پريتی زوج است jpe jnp
jnp Parity = 0 پرش در صورتی که پريتی فرد است jpo jp

دستورات پرش بعد از مقايسه بدون علامت

دستورالعمل
شرط
شرح
مترادف
متضاد
ja Carry=0, Zero=0 پرش در صورتی که بالاتر است jnbe jna
jnbe Carry=0, Zero=0 پرش در صورتی که پايين تر يا مساوی نيست ja jbe
jae Carry = 0 پرش در صورتی که بالاتر يا مساوی است jnc, jnb jnae
jnb Carry = 0 پرش در صورتی که پايين تر نيست jnc, jae jb
jb Carry = 1 پرش در صورتی که پايين تر است jc, jnae jnb
jnae Carry = 1 پرش در صورتی که بالاتر يا مساوی نيست jc, jb jae
jbe Carry = 1 يا Zero = 1 پرش در صورتی که پايين تر يا مساوی است jna jnbe
jna Carry = 1 يا Zero = 1 پرش در صورتی که بالاتر نيست jbe ja
je Zero = 1 پرش در صورتی که مساوی است jz jne
jne Zero = 0 پرش در صورتی که نامساوی است jnz je

دستورات پرش بعد از مقايسه علامتدار

دستورالعمل
شرط
شرح
مترادف
متضاد
jg Sign = Overflow يا Zero=0 پرش در صورتی که بزرگتر است jnle jng
jnle Sign = Overflow يا Zero=0 پرش در صورتی که کوچکتر يا مساوی نيست jg jle
jge Sign = Overflow پرش در صورتی که بزرگتر يا مساوی است jnl jge
jnl Sign = Overflow پرش در صورتی که کوچکتر نيست jge jl
jl Sign Overflow پرش در صورتی که کوچکتر است jnge jnl
jnge Sign Overflow پرش در صورتی که بزرگتر يا مساوی نيست jl jge
jle Sign Overflow يا Zero=1 پرش در صورتی که کوچکتر يا مساوی است jng jnle
jng Sign Overflow يا Zero=1 پرش در صورتی که بزرگتر نيست jle jg
je Zero = 1 پرش در صورتی که مساوی است jz jne
jne Zero = 0 پرش در صورتی که نامساوی است jnz je

ساختار شرط

از دستورات پرش شرطی می توان برای ساختن عبارات if استفاده کرد. به مثال های زير دقت کنيد.

مثال. در دستورات زير اگر Sum>0 باشد به AH مقدار يک را اختصاص می دهد درغير اينصورت BH را يک می کند.

          cmp Sum, 0
          jg Then
          jmp Else
Then: mov AH,1
          jmp EndIf
Else:   mov BH,1
EndIf:

مثال. در دستورات زير اگر Total>=100 and Count =0 باشد مقدار Value را با AX جمع می کند.

          cmp total, 100
          jge C2
          jmp EndIf
C2:     cmp Count, 10
          je Then
          jmp EndIf
Then: add AX, Value
EndIf:


دستورات چرخش رشته های بيتی را به صورت دايره ای حرکت می دهد اين دستورات مشابه شيفت عمل می کنند با اين تفاوت که بيتی که از يک طرف از داده خارج می شود به طور دوار از جهت ديگر وارد آن می شود. پردازنده 8086 چهار دستورالعمل چرخش (rol ، ror ، rcl و rcr) دارد.

ROL
ROR
RCL
RCR


ROL

دستورالعمل (rotate left) rol بيت های عملوند خود را به سمت چپ چرخش می دهد. فرم کلی آن به صورت زير است:

rol dest, count

دستورالعمل rol بيت های عملوند مقصد خود را به تعداد count به سمت چپ چرخش می دهد. بيتی که از سمت چپ خارج می شود از سمت راست وارد عملوند می شود.با ارزش ترين بيت عدد در فلگ carry کپی می شود.

عملوند اول عملوند مقصد است و عملوند دوم تعداد شيفت ها را مشخص می کند و می تواند عدد 1 يا برای تعداد بيشتر ثبات CL باشد. نوشتن عدد فوری بيشتر از 1 مستقيما در دستور غير مجاز است.

دستورالعمل rol به شکل های زير می تواند استفاده شود:

rol register, 1
rol memory, 1
rol register, CL
rol memory, CL

دستورالعمل rol بيت های فلگ را به صورت زير تغيير می دهد:

• فلگ carry حاوی آخرين با ارزش ترين بيت عملوند می شود.
• اگر تعداد چرخش يکبار باشد و علامت عدد بعد از چرخش تغيير کند فلگ overflow يک می شود. برای چرخش بيشتر از يکبار نامعين است.
• روی فلگ های Sign ، Zero ، Parity و Auxiliary carry تاثير ندارد. اگر نيازداريد مقدار اين فلگ ها را بعد از عمل چرخش بدانيد نتيجه را با صفر مقايسه کنيد تا اين فلگ ها تنظيم شوند.


مثال.

mov AX, C123h
mov CL,3
rol AX, CL    ;AX = 091Eh, CF = 0


ROR

دستورالعمل (rotate right) ror بيت های عملوند خود را به سمت راست چرخش می دهد. فرم کلی آن به صورت زير است:

ror dest, count

دستورالعمل ror بيت های عملوند مقصد خود را به تعداد count به سمت راست چرخش می دهد. بيتی که از سمت راست خارج می شود از سمت چپ وارد عملوند می شود.

مشابه دستورالعمل rol عملوند دستورالعمل ror می تواند ثبات يا حافظه باشد. تعداد چرخش عدد 1 يا ثبات CL است.

کم ارزش ترين بيت عملوند وارد فلگ carry می شود. بقيه فلگ ها مشابه دستورالعمل rol تاثير می پذيرند.


مثال.

mov AX, C123h
mov Cl,2
ror AX, CL     ;AX = F048h, CF = 1


RCL

دستورالعمل (rotate through carry left) rcl همانطور که از نامش پيدا است، بيت ها را از طريق فلگ carry به سمت چپ می چرخاند. شکل کلی آن به صورت زير است:

rcl dest, count

دستورالعمل rcl محتوای فلگ Carry را از سمت راست وارد عملوند مقصد می کند، سپس بيت های عملوند را به سمت چپ شيفت می دهد و آخرين بيت سمت چپ را وارد فلگ Carry می شود.

دستورالعمل rcl مشابه rol استفاده می شود و روی فلگ ها تاثير می گذارد.


RCR

دستورالعمل (rotate through carry right) rcr بيت های داده را از طريق فلگ carry به سمت راست می چرخاند. شکل کلی آن به صورت زير است:

rcr dest, count

دستورالعمل rcr محتوای فلگ Carry را از سمت چپ وارد عملوند مقصد می کند، سپس بيت های عملوند را به سمت راست شيفت می دهد و آخرين بيت سمت راست را وارد فلگ Carry می شود.

دستورالعمل rcr مشابه ror استفاده می شود و روی فلگ ها تاثير می گذارد.

دستورات شیفت يک رشته بیتی را به سمت راست يا چپ حرکت می دهند. توسط اين دستورات می توان روی بيت های داده کار کرد؛ داده را ادغام يا جدا کرد و عمليات محاسباتی را انجام داد. ريزپردازنده 8086 سه دستورالعمل شيفت (shl/sal، shr و sar) دارد. بخش زير هر يک از اين دستورالعمل ها را شرح می دهد.

SHL/SAL
SHR
SAR
کاربردهای شيفت


عمل شيفت بيت های داده را حرکت می دهد. حرکت بيت ها می تواند به سمت چپ (به سمت بيت های با ارزش) يا راست (به سمت بيت های کم ارزش) باشد. فلگ Carry معمولا آخرین بيت شیفت داده شده که از عملوند خارج می شود را می گيرد.

دو نوع شيفت وجود دارد: شيفت منطقی و شيفت رياضی. شيفت منطقی ساده ترين شيفت است که به طريق ساده ای بيت ها را شيفت می دهد. در شيفت رياضی علامت عدد حفظ می شود.


مثال. يک عدد شيفت داده شده يک بايتی نشان داده شده است.

Shift Example

توجه کنيد که بيت های جديدی که وارد می شوند هميشه صفر هستند.


SHL/SAL

دستورالعمل (shift left) shl يا (shift arithmetic left) sal بيت های داده را به سمت چپ حرکت می دهد. فرم کلی آنها به صورت زير است:

shl dest, count
sal dest, count

shl و sal معادل هستند و يک دستورالعمل را نشان می دهند يعنی کد يکسانی دارند. اين دستورالعمل ها هر بيت عملوند مقصد را به سمت چپ عدد به تعداد عملوند count حرکت می دهند. از سمت راست عدد 0 وارد عدد می شود و آخرين بيتی که از سمت چپ خارج می شود وارد فلگ carry می شود.

عملوند اول مقداری است که شيفت داده می شود و عملوند مقصد است. count تعداد شيفت ها را مشخص می کند و می تواند عدد 1 يا برای تعداد شيفت های بالاتر ثبات CL باشد. نوشتن تعداد شيفت بيشتر از 1 مستقيما در دستور غير مجاز است.

دستورالعمل shl/sal می تواند به صورت های زير بکار رود:

shl register, 1
shl memory, 1
shl register, CL
shl memory, CL

دستورالعمل shl/sal به صورت زير روی فلگ تاثير می گذارد:

• اگر تعداد شيفت صفر باشد فلگ ها تغييری نمی کنند.
• فلگ carry آخرين بيت خارج شده از سمت چپ عملوند را نگه می دارد.
• فلگ overflow در يک بيت شيفت يک می شود اگر دو بيت آخرعملوند متفاوت باشند. به عبارت ديگر بعد از عمل شيفت بيت علامت عدد تغيير کند. برای شيفت های بيشتر از يکبار نامعين است.
• فلگ zero، sign و parity با توجه به نتيجه تغيير می کنند.
• فلگ Auxilury Carry نامعين است.


مثال.

mov AX, 4123h
shl AX, 1    ; shift 1 bit to left, ax = 8246H, CF = 0


SHR

دستورالعمل (shift right) shr بيت های داده را به سمت راست حرکت می دهد. فرم کلی آنها به صورت زير است:

shr dest, count

دستورالعمل shr کليه بيت های عملوند مقصد را به تعداد count به سمت راست شيفت منطقی می دهد. از سمت چپ صفر وارد عملوند می شود و آخرين بيتی که از سمت راست خارج می شود وارد فلگ Carry می شود.

دستورالعمل shr مانند shl استفاده می شود؛ عملوند آن می تواند ثبات يا مکانی از حافظه باشد و تعداد شيفت ها می تواند عدد 1 يا ثبات CL باشد.

دستورالعمل shr فلگ ها را به صورت زير تنظيم می کند:

• اگر تعداد شيفت صفر باشد فلگ ها تغييری نمی کنند.
• فلگ carry آخرين بيت خارج شده از سمت راست عملوند را نگه می دارد.
• در يک بيت شيفت فلگ overflow يک می شود اگر دو بيت آخرعملوند متفاوت باشند. به عبارت ديگر اگر بعد از عمل شيفت بيت علامت عدد تغيير کند. برای شيفت های بيشتر از يکبار نامعين است.
• فلگ zero، sign و parity با توجه به نتيجه تغيير می کنند.
• فلگ Auxilury Carry نامعين است.


مثال.

mov AX, C1A5h
mov CL,3
shr AX, CL    ; shift 3 bit to right, ax = 1834h, CF = 1


SAR

دستورالعمل (shift arithmetic right) sar مانند دستورالعمل shr است با اين تفاوت که علامت عملوند تغيير را نمی دهد. فرم کلی آن به صورت زير است:

sar dest, count

اين شيفت برای اعداد علامتدار طراحی شده است و بيت های عملوند مقصد را به سمت راست شيفت رياضی می دهد و بيت علامت را در خودش کپی می کند.

دستورالعمل sar مشابه دستورالعمل shr بکار می رود و به همان صورت روی فلگ ها تاثير می گذارد.


مثال.

mov AX, C1A5h
sar AX, 1    ; shift 1 bit to right, ax = E0D2h, CF = 1


کاربردهای شيفت

مثال. فرض کنيد می خواهيد دو نيبل پائينی ثبات های AL و AH را با هم به صورت زير ترکيب کنيد. کد زير اين کار را انجام می دهد.

mov CL, 4
shl AH, CL
and AL, 0Fh
or AL, AH

مثال. فرض کنيد می خواهيد دو نيبل ثبات AL را از هم جدا کرده و نيمه سمت چپ را در ثبات AH و نيمه سمت راست را در ثبات AL به صورت زير قرار دهيد. کدهای زير اين عمل را انجام می دهد.

mov AH, AL
mov CL, 4
shr AH, CL
and AL, 0Fh

مثال. هر شيفت به چپ باعث دو برابر شدن عملوند می شود که سرعت بيشتری نسبت به عمل mul دارد. دستورالعمل های shl/sal برای ضرب مقادير علامت دار يا بدون علامت در توان های 2 استفاده می شود. دستور زير مقدار ثبات AX را در عدد 4 ضرب می کند.

mov CL,2
shl AX, CL

مثال. برای محاسبه 10×AX می توانيد به روش زير از دستورشيفت چپ استفاده کنيد (با توجه به اينکه 10×AX=8×AX + 2×AX).

shl AX, 1
mov BX, AX
shl AX, 1
shl AX, 1
add AX, BX

مثال. کدهای زير حاصلضرب AX×7 را محاسبه می کنند (با توجه به اينکه ax×7 = (ax×8)-ax ).

mov BX, AX
shl AX, 1
shl AX, 1
shl AX, 1
sub AX, BX

مثال. چون يک شيفت منطقی به سمت راست مقدار يک عدد صحيح بدون علامت را نصف می کند می توان برای تقسيم بر توان های 2 از آن استفاده کرد. دستورات زير خارج قسمت مقدار ثبات AX بر 8 را محاسبه می کنند.

mov CL,3
shr AX, CL

مثال. برای انجام تقسيم علامتدار بر توان های 2 از شيفت رياضی راست استفاده می شود. دستور زير مقدار ثبات AX را بر عدد 32 تقسيم می کند.

mov CL,5
sar AX, CL

مثال. توجه کنيد اگر عملوند منفی باشد نتيجه دو دستور sar و idiv متفاوت می شود. به دستورات زير دقت کنيد.

mov ax, -15
cwd
mov bx, 2
idiv            ;خارج قسمت 7- مي شود

mov ax, -15
sar ax, 1    ;خارج قسمت 8- مي شود

مثال. از شيفت رياضی راست می توانيد برای گسترش رياضی یک عدد علامتدار استفاده کنيد. به کدهای زير دقت کنيد.

; CBW معادل دستور:
mov AH, AL
mov CL, 7
sar AH, CL

; CWD معادل دستور:
mov DX, AX
mov CL, 15
sar DX, CL

البته وقتی يک دستور cbw يا cwd برای گسترش وجود دارد کسی از دو دستور استفاده نمی کند، ولی دستور شيفت اجازه می دهد که مقدار يک ثبات را در هر ثبات ديگرهم اندازه ای به طور رياضی گسترش دهيد:

; DX:BX به BX گسترش رياضی:
mov DX, BX
mov CL, 15
sar DX, CL

تبلیغات شما تبلیغات شما

دسترسی سریع

کدهای اختصاصی