نحوه درخواست برای نوشتن کد

با عرض سلام و خسته نباشید به دوستان دانشجو .

این وبلاگ برای راحتی شما دوستان ایجاد شده تا بتوانید کد های مورد نیاز خود را از آن بردارید .

یک سری از کد ها بصورت طبقه بندی شده موجود است .

برای نوشتن کدهای جدید میتونید با شماره 4442-480-0938 تماس بگیرید .

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

مثلا نوشتن کدی مثل نمونه کدهای سی و سی پلاس پلاس که موجود است بین 15 تا 20 هزار تومان است .

البته این قیمتها مربوط به سال 1392 می باشد .

نحوه واریز مبلغ به حساب برنامه نویس بصورت تلفنی گفته خواهد شد .

برای ارسال ایمیل به برنامه نویس آدرس زیر موجود است .

Majid_Ahadi@hotmail.com

در صورتی که هرگونه سوالی داشتید با شماره موبایل ذکر شده در قسمت بالا تماس بگیرید .

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

با تشکر .

1392/6/10


دل نوشته

سلام .

خيلي وقت بود سري به وبلاگ نزده بودم . بعضي از دوستان براي انجام پروژه تماس گرفته بودند كه بعضي انجام شد . دوستان اگر نظري در مورد بهتر شدن وبلاگ دارن بگن . شايد ايجاد انگيزه شد كه مطلب بيشتري بذاريم .

لاتک به درد همه نمی‌خورد، ولی شاید به درد شما بخورد

برگرفته شده از ویکی لاتک پارسی (بعضی از اتصالها درست نمی‌کنند)

مقدمه

هرکس که مدتی با مایکروسافت ورد کار کرده باشد، تا حالا به قانون‌های زیر درباره‌اش پی برده است:

۱. احتمال قفل‌کردن برنامه نسبت مستقیم دارد با اهمیت نوشته‌ای که تایپ می‌کنید.

۲. احتمال قفل‌کردن برنامه نسبت عکس دارد با زمانی که تا مهلت نوشتن باقی مانده است.

۳. احتمال قفل‌کردن برنامه نسبت مستقیم دارد با زمانی که از آخرین ذخیره‌کردن نوشته گذشته است.

از شوخی که بگذریم، من هم نوشته‌های بزرگ زیادی را با Word تایپ کرده‌ام. البته این روزها می‌توانم با اپن آفیس (OpenOffice) هم کار کنم که نرم‌افزار بسیار خوبی است و هرکاری را که بخواهم می‌کند. ولی پس از سروکله‌زدن‌های بسیار با Word، وقتی دانشجوی دکتری شدم تصمیم گرفتم Latex را هم امتحان کنم. راستش، خیلی هم به دلخواه خودم نبود؛ قرار بود مقاله‌ای را به یک مجله بفرستم و فهمیدم که آن مجله فقط نوشته‌های لاتک را می‌پذیرد. مهلت فرستادن مقاله همان روزی بود که من از فراخوان مقاله باخبر شدم! مجبور شدم پابرهنه بپرم به وسط لاتک، و خب حدس می‌زنید که اصلاً کار آسانی نبود. (نکتهٔ آموزشی: هیچ گاه یک چیز تازه را با عجله یادنگیرید!)

ولی من از رو نرفتم و کم‌کم فهمیدم که کار با لاتک آن‌قدرها هم سخت نبود. مدتی طول کشید تا یادش بگیرم، ولی برای نوشته‌های ساده‌ای که من معمولاً می‌نوشتم، چیز زیادی برای یادگرفتن نبود. خیلی خوشحالم که آن وقت‌ها دلسرد نشدم، زیرا الان اصلاً دوست ندارم نوشته‌ها و گزارش‌هایم را با چیزی جز لاتک بنویسم. البته من تنها کسی نیستم که از ترک دنیای ویزی‌ویگ (WYSIWYG) خوشحال است.

این مقاله قرار نیست خودآموز استفاده از لاتک باشد. فقط می‌خواهم کمی از خوبی‌های لاتک بگویم و این که چرا فکر می‌کنم امکانات لاتک به امکانات نرم‌افزارهای واژه‌پرداز (مثل Word) سر است.

لاتک چیست؟

در سال ۱۹۸۷، دونالد کانوت (Donald Knuth) -که یکی از مشهورترین و معتبرترین دانشمندان علوم کامپیوتر است- شروع به ساخت یک سیستم حروف‌چینی به نام تک (TeX) کرد. طراحی زبان تک پس از ده سال تمام شد (خودش در آغاز فکر می‌کرد یک سال بیشتر طول نخواهد کشید‍!). تک می‌توانست چیدمان (layout) نوشته‌ها را به طرز حیرت‌آوری تنظیم کند. البته این قدرت زیاد باعث می‌شد که خیلی هم پیچیده باشد. تا این که در دههٔ ۱۹۸۰ کسی به نام لسلی لمپرت (Leslie Lamport) مجموعه‌ای از ماکروها را طراحی کرد تا از این پیچیدگی‌ها بکاهد. کار او باعث شد تا حروف‌چینی نوشته‌ها آسان‌تر شود، و در ضمن محتوای نوشته و ظاهر آن از هم مستقل باقی بماند. این سیستم جدید لاتک (Latex) نام گرفت.

لاتک درواقع یک زبان نشانه‌گذاری (markup language) است. محتوا در یک پروندهٔ متنی نوشته می‌شود و نشانه‌گذاری‌ها به شکل فرمان‌هایی بین متن قرارمی‌گیرند و مشخص می‌کنند که هر بخش از نوشته چه‌طور نمایش یابد. مفسر لاتک آن پرونده را می‌خواند، محتوا را به شکل یک نوشته درمی‌آورد و یک پروندهٔ خروجی می‌سازد. همان طور که می‌بینید، لاتک مثل نرم‌افزارهای واژه‌پرداز معمولی نیست.

جدابودن محتوا و ظاهر نوشته

برتری بزرگ لاتک در این موضوع برای کاربران Word چندان واضح نیست، زیرا آن‌ها نمی‌دانند که این ویژگی چه‌قدر خوب است. وقتی با لاتک نوشتهٔ خود را می‌نویسید، فقط به محتوای نوشته فکر می‌کنید و ساختار متن را مستقیماً به لاتک می‌گویید؛ مثلاً می‌گویید که از این‌جا به بعد یک بخش جدید آغاز می‌شود، ولی لازم نیست به این فکر کنید که عنوان بخش چه‌شکلی باید تایپ شود.

این درست برعکس کاربران Word است که بلافاصله عنوان نوشته را انتخاب کرده و قالب‌بندی‌اش می‌کنند (مثلاً آن را بزرگ می‌کنند، یا فونتش را تغییر می‌دهند). نکته این‌جاست که این کار باید در کل نوشته با دست اعمال شود. در مقابل، لاتک برای قالب‌بندی از سبک‌های تعریف‌شده‌ای استفاده می‌کند. این سبک‌ها می‌گویند که هر بخش از نوشته چه‌طور باید نمایش یابد. اگر مثلاً خواستید فونت عنوان همهٔ بخش‌های نوشته‌تان را تغییر دهید، کافی است که تعریف آن سبک را عوض کنید تا کل نوشته خودبه‌خود به‌روز شود. خوبی این روش این است که نوشته‌تان خودسازگار و هماهنگ باقی می‌ماند.

البته Word هم این ویژگی سَبْک (Style) را دارد. ولی از آن‌جا که به‌کاربردن این ویژگی اختیاری است، کاربران معمولاً نمی‌دانند که چنین ویژگی‌ای هم اصلاً هست. لاتک مجبورتان می‌کند که ساختار منطقی نوشتهٔ خود را مشخص کنید (و کار خیلی خوبی می‌کند!) و برای همین است که می‌تواند از نوشتهٔ شما یک خروجی بسیار زیبا بسازد.

انعطاف‌پذیری

می‌توانید با لاتک هرکاری که فکرش را می‌کنید انجام دهید! در طول سالیان دراز، بسته‌های بسیار زیادی ساخته شده‌اند که ویژگی‌های لاتک را گسترش می‌دهند و بیشتر آن‌ها هم به رایگان در دسترس هستند. برای نمونه، کاربران اصلی لاتک که دانشگاهیان و پژوهشگران هستند بستهٔ Bibtex را به کار می‌برند که نوشتن و مدیریت مراجع مقالات را بسیار آسان می‌کند (من همیشه برای همکاران Wordکارم که مراجع را با دست تایپ می‌کنند افسوس می‌خورم). بسته‌های دیگری هستند که با آن‌ها می‌توانید نت‌های موسیقی بنویسید، حرکات شطرنج را نمایش دهید، و هزار کار دیگر. با توجه به سابقهٔ زیاد تک، هر کاری که با لاتک بخواهید بکنید احتمالش زیاد است که کسی قبلاً بسته‌ای برایش نوشته باشد.

تسلط بر نوشته

حتی در نوشته‌های کوتاه هم شاید شما با رفتار غیرهوشمندانهٔ Word روبه‌رو شده‌باشید. مثلاً گاهی وقت‌ها می خواهید تصویری را در پایین صفحه بگذارید و می‌دانید که تصویر در آن‌جا جا می‌شود، ولی Word نمی‌گذارد آن‌جا بگذاریدش! یا مثلاً در یک نوشتهٔ ۳۰ صفحه‌ای پر از شکل و جدول، یک بعدازظهر را صرف می‌کنید تا همه‌چیز مرتب شود؛ همهٔ تصاویر سرجای خودشان باشند و غیره. آن وقت می‌فهمید که یک جای نوشته‌تان کمی گنگ است و تصمیم می‌گیرید آن‌جا را کمی بیشتر توضیح دهید. یک جمله به متن اضافه می‌کنید، و می‌بینید که یکی از تصویرها به صفحهٔ بعد می‌پرد و یک فضای خالی بزرگ در پایین صفحه باقی می‌گذارد! بعد می‌فهمید که در یک واکنش زنجیره‌ای، همهٔ تصویرها و جدول‌هایتان تا آخر نوشته به هم ریخته‌اند! واقعاً خنده‌دار است. در مقابل، لاتک خیلی باهوش‌تر است و تصویرها و جدول‌هایتان را همیشه در جاهای معقول می‌گذارد. بنابراین اگر خواسته باشید که تصویرتان در پایین یک صفحه بماند، آن تصویر همیشه همان پایین می‌ماند!

البته با این که لاتک در جزئیات حروف‌چینی به جای شما تصمیم می‌گیرد، ولی اگر بخواهید، می‌توانید روی نمایش نوشته‌تان اختیار کامل داشته باشید.

کیفیت

به سختی می‌توان این موضوع را انکار کرد که کیفیت خروجی‌های لاتک بسیار فراتر از خروجی‌های Word است. این برتری وقتی آشکارتر می‌شود که نوشتهٔ شما پر از روابط ریاضی باشد. لاتک همچنین الگوریتم‌های بسیار بهتری برای ترازبندی متن، شکستن کلمات و غیره دارد که باعث می‌شود خروجی شما بسیار حرفه‌ای‌تر به نظر برسد.

مردم معمولاً یادشان می‌رود که حروف‌چینی یک مهارت حرفه‌ای است. حروف‌چین‌ها سال‌ها آموزش می‌بینند تا بیاموزند که یک کتاب را چگونه حروف‌چینی کنند. اما به محض این که شما برنامهٔ واژه‌پردازتان را باز می‌کنید، مرتکب اشتباهات حروف‌چینی می‌شوید. مثلاً حروف‌چین‌ها می‌دانند که خواندن سطرهایی که پهنایشان ۶۶ حرف است آسان‌تر است. به کتاب‌هایتان نگاه کنید و تعداد حرف‌ها را بشمارید! ولی تنظیم پیش‌فرض واژه‌پردازها به طور میانگین ۱۰۰ حرف در هر سطر است. حدس می‌زنم که خیلی‌ها برایشان مهم نیست، ولی اگر قرار باشد نوشته‌های بزرگ زیادی را بخوانید، برایتان مهم می‌شود.

یک مثال فوری. یک نوشتهٔ لاتک را که پیش‌تر با آن ساختار نوشته‌ها را در لاتک معرفی کرده‌بودم برداشتم. همان نوشته‌ها را در Word هم نوشتم و سبک‌های معادل Word را رویش اعمال کردم. همه‌جا از سبک‌های پیش‌فرض استفاده کردم. در Word سبکی برای چکیده وجود نداشت، برای همین فقط عنوان را پررنگ کردم. خروجی Word را با خروجی لاتک مقایسه کنید. سبک‌هایی که Word به کار می‌برد چندان خوب نیستند. البته می‌توان این سبک ها را آن‌قدر دستکاری کرد که تاحدی شبیه لاتک شوند، ولی من ترجیح می‌دهم این کار را نکنم و به جایش با همان لاتک خودم کار کنم!

بیشتر کتاب‌ها با لاتک حروف‌چینی می‌شوند. واژه‌پردازها برای این کار معمولاً آن قدر خوب نیستند- آن ها را فقط نویسندگان برای نوشتن به کار می‌برند و نوشته‌هایشان بعداً به نرم‌افزار حروف‌چینی حرفه‌ای منتقل می‌شود. البته با Word هم می‌شود کتاب‌ها را حروف‌چینی کرد، همان‌طور که با پا هم می‌شود رانندگی کرد، ولی کار عاقلانه‌ای نیست!

پایداری

یکی از دلایلی که مردم با Word مشکل دارند این است که Word در هنگام ویرایش نوشته‌های طولانی زیاد قفل می‌کند. «بازیابی نوشته‌ها» (Document Recovery) یکی از ویژگی‌های پرکاربرد Word است. نمی‌دانم چرا مایکروسافت به جای طراحی این ویژگی روی پایداری نرم‌افزارش بیشتر کار نمی‌کند! (البته پایداری لزوماً قابل‌تعمیم نیست. من فقط از تجربهٔ شخصی خودم و دوستانم حرف می‌زنم. کسی را هم نمی‌شناسم که تا حالا دچار مشکل ناپایداری Word نشده باشد، ولی معنی‌اش این نیست که چنین کسی وجود ندارد.)

از آن‌جا که لاتک بسیار کامل است، و برنامه‌نویسان بسیار باهوشی آن را ساخته‌اند، اشکالات آن بسیار کم است. حتی اگر هم اشکالی وجود داشته باشد، هیچ‌گاه پروندهٔ ورودی شما از دست نمی‌رود. ولی در Word، اگر هر ابزارش قفل کند، می‌تواند نوشتهٔ شما را نابود کند.

و در ضمن، در لاتک نیازی نیست نگران ویروس‌هایی باشید که در ماکروهای Word پنهان می‌شوند!

قیمت

در این مورد لاتک هیچ حرفی باقی نمی‌گذارد، چون رایگان است! ضرب‌المثل «هرچی بیشتر پول بدی، بیشتر آش می‌خوری» دربارهٔ لاتک صادق نیست، درست مثل بیشتر نرم‌افزارهای آزاد/متن‌باز. شما با یک نرم‌افزار بسیار قوی و پایدار روبه‌رویید که سال‌ها از رقیبانش جلوتر است.

غلط‌یاب املایی چه‌طور؟

نکتهٔ خوبی است. این یک کمبود لاتک نیست، زیرا لاتک فقط متنی را پردازش می‌کند که شما به آن می‌دهید. اما در ویرایشگر متن، بر خلاف Word، شما خط‌های قرمزی را که اشتباه‌های املایی یا دستوری را یادآوری می‌کنند نمی‌بینید. و این چیزی است که بیشتر کاربران می‌خواهند داشته باشند.

به نظر من داشتن یک غلط‌یاب دستوری چیز خوبی نیست. کسی که واقعاً برای نوشتن نیازمند غلط‌یاب دستوری است، بهتر است به جای تکیه‌کردن بر توصیه‌های به دردنخور غلط‌یاب، برود یک کتاب دستور زبان بخواند.

دوم، وجود ویژگی اصلاح خودکار اشتباه‌های املایی (auto-correct)، هرچند که چیز خوبی به نظر می‌آید، در درازمدت مفید نیست. درست است که اشتباه‌های رایج ما را برطرف می‌کند، ولی باعث می‌شود که نتوانیم از اشتباه‌هایمان بیاموزیم. اگر هربار که به جای the می‌نویسیم teh، قرار باشد Word آن را برایمان اصلاح کند، هیچ‌گاه نمی‌فهمیم که داریم اشتباه می‌کنیم. با این حال، اگر فکر می‌کنید که خیلی نیازمند این ویژگی هستید، به آسانی می‌توانید هر ویرایشگر متن آبرومندی را مجهز به غلط‌یاب املایی کنید. برنامه‌های Ispell و Aspell رایج‌ترین غلط‌یاب‌های املایی هستند که هر دو متن‌بازند و می‌توانند با ویرایشگرهای متن یک‌پارچه شوند.

فرایند یادگیری

خب پس چرا همه با لاتک کار نمی‌کنند؟ زیرا کار با لاتک نیاز به «کمی» آموزش دارد. باید کمی وقت بگذارید تا کار با لاتک را یاد بگیرید. ولی خیلی زود می‌فهمید که لاتک ارزش این زمان کم را داشت. ساده‌ترین نوشتهٔ لاتک چنین چیزی است:

% hello.tex - Hello world Latex example
\documentclass{article}
\begin{document}
Hello World!
\end{document}

این دستورات این خروجی را می‌سازند. خیلی هم پیچیده نبود، نه؟ برای آموختن مقدمات لاتک، می‌توانید راهنمای لاتک را ببنید.

پس لاتک برای چه کسانی خوب است؟

خیلی ساده، هر کسی که نوشته‌های نه‌چندان ساده می‌نویسد و از محیط ناکارامد واژه پردازها خسته‌شده، لاتک را درخواهدیافت! اگر شما کار دانشگاهی می‌کنید، احتمالاً مجبورید با لاتک کار کنید! هرکس که باید نوشته‌های مربوط به ریاضی بنویسد، نرم‌افزاری قوی‌تر و باکیفیت‌تر از لاتک پیدا نخواهد کرد. حتی دانشنامهٔ ویکی‌پدیا هم برای نوشتن رابطه‌های ریاضی در مقاله‌هایش، لاتک را به کار می‌برد.

لاتک برای آدم‌های خیلی تنبل یا آن‌هایی که تغییر را دوست ندارند خوب نیست! برای من این تغییر کاملاً سودآور بود، زیرا الان می‌توانم نوشته‌ها و گزارش‌هایم را با سرعت بیشتری بنویسم. هرکسی می‌تواند لاتک را امتحان کند و تفاوتش را ببیند و تصمیم بگیرد که چه نرم‌افزاری را برای نوشتن به کار ببرد.

منبع

آموزش لاتک


  • آشنایی با LaTeX Render

LaTeX Render ابزاری است برای تهیه ی عکس به فرمت PNG یا GIF از کد LaTeX که معمولا از آن بر روی وب برای تولید فرمولهای ریاضی استفاده می شود.

 

  • پیش نیازهای LaTeX Render

LaTeX Render برای کار، نیاز به یک توزیع LaTeX، نرم افزار Image Magic و نرم افزار GhostScript دارد.

  • LaTeX Render بر روی وب چگونه کار می کند؟

LaTeX Render می تواند بر روی هاستهایی که از PHP پشتیبانی می کنند، پیاده سازی شود. سایت رسمی LaTeX Render یک مجموعه فایل برای نصب بر روی هاستها ارایه کرده که از اینجا قابل دانلود است.

  • پیاده سازی TeX-پارسی بر روی LaTeX Render

برای پیاده سازی TeX-پارسی بر روی LaTeX Render موارد زیر نیاز است:

  1. یک هاست بر روی ویندوز(ترجیحا ویندوز XP) که حداقل ۱۵۰ مگابایت فضا داشته باشد و ترافیک مناسبی هم در ماه داشته باشد. همچنین باید از PHP پشتیبانی کرده و Cpanel مناسبی داشته باشد.
  2. نصب TeX-پارسی بر روی هاست. یک نسخه کم حجم TeX-پارسی را از اینجا دانلود کنید.
  3. نصب Image Magic و GhostScript بر روی هاست. دانلود Image Magic(تحت ویندوز) دانلود GhostScript
  4. یک مبدل یونیکد به TeX-پارسی (مثل مبدلی که آقای واحدی نوشته اند.) . دانلود فایلهای مبدل
  5. Python. دانلود یک نسخه کم حجم
  6. نصب فایلهای LaTeX Render بر روی هاست

 

  • روش کارکرد LaTeX Render بر روی TeX-پارسی

وقتی که TeX-پارسی،  GhostScript، Image Magic و Python نصب شده باشند، می توان دستورات زیر را در یک batch file قرار داد و اجرا کرد:

python undk-0.6.py %1.txt %1.tex
parsilatex %1.tex
dvips -E %1.dvi -o %1.ps
convert -density 120  -trim  %1.ps %1.png
del %1.aux %1.dvi %1.log %1.ps

فایل txt ورودی باید قالبی به شکل زیر داشته باشد:

\طبقه‌نوشتار{مقاله}
\سبک‌صفحه{خالی}
\شروع‌{نوشتار}
٪ دستورات فرمول
\پایان{نوشتار}

 

  • مشکلات پیش روی
  1. بنده نتوانستم با مبدل undk فایل txt خود را به فایلی بدون اشکال برای کامپایل توسط TeX-پارسی تبدیل کنم. اگر آقای واحدی این پست رو می خوانند لطفا یک نمونه فایل txt تبدیل شده با undk به همراه فایل خروجی tex که توسط TeX-پارسی بدون هیچ مشکلی Compile می شود ارسال نمایند.(در حالت زبان فارسی با متن انگلیسی در بین)
  2. هاست باید حتما ویندوز XP باشد. چون TeX-پارسی روی ویندوزهای دیگر مشکل دارد.
  3. باید یک Cpanel بسیار قوی داشت.

 

اگر مشکلات بالا حل شود، تازه فقط می توان امید داشت که بتوان از TeX-پارسی برای تولید عکس از کد فرمول بر روی وب بهره برد. خود فرآیند تنظیم LaTeX Render برای کار با TeX-پارسی هم چندان آسان نیست.


مقاله آموزشی لاتک

کتاب آموزش لاتک، ترجمه دکتر مهدی امیدعلی (pdf)

خود آموز لاتک و زی‌پرشین (در دست ترجمه)

The_XePersian_Workshop_Sharif_24_Esfand_1388.pdf

آموزش اسمبلی

برای دریافت فایل Word آموزش اسمبلی با ایمیل تماس بفرمائید .

هزینه 5 هزار تومان

آموزش اسمبلی بخش 6

v\:* {behavior:url(#default#VML);} o\:* {behavior:url(#default#VML);} w\:* {behavior:url(#default#VML);} .shape {behavior:url(#default#VML);}
ساختارهای حلقه تکرار در اين صفحه دستورات حلقه و نحوه پياده سازی ساختارهای حلقه های تکرار توضيح داده می شود. 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: زيربرنامه اين صفحه نگاهی به زيربرنامه ها در زبان اسمبلی دارد. چگونگی تعريف زيربرنامه، دستورات فراخوانی و برگشت از زيربرنامه و نحوه ارسال و دريافت پارامترها شرح داده خواهد شد. تعريف زيربرنامه زيربرنامه های near و far دستورات فراخوانی و بازگشت زيربرنامه ارسال و دريافت پارمترها زير برنامه (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 مثال. محاسبه مجموع سه عدد که از طريق پشته به زيربرنامه ارسال شده اند وقفه ها هر رويدادی که باعث شود CPU اجرای عادی يک برنامه را قطع کند وقفه ناميده می شود. يک برنامه نويس اسمبلی با صدور وقفه های نرم افزاری می تواند به طور موثری با دستگاه های جانبی ارتباط برقرار کند. انواع وقفه INT جدول بردار وقفه چند نمونه وقفه متعارف گاهی اوقات جريان عادی اجرای يک برنامه برای پردازش رويدادی که نياز به پاسخ سريع دارد متوقف می شود. سخت افزار کامپيوتر برای مديريت اين رويدادها مکانيسمی به نام وقفه (interrupt) را دارد. مثال. وقتی mouse حرکت می کند، سخت افزار mouse برنامه جاری را متوقف می کند تا حرکت mouse گرفته شود( برای حرکت مکان نمای mouse روی صفحه نمايش). وقتی CPU يک سيگنال وقفه را تشخيص می دهد، فعاليت جاری خود را متوقف می کند و روتين خاصی را فراخوانی می کند که روتين وقفه (interrupt handler) نام دارد. اين روتين علت وقوع وقفه را تشخيص می دهد و عکس العمل مناسب را انجام می دهد. بيشتر روتين های وقفه بعد از پايان يافتن کنترل اجرا را به برنامه متوقف شده بازمی گردانند. آنها کليه مقادير ثبات ها را به وضعيت قبل از توليد وقفه بر می گردانند. بنابراين برنامه متوقف شده به گونه ای به اجرا ادامه می دهد که هيچ اتفاقی نيافتاده است به جز اين که سيکل های CPU را از دست می دهند. وقتی دو يا چند وقفه همزمان با هم اتفاق می افتند، CPU از سيستم الويت استفاده می کند و می تواند در طی اجرای بخش بحرانی يک برنامه وقفه ها را غيرفعال کند. وقتی دارد يک روتين وقفه را اجرا می کند کليه وقفه های با الويت کمتر يا، تا زمان خاتمه اجرای روتين، غير فعال هستند. انواع وقفه 256 سطح الويت توسط پردازنده های 80x86 پشتيبانی می شود که می توان آنها را به سه گروه کلی تقسيم کرد: • وقفه های داخلی سخت افزاری • وقفه های خارجی سخت افزاری • وقفه های نرم افزاری وقفه های داخلی سخت افزاری وقفه های داخلی سخت افزاری (internal hardware-interrupts) بدليل رخ دادن وضعيت معينی که درحين اجرای يک برنامه پيش آمده توليد می شوند(مانند تقسيم بر صفر). وقفه هايی که در اثر خطا بوجود می آيد تله (trap) هم ناميده می شود. تله باعث سقط برنامه می شوند. اين وقفه ها توسط سخت افزار اداره می شوند و امکان تغيير آنها وجود ندارد. اما با وجوديکه نمی توان آنها را مستقيما مديريت کرد، اين امکان وجود دارد که از اثر آن روی کامپيوتر به نحو مفيدی استفاده شود. مثال. سخت افزار وقفه شمارنده ساعت کامپيوتر را چندبار در ثانيه فراخوانی می کند تا زمان را نگه دارد. می توان برنامه ای نوشت که مقدار شمارنده ساعت را خوانده آنرا به شکل قابل درک کاربر به صورت ساعت و دقيقه تبديل کند. وقفه های خارجی سخت افزاری وقفه های خارجی سخت افزاری (external hardware-interrupts) خارج از CPU و توسط دستگاه های جانبی ، مانند صفحه کليد، چاپگر، کارت های ارتباطی و يا کمک پردازنده توليد می شوند. دستگاه های جانبی با ارسال وقفه به CPU خواستار قطع اجرای برنامه فعلی شده و CPU را متوجه خود می کنند. آنها به پايه (maskable interrupts) INTR يا (non maskable interrupts) NMI پردازنده متصل هستند. وقفه های دستگاه ها می توانند از طريق مداری به نام PIC 8259A، که کارش منحصرا سروکار داشتن با اين نوع وقفه هاست، به پردازنده ارسال شوند. مدار (programmableinterrupt controller) PIC که توسط CPU کنترل می شود سيگنال هايش را روی پايه INTR قرار می دهد و امکان فعال و غيرفعال کردن وقفه ها و تغيير سطح الويت را تحت نظارت يک برنامه می دهد. دستورات STI و CLI می توانند برای فعال و غيرفعال کردن وقفه هایی که روی پايه INTR ارسال می شوند بکار روند که البته روی وقفه های NMI تاثيری ندارد. وقفه های نرم افزاری وقفه های نرم افزاری (software interruptions) در نتيجه دستورالعمل int در يک برنامه درحال اجرا توليد می شوند. برنامه نويس می تواند با دادن دستور int يک وقفه نرم افزاری توليد کند. بدين طريق بلافاصله اجرای برنامه فعلی را متوقف می کند و CPU را به روتين وقفه هدايت می کند. برنامه نويس از طريق وقفه ها می تواند در برنامه با وسايل جانبی ارتباط برقرار کند. استفاده از وقفه ها باعث کوتاهتر شدن کد برنامه و درک آسانتر و اجرای بهترآن می شود. روتين های وقفه نرم افزاری بخشی از سيستم عامل هستند. از اينرو وقفه های نرم افزاری را می توان به دو گروه تقسيم کرد؛ وقفه های سيستم عامل DOS و وقفه های BIOS. وقفه های DOS آسانتر استفاده می شوند اما از وقفه های BIOS که قسمتی از سخت افزار هستند کندتر هستند. DOS اين نوع وقفه ها را برای اجرای (application programming interface) API خودش استفاده می کند. بيشتر سيستم عامل های جديد مانند Windows و Unix واسطه C-based را استفاده می کنند. INT دستورالعمل (interrupt) int يک روتين وقفه را فراخوانی می کند. فرم کلی آن به صورت زير است: int n n شماره وقفه موردنظر و مقداری بين 0 تا 255 است که اجازه فراخوانی 256 روتين مختلف وقفه را می دهد. دستورالعمل int يک فراخوانی سيستمی را می سازد و شکل خاصی از دستورالعمل فراخوانی يک زيربرنامه (دستورالعمل call) است. مشکل دستورالعمل int اين است که تنها 256 روتين وقفه را می تواند پشتيبانی کند. درحاليکه DOS به تنهايی دارای بيش از 100 سرويس مختلف وقفه و BIOS بيش از هزاران سرويس وقفه است. که اين تعداد بيش از کليه وقفه هايی است که توسط اينتل رزرو شده است. برای حل اين مشکل از يک شماره وقفه برای هر دسته از سرويس های وقفه و يک شماره تابع برای تعيين سرويس موردنظر استفاه می شود. شماره تابع توسط يکی از ثبات ها (اکثرا AH) هنگام فراخوانی وقفه ارسال به روتين وقفه می شود. مثال. سيستم عامل DOS شماره وقفه 21h را بکار می گيرد. برای انتخاب يک تابع خاص، قبل از فراخوانی وقفه، کد تابع در ثبات AH قرار می گيرد. برای نمونه تابع 4Ch اين وقفه برای خاتمه برنامه و برگشت به محيط DOS فراخوانی می شود. mov AH, 4Ch int 21h جدول بردار وقفه هر سطح وقفه يک محل رزرو شده در حافظه دارد که بردار وقفه (interrupt vector) ناميده می شود. همه بردارهای وقفه در جدولی به نام جدول بردار وقفه (interruptvector table) نگهداری می شوند. اين جدول از ابتدای حافظه اصلی يعنی آدرس 0000:0000 ذخيره شده است. هر بردار وقفه 4 بايت طول دارد. دوبايت بالای آن آفست و دو بايت پايين آن سگمنت روتين وقفه را دربر می گيرند. چون 256 روتين های وقفه وجود دارد بنابراين اندازه جدول بردار وقفه 256×4=1024=1KB است. شماره وقفه به عنوان انديسی برای جدول بردار وقفه استفاده می شود. آفست روتين وقفه شماره n در آدرس n×4 و آدرس سگمنت روتين وقفه شماره n در آدرس n×4+2 جدول قرار دارد. يک ويژگی خوب اين سيستم اين است که می توان بردارها را برای اشاره به روتين ديگری تغيير داد. که اين همان کاری است که برنامه های TSR (Terminate and StayResident) انجام می دهند. در برنامه هميشه توسط شماره وقفه به يک روتين وقفه مراجعه می شود بنابراين برنامه نيازی به دانستن آدرس واقعی در حافظه ندارد و آدرس روتين وقفه هنگام اجرا توسط CPU تعيين می شود. مثال. آدرس های آفست و سگمنت روتين وقفه شماره 5 برابر با 5×4=0014h و 5×4+2=0016h می باشد. چند نمونه وقفه متعارف وقفه 21h تابع 01h. يک کليد را از صفحه کليد می خواند. کد کليد خوانده شده در ثبات AL برگردانده می شود. mov AH, 0lh int 21h mov AL, character تابع 02h. يک کاراکتر را روی صفحه نمايش نشان می دهد. کد کاراکتر در ثبات DL بايد قرار داده شود. mov AH, 02h mov DL,character int 21h تابع 09h. يک رشته کاراکتری را روی صفحه نمايش نشان می دهد. آدرس شروع رشته بايد در ثبات های DS:DX قرار بگيرد. انتهای رشته توسط کاراکتر ($) بايد تعيين شده باشد. mov AH,09h lea DX, string int 21h وقفه 10h تابع 02h. مکان نما را روی صفحه نمايش به سطر و ستون خاصی منتقل می کند. شماره صفحه در BH، شماره سطر در DH و شماره ستون در DL بايد قرار بگيرد. مختصات صفحه از نقطه 0 و 0 از گوشه بالای چپ صفحه نمايش شروع می شود. mov AH, 02h mov BH, page mov DH, row mov DL, column int 10h تابع 09h. يک کاراکتر را با رنگ معين چندبار نمايش می دهد. کد کاراکتر در AL، شماره صفحه در BH، خاصيت رنگ کاراکتر در BL و تعداد تکرار کاراکتر در CX بايد قرار بگيرد. mov AH, 09h mov AL, character mov BH, page mov BL, attribute mov CX, number int 10h برنامه اسمبلی نمايش يک رشته کاراکتری در ورودی .MODEL small .STACK 256 .DATA msg DB "Hello, World!$" .CODE MAIN:  mov ax,@data ;Initialize DS to address mov ds,ax ; of data segment lea dx,msg ;get message mov ah,09h ;display string function int 21h ;display message Exit: mov ah,4Ch ;DOS function: Exit program mov al,0 ;Return exit code value  int 21h ;Call DOS. Terminate program END MAIN ; End program/entry point برنامه اسمبلی تبديل کاراکتر ورودی از حرف کوچک به حرف بزرگ .MODEL small .STACK 256 .DATA MSG1 DB 'Enter a lower case letter: $' MSG2 DB 0Dh,0Ah,'In upper case it is: ' CHAR DB ?,'$' exCode DB 0 .CODE MAIN: mov ax,@data ; Initialize DS to address mov ds,ax ; of data segment ;print user prompt  mov ah,9 ; display string fcn lea dx,MSG1 ; get first message int 21h ; display it ;input a character and convert to upper case mov ah,1 ; read char fcn int 21h ; input char into AL sub al,20h ; convert to upper case mov CHAR,al ; and store it ;display on the next line  mov dx,offset MSG2  ; get second message  mov ah,9  ; display string function  int 21h ; display message and upper case  ;return to DOS Exit: mov ah,4Ch  ; DOS function: Exit program mov al,exCode ; Return exit code value int 21h ; Call DOS. Terminate program  END MAIN ; End of program / entry point پشته پشته يک ليست LIFO است که می تواند به عنوان محلی مناسب برای ذخيره داده های موقتی استفاده شود. پشته برای فراخوانی زيربرنامه ها، ارسال پارامترها و متغيرهای محلی هم به کار می رود. دستورات ابتدائی پشته push و pop هستند. تعريف پشته در برنامه دستورات push و pop ثبات SP پشته (stack) ناحيه ای از حافظه اصلی است که به صورت LIFO (last-in-first-oout) سازماندهی شده است. يعنی آخرين داده ای که وارد آن می شود اولين داده ای است که از آن خارج می شود. تعريف پشته در برنامه در برنامه های exe ناگزير به تعريف پشته توسط راهنمای .stack هستيد. اندازه پشته توسط راهنمای .stack تعيين می شود. اگر اندازه تعيين نشود 1KB درنظر گرفته می شود. .stack [size] مثال. پشته ای با اندازه 1024 بايت ايجاد می شود. .stack مثال. پشته ای با اندازه 256 بايت ايجاد می شود. .stack 100h نکته. اگر برنامه نويس راهنمای .stack را استفاده نمايد قصد توليد برنامه exe دارد. برای توليد برنامه های com از راهنمای .stack استفاده نمی شود و هرچه از سگمنت کد باقی بماند به عنوان پشته درنظر گرفته می شود. دستورات push و pop دستورات push و pop دستورات پايه برای استفاده از پشته هستند. برنامه نويس اسمبلی از طريق دستورات زير می تواند داده های خود را در پشته قراردهد و يا از پشته بردارد. فرم کلی دستورات به صورت زير است: push mem/reg pop   mem/reg push عملوند خود را به پشته اضافه می کند و دستور pop مقداری را از پشته حذف می کند و در عملوند خود قرار می دهد. داده ای که برداشته می شود هميشه آخرين داده ای است که اضافه شده است. عملوند دستورات push و pop نمی توانند فوری يا ثبات های CS و IP و flag باشند. مثال 1. دستورات زير يک کلمه را در پشته قرار می دهد. mov AX, 12 push AX مثال 2. دستورات زير محتوای دو متغير Value و Count را با هم تعويض می نمايد. push Value push Count pop Value pop Count مثال 3. به کمک دستورات پشته می توان محتوای يک ثبات سگمنت را در ديگری کپی کرد. push DS pop CS مثال 4. مقدار نهائی AX برابر با 1234h است. ابتدا در AX عدد 1234h ذخيره می شود سپس وقفه فراخوانی می شود. مقدار AX از پشته بازيابی می شود. mov AX,1234H push AX mov AH,09 int 21H pop AX ثبات SP ثبات های SS و SP به پشته به صورت SS:SP به پشته اشاره می کنند. در هنگام اجرای يک برنامه EXE ثبات SS توسط سيستم عامل برابر آدرس سگمنتی که پشته را در بردارد و ثبات SP برابر آفست بالای پشته می شود. يعنی آدرس جائی از پشته که داده بايد برداشته شود. پشته به صورت معکوس در حافظه رشد می کند(يعنی به سمت آدرس های کمتر). وقتی يک کلمه در پشته اضافه می شود در آدرس SS:SP ذخيره می شود و از SP دو واحد کم می شود. دستور push مقدار SP را کاهش و دستور pop مقدار SP را افزايش می دهد. هنگامی که يک کلمه در پشته push شود عمليات زير توسط CPU انجام می شود: 1. دو واحد از ثبات SP کم می شود. 2. کلمه مورد نظر در آدرس SS:SP کپی می شود. هنگامی که يک کلمه از پشته pop شود عمليات زير توسط CPU انجام می شود: 1. از آدرسSS:SP يک کلمه خوانده می شود. 2. دو واحد به ثبات SP اضافه می شود. مثال. فرض کنيد مقدار اوليه SP برابر با 1000h است. push word 1 ;1 stored at 0FFEh, SP = 0FFEh push word 2 ;2 stored at 0FFCh, SP = 0FFCh push word 3 ;3 stored at 0FFAh, SP = 0FFAh pop AX ;AX = 3, SP = 0FFCh pop BX ;BX = 2, SP = 0FFEh pop CX ;CX = 1, SP = 1000h هنگام فراخوانی زير برنامه توسط دستور call مقدار ثبات های CS و IP که به دستورالعمل بعدی اشاره می کنند در پشته ذخيره می شود. هنگامی که CPU در زير برنامه به دستور ret می رسد مقادير را از پشته حذف و به ثبات های CS و IP برمی گرداند. مثال. شکل زير وضعيت پشته را قبل و بعد از انجام دستور Call 3C10:0720 نشان می دهد.   دستورات کنترل CPU STC CLC CMC STI CLI NOP STC دستورالعمل (set carry) stc باعث يک شدن فلگ Carry می شود. فرم کلی آن به صورت زير است: stc دستورالعمل stc روی فلگ های ديگر تاثير ندارد. CLC دستورالعمل (clear carry) clc باعث صفر شدن فلگ Carry می شود. فرم کلی آن به صورت زير است: clc دستورالعمل clc روی فلگ های ديگر تاثير ندارد. CMC دستورالعمل (compliment carry) cmc باعث عکس شدن فلگ Carry می شود. يعنی اگر صفر باشد آنرا يک و اگر يک باشد آنرا صفر می کند. فرم کلی آن به صورت زير است: cmc دستورالعمل cmc روی فلگ های ديگر تاثير ندارد. STI دستورالعمل (set interrupt) sti باعث يک شدن فلگ Interrupt می شود. فرم کلی آن به صورت زير است: sti دستورالعمل sti روی فلگ های ديگر تاثير ندارد. CLI دستورالعمل (clear interrupt) cli باعث صفر شدن فلگ Interrupt می شود. فرم کلی آن به صورت زير است: cli دستورالعمل cli روی فلگ های ديگر تاثير ندارد. NOP دستورالعمل (no operation) nop هيچ عملی انجام نمی دهد. برنامه نويسی اسمبلی در اين صفحه ساختار کلی يک برنامه به زبان اسمبلی توضيح داده می شود. به نحوه تعريف متغيرها و ثابت ها، استفاده از راهنماهای اسمبلر و اسمبل کردن و اجرای برنامه نيز اشاره شده است. . ساختار يک برنامه اسمبلی شناسه ها مدل حافظه راهنماهای سگمنت ثابت ها و متغيرها برنامه اصلی اسمبل و اجرای برنامه ساختار يک برنامه اسمبلی هربرنامه اسمبلی از تعدادی خط تشکيل شده است (line oriented). هر خط اگر توضيح نباشد شامل چهار فيلد می تواند باشد: برچسب، نماد سمبليک، عملوندها و توضيحات. قالب کلی يک عبارت به صورت زير است: [label] mnemonic [operand][;comment] مثال. MovInstruction: mov AX,BX     ;this is a MOV instruction برچسب برچسب ها در برنامه اسمبلی مانند زبان های سطح بالا بکار گرفته می شوند. برچسب (label) يک فيلد اختياری است که نامی را به عبارت اختصاص می دهد وامکان مراجعه و پرش به اين دستور را از دستورات ديگر فراهم می کند. برچسب های درون سگمنت کد به علامت کولن (:) ختم می شوند و اغلب به عنوان مقصد در دستورات پرش استفاده می شوند. مثال.               mov cx, 25 Loop1:   mov ax, cx               call PrintInteger               loop Loop1 يک برچسب می تواند حداکثر 31 کاراکتر باشد. کاراکترهای معتبر برچسب عبارتند از: حروف، ارقام و کاراکترهای $ _ @ و . هستند. برچسب نبايد از کلمات کليدی باشد و با رقم شروع شود و کاراکتر نقطه تنها می تواند به عنوان اولين کاراکتر آن استفاده شود. نکته. برای خوانائی برنامه بهتر است برچسب از ستون اول آغاز شود. نکته. بعضی از نمادهای سمبليک نيازمند يک برچسب هستند. نکته. اسمبلر حساس به متن نيست. بين کاراکترهای کوچک و بزرگ تفاوتی قائل نمی شود. نماد سمبليک فيلد نماد (mnemonic) شامل يک دستورالعمل است. دستورالعمل ها به دو دسته تقسيم می شوند: دستورالعمل های زبان اسمبلی، راهنماهای اسمبلر(assembler directives). منظور از دستورالعمل های زبان اسمبلی دستورالعمل های 80x86 معرفی شده در بخش قبل هستند (مانند mov، add و ...). در زمان ترجمه برنامه اسمبلر معادل کد زبان ماشين آنها را پيدا کرده و جايگزين می کند. راهنماهای اسمبلر دستورات خاصی هستند که از دستورالعمل های معتبر 80x86 نيستند و در فايل ترجمه شده ظاهر نمی شوند بلکه هنگام ترجمه برنامه به اسمبلر مطالبی را می فهمانند (model، .stack، .data،.code، dw و end). عملوند فيلد عملوند (operand) شامل پارامترهای موردنياز دستورالعمل مشخص شده در فيلد نماد هستند. هر دستورالعمل با توجه به عملکردش ممکن است شامل هيچ، يک يا دو عملوند باشد. عملوند مکان داده مورد نياز پردازنده جهت انجام عمل خواسته شده را مشخص می کند. فيلد عملوند در راهنماهای اسمبلر معمولا شامل اطلاعات بيشتری است. مثال. nop                ;no operands -- does nothing inc AX            ;one operand -- adds 1 to the contents of AX add Word1,2 ;two operands -- adds 2 to the contents                       ; of memory word WORD1 نکته. عملوندها هرگز تنها ظاهر نمی شوند. نوع و تعداد عملوندها بستگی به دستورالعمل دارد. نکته. عملوندها با کاما از هم جدا می شود. نکته. در دستورات دو عملوندی، عملوند اول مقصد (destination) و عملوند دوم مبدا (source) ناميده می شود. توضيح فيلد توضيح (comment) اجازه می دهد برای هر خط برنامه يادداشتی را بنويسيد. فيلد توضيح هميشه با (;) شروع می شود. وقتی اسمبلر يک خط را پردازش می کند هر چيزی که با سميکولن شروع شود را نديده می گيرد. درک برنامه اسمبلی بدون توضيحات تقربا غرر ممکن است. برنامه های خوب برای هر دستور توضيحی می نويسند . مثال. ; Initialize registers    mov AX,0    mov BX,0    mov CX,0    ;CX counts terms, initially 0 نکته. هر عبارت زبان اسمبلی در يک خط ظاهر می شود. نمی توانيد چند عبارت زبان اسمبلی را در يک خط داشته باشيد. نکته. می توانيد بين خط های برنامه خط خالی قرار دهيد. خط های خالی برای جدا کردن بخش های برنامه مفيد هستند. نکته. فيلدهای مختلف می توانند در هر ستونی قرار بگيرند. هر تعداد فضای خالی می تواند فيلدها را از هم جدا کند. استفاده درست از فضای خالی در برنامه باعث خوانايی بيشتر برنامه می شود. نکته. بين فيلدهای يک عبارت حداقل يک فاصله وجود دارد. نکته. تنها فيلدی که بايد در هر دستور زبان اسمبلی وجود داشته باشد نماد سمبليک است. شناسه ها شناسه (identifier)، سمبل ، يا برچسب اسمی است که به مقدار مشخصی نسبت داده می شود. اين مقدار می تواند آفستی در يک سگمنت، يک مقدار ثابت، يک آدرس سگمنت، آفستی درون يک رکورد يا حتی عملوند يک دستورالعمل باشد. در هر صورت شناسه امکان ارائه چيزی را با نام آشنا و قابل فهمی را می دهد. نام شناسه از حروف، ارقام و کاراکترهای خاص تشکيل شده است. با محدوديت های زير: • يک شناسه نمی تواند با يک رقم عددی شروع شود. • يک اسم می تواند هر ترکيبی از حروف بزرگ و کوچک باشد. اسمبلر با حساس به متن نيست. • يک شناسه ممکن است هر تعداد کاراکتری باشد ولی تنها 31 کاراکتر اول آن استفاده می شود. اسمبلر کاراکترهای بعدی را نديده می گيرد. • کاراکترهای _، $، ? و @ ممکن است درون يک سمبل ظاهر شوند. البته کارکترهای $ و? خاص هستند و نمی توانيد شناسه را منحصرا با اين کاراکترها بسازيد. • يک شناسه نمی تواند از اسامی رزرو شده باشد. دستورالعمل های 80x86 و نام ثبات ها رزرو شده هستند. مثال. چند شناسه مجاز. Item1 Bletch RightHere Right_Here __Special $1234 @Home Dollar$ WhereAmI? @1234 .TEST SUM_OF_DIGITS مثال. بعضی شناسه های غيرمجاز. 1TooMany Hello.There $ LABEL Right Here Hi,There مدل حافظه مدل حافظه برنامه توسط راهنمای .model مشخص می شود. عملوند مقابل آن می تواند يکی از انتخاب های زير باشد: تعداد سگمنت کد تعداد سگمنت داده مدل حافظه 1 - Tiny 1 1 Small بيشتر از يکی 1 Medium 1 بيشتر از يکی Campact بيشتر از يکی بيشتر از يکی Large آرايه های بزرگتر از 64K Huge بدون سگمنت، تنها در مد محافظت شده Flat

آموزش اسمبلی بخش 7

راهنماهای سگمنت

هر برنامه شامل يک يا چند سگمنت است. هنگامی که برنامه دارد اجرا می شود ثبات های سگمنت به سگمنت های جاری اشاره می کنند. چهار سگمنت را در آن واحد می توان داشت؛ کد، داده، پشته و ‌اضافی. در مد حقيقی هر سگمنت حداکثر 64KB است. البته معمولا برنامه ها کمتر از 64KB را استفاده می کنند. اسمبلر اندازه سگمنت را بر اساس تعداد بايت های مورد استفاده سگمنت تنظيم می کند. بنابراين اگر برنامه ای برای نمونه تنها 10KB برای ذخيره داده نياز دارد سگمنت داده 10KB می شود نه 64KB.

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

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

وقتی اجرای برنامه آغاز می شود سيستم عامل دو ثبات سگمنت CS و SS را برای اشاره به کد برنامه و سگمنت پشته مقداردهی می کند. برای دسترسی به سگمنت داده ثبات ds بايد حاوی آدرس سگمنت داده باشد. قبل از دسترسی به هر داده ای برنامه بايد آدرس سگمنت را در ثبات DS ذخيره کند.

سگمنت ها در برنامه اسمبلی توسط راهنماهای segment و ends مشخص می شوند. يک سگمنت به فرم کلی زير مشخص می شود:

segmentname segment {READONLY} {align} {combine} {use} {'class'}
      
segmentname ends

segmentname شناسه ای است که نام سگمنت معين می کند. نام سگمنت برای بدست آوردن آدرس آنها توسط اسمبلر استفاده می شود. نام سگمنت بايد در راهنمای ends هم مشخص شود.

align می تواند يکی از کلمات byte، word، dword، para يا page باشد. اين پارامتر مشخص می کنند سگمنت در محدوده بايت، کلمه، کلمه مضاعف، پاراگراف يا صفحه بار شود. اگر بايت باشد سگمنت از اولين بايت آزاد بعد از آخرين سگمنت ذخيره می شود. اين فيلد می تواند حذف شود. پيش فرض پاراگراف است. پاراگراف مضربی از 16 بايت است.

فيلد combine ترتيبی را که سگمنت های هم نام در فايل مقصد توسط اسمبلر نوشته می شوند را کنترل می کند و می تواند يکی از کلمات public، stack، common يا memory باشد. نوع stack برای سگمنت های پشته و public برای بقيه سگمنت ها استفاده می شود.


مثال.

DSEG    segment
Item1   byte   0
Item2   word   0
DSEG    ends

CSEG    segment
      mov   AX, 10
      add   AX, Item1
      ret
CSEG    ends


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


مثال. دستور زير آدرس سگمنت داده را در ثبات DS قرار می دهد.

mov   AX, dseg   ;Loads AX with segment address of dseg.
mov   DS, AX      ;Point ds at dseg.


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

سگمنت ها به ترتيبی که در برنامه تعريف شده اند در حافظه بار می شوند.

ساختار کلی يک برنامه

SSeg    SEGMENT PARA
   DW 32 dup(0)
SSeg   ENDS
DSeg   SEGMENT PARA
   ;declarations
DSeg   ENDS
CSeg   SEGMENT PARA
   Main PROC FAR
      ASSUME SS:SSeg, DS:DSeg, CS:CSeg
      mov AX,DSeg
      mov DS,AX
      ...
      mov AX,4c00h
      int 21h
   Main ENDP
CSeg   ENDS
END Main

ساختار کلی برنامه با راهنماهای ساده شده سگمنت

        .MODEL small
        .STACK [size]
        .DATA
   ;declarations
        .CODE
Main:
   mov AX,@Data
   mov DS,AX
   ...
   mov AX,4c00h
   int 21h ;return to DOS
END Main


نکته. ابتدای هر برنامه اسمبلی بايد آدرس سگمنت داده در ثبات DS قرار گيرد.


مثال. برنامه first.asm برای نمايش پيغام روی صفحه.

; First.asm
;
        .MODEL small
        .STACK [size]
        .DATA
message db "Hello world, I'm learning Assembly !!!", "$"
        .CODE
main PROC
   mov AX,seg message
   mov DS,AX
   mov AH,09
   lea DX,message
   int 21h
   mov AX,4c00h
   int 21h ;return to DOS
main ENDP
END main

ثابت ها و متغيرها

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

ثابت ها

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


مثال.

123
3.14159
"Literal String Constant"
0FABCh
'A'


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

مبنا

پسوند

Binary

B يا b

decimal

D يا d يا T يا t

hexadecimal

H يا h

ثابت های رشته ای درون گيومه (") يا تک گيومه (') قرار می گيرند.


مثال. ثابت های عددی.

0F000h
12345d
0110010100b

مثال. ثابت های رشته ای.

"This is a string"
'So is this'
'Doesn''t this look weird?'
"Doesn't this look weird?"
"Microsoft claims ""Our software is very fast."" Do you believe them?"
'Microsoft claims "Our software is very fast." Do you believe them?'


ثابت نامدار (named constant) نام سمبليکی است که نشانگر مقدار ثابتی طی فرآيند اسمبلی است. ثابت ها به صورت کلی زير تعريف می شوند:

ConstantName EQU Value
ConstantName = Value

ConstantName نام ثابت است و Value مقداری است که به ثابت اختصاص داده می شود.


مثال.

One         equ 1
Minus1    equ -1
TryAgain equ 'Y'
String      equ "Hello there"
Num = 16
Size = Count * Element


نکته. علامت مساوی تنها برای مقدارهای عددی بکار می رود.

متغيرها

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

VariableName Type InitialValue|?

Type نوع متغير را مشخص می کند که می تواند يکی از نوع های جدول زير باشد. نوع هائی که اغلب مورد استفاده قرار می گيرند DB و DW هستند. InitialValue مقداراوليه متغير است. اگر نخواهيم مقدار اوليه بدهيم علامت سوال (?) می گذاريم.

تعداد بايت

نوع

1

byte/sbyte/db

2

word/sword/dw

4

dword/sdword/dd

8

qword/dq

10

tbyte/dt


مثال.

num db 25h
sum dd ?
ANum db -4

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

X dw 040Ch,10b,-13,0
Y db 'This is an array'
Z dd 10, 13, 'A','B','C'

مثال. برای تعريف يک متغير آرايه از راهنمای dup استفاده می شود.

Memory db 30 dup('$')
BigAry   dw 100 dup(?)


برنامه اصلی

دستورالعمل های برنامه در سگمنت کد قرار می گيرند. کد معمولا در زير برنامه نوشته می شود. برای پايان اجرای برنامه و بازگشت به محيط سيستم عامل تابع 4c از وقفه 21h در انتهای هر برنامه بايد فراخوانی شود. اگر کد صفر در ثبات AL ذخيره شود به معنی اينستکه برنامه با موفقيت به پايان رسيده است. در انتهای هر برنامه دستورات زير بايد اضافه شود.

Mov AX,4c00h
Int 21h

راهنمای end انتهای سگمنت کد و پايان برنامه را برای اسمبلر مشخص می کند. عملوندی که در مقابل آن ذکر می شود نقطه آغاز اجرا يا نام تابع اصلی برنامه را به سيستم عامل می گويد و باعث می شود انتقال کنترل اجرا هنگام شروع اجرای برنامه می شود. در واقع ثبات های CS و IP را تنظيم می کند. نقطه شروع الزاما بلافاصله بعد از راهنمای .code نيست. اگر اين عملوند نباشد سيستم عامل از اولين بايت سگمنت کد شروع به اجرا می کند.


اسمبل و اجرای برنامه

برای ايجاد برنامه به سه ابزار نياز است: يک اديتور متن، يک اسمبلر برای تبديل برنامه به فايل مقصد و يک لينکر برای توليد فايل اجرائی.

برنامه اسمبلی را در يک اديتور متن نوشته و با پسوند .asm ذخيره کنيد. فراموش نکنيد که حتما از يک اديتور اسکی استفاده کنيد. توسط اسمبلر (masm.exe يا tasm.exe) از فايل مبدا .asm فايل مقصد .obj را ايجاد کنيد. اسمبلر برنامه زبان اسمبلی را به کد ماشين تبديل می کند. اگر خطائی در برنامه وجود داشته باشد اسمبلر خطا را گزارش می دهد. لينکر (link.exe يا tlink.exe) از يک يا ترکيب چند فايل .obj يک برنامه قابل اجرا از نوع .exe يا .com را می سازد.


مثال. برنامه first.asm را در اديتور متن ذخيره کنيد. سپس در خط فرمان سيستم عامل، در محلی که اسبلر نصب شده است، دستورات زير را به ترتيب وارد کنيد تا فايل اجرائی first.exe ايجاد شود.

C:\masm>masm first
C:\masm>link first
C:\masm>first


مثال: در برنامه زير دو عدد با هم جمع می شود.

        .MODEL small
        .STACK [size]
        .DATA
number1 DW 0800h   ;=128
number2 DW ffebh    ;=-493
sum         DW ?            ;store result

        .CODE
begin:
   mov AX,@Data
   mov DS,AX
   mov AX,number1 ;get first number in AX
   add AX,number2  ;add AX with second number
   mov sum,AX      ;store result in Sum
   mov AX,4c00h
   int 21h
END begin

مثال: تکه برنامه زير اعداد 1 تا 10 را در آرايه ای از نوع word ذخيره می نمايد.

        .MODEL small
        .STACK [size]
        .DATA
Array DW 10 dup(?)
        .CODE
begin:
   mov AX,@Data
   mov DS,AX
   mov CX,1
   mov SI, offset Array
Forl:
   mov [SI], CX
   inc SI
   inc SI
   cmp CX,10
   je endf
   inc CX
   jmp forl
Endf:
   mov AX,4c00h
   int 21h
END begin

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

مثال: کاراکتر * را نمايش می دهد.

        .MODEL small
        .STACK [size]
        .CODE
main PROC
   mov AH,2h
   mov DL,2ah
   int 21h
   mov AX,4c00h
   int 21h
main ENDP
END main


ماکرو


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

تعريف ماکرو
کتابخانه ماکرو


ماکرو (macro) نام مخففی برای مجموعه ای از دستورالعمل ها، راهنماها يا ماکروهای ديگر است که يکبار نوشته می شود و به هر تعداد دفعات لازم قابل استفاده است.

اسمبلر هنگام ترجمه برنامه در مواجهه با نام ماکرو دستورات معادل را قرار می دهد.


تعريف ماکرو

تعريف ماکرو توسط برنامه نويس و با استفاده از راهنمای macro صورت می گيرد. فرم کلی تعريف ماکرو به شکل زير است:

MacroName MACRO [parameter1, parameter2...]
     ...
MacroName ENDM

تعريف ماکرو با راهنمای macro شروع و با راهنمای endm پايان می پذيرد. نام ماکرو قبل از هر دو راهنمای بايد يکسان باشد.

در ماکرو، برخلاف زيربرنامه، ارسال پارامتر امکان پذير است.


مثال. تعريف ماکرو ExitPgm برای خروج از برنامه و برگشت به محيط سيستم عامل.

; ExitPgm- Returns control to MS-DOS
ExitPgm MACRO
     mov AH, 4ch
     int 21h
ExitPgm ENDM

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

Position MACRO Row, Column
     push AX
     push BX
     push DX
     mov AH, 02H
     mov DH, Row
     mov DL, Column
     mov BH, 0
     int 10H
     pop DX
     pop BX
     pop AX
Position ENDM


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

ماکرو فوق به صورت زير فراخوانی می شود.

Position 8, 6


کتابخانه ماکرو

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

INCLUDE NameOfTheFile


 

زيربرنامه


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

تعريف زيربرنامه
زيربرنامه های near و far
دستورات فراخوانی و بازگشت زيربرنامه
ارسال و دريافت پارمترها


زير برنامه (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

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


 

آموزش اسمبلی بخش 5

v\:* {behavior:url(#default#VML);} o\:* {behavior:url(#default#VML);} w\:* {behavior:url(#default#VML);} .shape {behavior:url(#default#VML);} دستورات منطقی
دستورات منطقی 8086 عمليات منطقی and، or، xor و not را به صورت بيت به بيت روی عملوندها انجام می دهند. AND OR XOR NOT TEST دستکاری بيت ها AND دستورالعمل and عمل and منطقی را روی دو عملوند خود انجام می دهد و نتيجه را در عملوند اول ذخيره می کند. فرم کلی آن به صورت زير است: and dest, src طبق جدول زير عمل and روی بيت های متناظر دو عملوند انجام می شود. هر بيت نتيجه در صورتی يک است که بيت های متناظر هر دو عملوند يک باشند در غير اينصورت صفر می شود. A B A and B 1 1 1 1 0 0 0 1 0 0 0 0 دستورالعمل and به شکل های زير می تواند بکار برود: and register, register and memory, register and register, memory and register, immediate data and memory, immediate data and /AL, immediate data دستورالعمل and روی فلگ های زير تاثير می گذارد: • فلگ carry و overflow را صفر می کند. • فلگ های zero، sign و parity با توجه به نتيجه تاثير می پذيرند. فلگ zero وقتي دو عملوند در هيچ مکانی بيت مشابه نداشته باشند يک می شود. مثال mov AX, C123h and AX, 82F6h دستورالعمل and برای محاسبه سريع باقيمانده يک عدد بر توانی از 2 می تواند استفاده شود. برای پيدا کردن باقيمانده عملوندی بر مقدار 2n کافيست and عملوند با مقدار 2n-1 محاسبه شود. مثال. دستور زير باقيمانده بر عدد 8 را محاسبه می کند. and AX, 7 OR دستورالعمل or عمل or منطقی را روی عملوندهای خود انجام می دهد. فرم کلی آن به صورت زير است: or dest, src عمل or، طبق جدول زير، روی عملوندها بيت به بيت انجام می گيرد. هر بيت نتيجه دستور or در صورتی صفر است که بيت های متناظر هر دو عملوند صفر باشند در غير اينصورت يک می شود. A B A or B 1 1 1 1 0 1 0 1 1 0 0 0 دستورالعمل or مشابه عمل and روی فلگ های Carry، Zero، Sign، Overflow و Parity تاثير می گذارد. فلگ zero وقتی يک می شود که هردو عملوند صفر باشند. عملوندهای دستورالعمل or مشابه دستورالعمل and می تواند ثبات، حافظه يا داده فوری باشد. مثال. mov AX, C123h or AX, E831h XOR دستورالعمل xor عمل xor منطقی را روی بيت های دو عملوند خود انجام می دهد و نتيجه را در عملوند اول ذخيره می کند. فرم کلی آن به صورت زير است: xor dest, src هر بيت نتيجه دستورالعمل xor مطابق جدول زير تنظيم می شد. بيت نتيجه زمانی صفر است که هردو بيت عملوند مشابه هم باشند درغير اينصورت يک می شود. A B A xor B 1 1 0 1 0 1 0 1 1 0 0 0 دستورالعمل xor مشابه عمل and روی فلگ ها تاثير می گذارد. اگر هردو عملوند دستورالعمل مساوی باشند نتيجه صفر می شود و در نتيجه فلگ zero يک می شود. برای صفر کردن ثبات ها می توان از دستورالعمل xor register, register استفاده کرد که کوتاهتر از دستورالعمل mov register,0 است. مثال. mov AX, C123h xor AX, E831h NOT دستورالعمل not مکمل يک عملوند خود را محاسبه و در آن ذخيره می کند. فرم کلي آن به صورت زير است: not dest دستورالعمل not بيت های عملوند را عکس می کند؛ صفرها را به يک و يک ها را به صفر تبديل می نمايد. دستورالعمل not به يکی از دو حالت زير می تواند استفاده شود: not register not memory دستورالعمل not روی هيچکدام از فلگ ها تاثير ندارد. مثال. mov AX, C123h not TEST دستورالعمل test مشابه and است و عمل and منطقی را روی دو عملوند خود انجام می دهد با اين تفاوت که نتيجه را جائی ذخيره نمی کند. test dest, src فلگ های Carry، Zero، Sign، Overflow و Parity مشابه دستورالعمل and تاثير می پذيرند. دستورالعمل test برای بررسی يک بودن بيتی می تواند استفاده شود. مثال. دستور زير مقدار 1 را با ثبات AL به طور منطقی and می کند. اگر بيت شماره 0 ثبات AL صفر باشد نتيجه دستور صفر شده و فلگ zero برابر با يک می شود در غير اينصورت فلگ zero صفر می شود. بررسی فلگ zero بعد از اين اجرای دستور نشان می دهد که بيت صفر ثبات AL يک بوده است يا خير. test AL, 1 مثال. دستور زيرا برای بررسی بيت های 0، 2 و 8 ثبات DX می تواند استفاده شود. اگر همگی صفر باشند فلگ zero يک می شود. test DX, 105h دستکاری بيت ها از دستورات منطقی برای دستکاری بيت های معينی از عملوند مقصد بدون تاثير روی بيت های ديگر آن می استفاده می شود. برای اين منظور يک ماسک ساخته می شود. يک ماسک مقدار است که بيت های مشخصی از يک عملوند را صفر يا يک می کند بدون اينکه نغييری روی بقيه بيت های آن داشته باشد. صفر کردن بيت ها با دستورالعمل AND با دقت در جدول and می توان مشاهده کرد که نتيجه and عدد صفر با هر بيتی برابر با صفر می شود. اگر عدد يک با بيتی and شود نتيجه همان بيت می شود. از اين خاصيت برای صفر کردن بيت های انتخابی يک مقدار بدون تاثير روی بقيه بيت ها می توان استفاده کرد. مثال. درمثال زير بيت شماره 5 ثبات صفر می شود و بقيه بيت ها بدون تغيير باقی می مانند. mov AX, C123h and AX, FFDFh يک کردن بيت ها با دستورالعمل OR دستورالعمل or را می توانيد برای يک کردن بيت های انتخابی يک عدد بکار ببريد. مثال. درمثال زير بيت شماره 3 ثبات يک می شود و بقيه بيت ها بدون تغيير باقی می مانند. mov AX, C123h or AX, 8 دستورات شيفت دستورات شیفت يک رشته بیتی را به سمت راست يا چپ حرکت می دهند. توسط اين دستورات می توان روی بيت های داده کار کرد؛ داده را ادغام يا جدا کرد و عمليات محاسباتی را انجام داد. ريزپردازنده 8086 سه دستورالعمل شيفت (shl/sal، shr و sar) دارد. بخش زير هر يک از اين دستورالعمل ها را شرح می دهد. SHL/SAL SHR SAR کاربردهای شيفت عمل شيفت بيت های داده را حرکت می دهد. حرکت بيت ها می تواند به سمت چپ (به سمت بيت های با ارزش) يا راست (به سمت بيت های کم ارزش) باشد. فلگ Carry معمولا آخرین بيت شیفت داده شده که از عملوند خارج می شود را می گيرد. دو نوع شيفت وجود دارد: شيفت منطقی و شيفت رياضی. شيفت منطقی ساده ترين شيفت است که به طريق ساده ای بيت ها را شيفت می دهد. در شيفت رياضی علامت عدد حفظ می شود. مثال. يک عدد شيفت داده شده يک بايتی نشان داده شده است. توجه کنيد که بيت های جديدی که وارد می شوند هميشه صفر هستند. 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 دستورات چرخش دستورات چرخش رشته های بيتی را به صورت دايره ای حرکت می دهد اين دستورات مشابه شيفت عمل می کنند با اين تفاوت که بيتی که از يک طرف از داده خارج می شود به طور دوار از جهت ديگر وارد آن می شود. پردازنده 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 و Auxiliarycarry تاثير ندارد. اگر نيازداريد مقدار اين فلگ ها را بعد از عمل چرخش بدانيد نتيجه را با صفر مقايسه کنيد تا اين فلگ ها تنظيم شوند. مثال. 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 چند نوع دستورالعمل پرش را در اختيار می گذارد. دستور پرش بدون شرط دستورات پرش شرطی ساختار شرط دستورات برنامه پشت سر هم اجرا می شوند يعنی پردازنده دستورات را به ترتيبی که در برنامه ظاهر شده اند اجرا می کند. ساختارهای کنترلی نظير عبارات شرطی، حلقه ها و فراخوانی زيربرنامه روال اجرای برنامه را تغيير می دهد. زبان های سطح بالا ساختارهای کنترلی سطح بالا مانند دستورات 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:

آموزش اسمبلی بخش 4

v\:* {behavior:url(#default#VML);} o\:* {behavior:url(#default#VML);} w\:* {behavior:url(#default#VML);} .shape {behavior:url(#default#VML);}
برنامه Debug صفحه بعد   صفحه قبل برنامه Debug محيطی برای بررسی فايل های مقصد دودوئی و اجرائی است. برنامه امکان انجام تغييرات جزئی در يک برنامه اجرائی را فراهم می کند بدون اينکه نياز به دوباره اسمبل کردن آن باشد. دستورات Debug برنامه Debug ابزاری جهت اشکالزدائی، اجرا و تغيير برنامه ها می باشد. اين برنامه امکان نوشتن و اجرای برنامه های کوتاه اسمبلی، نمايش و تغيير محتوای حافظه و ثبات ها، تهيه ليست اسمبلی از يک برنامه اجرائی و دسترسی به پورت ها را می دهد برنامه Debug جزو فرامين خارجی سيستم عامل DOS است و همراه با سيستم عامل نصب می شود. در سيستم عامل ويندوز می توانيد آنرا در شاخه WINDOWS\system32 پيدا کنيد. به دو طريق می توان Debug را اجرا کرد. در روش اول تنها نام برنامه Debug در خط فرمان سيستم عامل وارد می شود. در روش دوم نام يک فايل اجرائی و ليست پارامترهای موردنياز آن مقابل کلمه Debug وارد می شود. در اين حالت فايل اجرائی در حافظه لود می شود و تعداد بايت های آن در ثبات های BX:CX قرار می گيرد. دستورات Debug با اجرای برنامه علامت آمادگی ( _ ) ظاهر می شود. کليه فرامين Debug در مقابل اين علامت بايد وارد شود. دستورات Debug همگی شامل يک حرف و تعدادی پارامتر هستند. اگر خطائی در گرامر دستور وجود داشته باشد Debug دستور و محل خطا را نمايش می دهد. هنگام وارد کردن دستورات می توانيد از ترکيب حروف بزرگ و کوچک استفاده کنيد. کليه فرامين Debug با کليد کنترل Control+C خنثی می شوند. فرامين در جدول زير ليست شده است: دستور عمل شرح مثال A [address] Assemble وارد کردن دستورات اسمبلی A CS:0100 C range address Compare مقايسه بخشی ازحافظه با بخش ديگر C 100 L 100 300 D [range] Dump نمايش محتويات حافظه D cs:100 110 E address [list] Enter تغيير محتويات حافظه E 100 EB F range list Fill پرکردن آدرس داده شده با مقادير معين F 04BA:100 L 100 42 G [=address [address…]] Go اجرای برنامه موجود در حافظه G cs:7550 H value value Hex حاصل جمع و تفريق دو عدد هگز H 19F 10A I value Input خواندن يک بايت از پورت مشخص شده I 2F8 L [address [drive:record record]] Load بار کردن يک فايل در حافظه L 04BA:100 M range address Move انتقال يک بلاک حافظه M CS:100 110 CS:500 N filename [filename] Name تنظيم نام فايل N file1.exe O value byte Output ارسال يک بايت به پورت خرجی O 2F8 4F Q Quit خروج از محيط Debug Q R [register-name] Register نمايش و تغيير محتويات ثبات ها R AX S range list Search جستجوی حافظه S 0FE4:100 110 41 T [=address] [value] Trace اجرای خط به خط دستورات اسمبلی T=011A:0100 1 U [range] Unassemble نمايش دستورات اسمبلی U 04BA:100 L 10 W [address [drive:record record]] Write نوشتن فايل روی ديسک W CS:100 1 37 2B ?   نمايش ليست کليه فرامين Debug ? مثال. برای ذخيره برنامه موجود در حافظه روی ديسک نام فايل و اندازه آن بايد به صورت زير مشخص شود: _N filename.Com _R BX:CX _W مثال. دستور r محتويات ثبات های CPU و دستورالعمل بعدی که بايد اجرا شود را نمايش می دهد. در نمايش محتويات ثبات ها وضعيت فلگ ها با کدهای زير مشخص می شوند: Flag Set Clear Overflow OV NV Direction DN(Decrement) UP(Increment) Interrupt EI(Enabled) DI(Disabled) Sign NG(Negative) PL(Plus) Zero ZR NZ Auxiliary Carry AC NA Parity PE(Even) PO(Odd) Carry CY NC   دستورات اسمبلي يک برنامه پيچيده از کنار هم قرار دادن دستورات ساده اسمبلی شکل می گيرد. هنگام شروع برنامه نويسی به زبان اسمبلی نيازی به يادگيری کليه دستورات نيست، بنابراين برخی از پرکاربردترين دستورات 80x86 در ادامه شرح داده خواهند شد. دستورات به صورت زير گروه بندی شده اند: 1. دستورات انتقال داده MOV, XCHG, LEA 2. دستورات گسترش داده CBW, CWD 3. دستورات رياضی    • جمع و تفريق ADD, ADC, INC, SUB, SBB, DEC    • ضرب و تقسيم MUL, IMUL, DIV, IDIV    • منفی کردن NEG    • مقايسه CMP 4. دستورات بيتی    • عمليات منطقی AND, OR, XOR, NOT, TEST    • عمليات شيفت SHL/SAL, SHR, SAR    • عمليات چرخش ROL, ROR, RCL, RCR 5. دستورات کنترل جريان برنامه    • پرش شرطی و بدون شرط    • ساختارهای حلقه تکرار LOOP    • زيربرنامه CALL, RET    • وقفه INT, IRET 6. دستورات پشته PUSH, POP 7. دستورات ورودي/خروجي IN, OUT 8. دستورات کنترل CPU STC, CLC, CMC, STI, CLI, NOP دستورات اسمبلی - دستورات انتقال داده دستورالعمل های انتقال داده مقادير را از يک محل به محل ديگر کپی می کنند. MOV XCHG LEA MOV ساده ترين دستورالعمل mov است که دارای دو عملوند است. اين دستورالعمل محتوای دومين عملوند خود را در اولين کپی می کند. فرم کلی آن به صورت زير است: mov Dest, Source دستور mov يک کپی از Source را گرفته و آنرا در Dest ذخيره می کند. محتوای Source بعد از اجرای دستور تغيير نمی کند ولی مقدار قبلی Dest رونويسی می شود. دستور mov مشابه دستور انتساب در زبان های سطح بالا است ( Dest := Source; در زبان Pascal يا Dest=Source; در زبان C). با توجه به نوع عملوندها، انواع مختلفی از دستورالعمل mov را می توان داشت. متداولترین آنها عبارتند از: mov register, register mov memory, register mov register, memory mov memory, immediate data mov register, immediate data mov AX/AL, memory mov memory, AX/AL mov segment register, memory 16 mov segment register, register 16 mov register 16, segment register mov memory 16, segment register چند موضوع مهم درباره دستور mov را باید همواره بخاطر داشت: 1. انتقال حافظه به حافظه وجود ندارد. يعنی هردو عملوند همزمان نمی توانند عملوند حافظه ای باشند. 2. عملوندها می تواند از نوع بايت يا کلمه باشند. اما هردو عملوند حتما بايد هم اندازه باشند (برای مثال دستور mov AX,BL اشتباه است). اين برای عملوند های حافظه و ثبات هم باید رعایت شود (اگر متغيری را يک بايتی تعريف کنيد و آنرا در ثبات AX منتقل کنيد اسمبلر پيغام خطا صادر می کند). 3. با اين دستور نمی توان يک داده فوری را در يک ثبات سگمنت منتقل کرد. 4. هر دو عملوند نمی توانند ثبات سگمنت باشند. 5. گونه هائی از دستور mov سريع تر و کوتاهتراز بقيه هستند. برای مثال هر دو دستور mov ax, mem و mov reg, mem داده ای را از حافظه به ثبات کپی می کنند اما دستورالعمل اول کوتاهتر و سريع تر از دومی است. 6. می توان يک مقدار فوری را در يک محل حافظه منتقل کرد. در اين حالت داده فوری به اندازه عملوند مقصد گسترش داده می شود (مگراينکه بزرگتر از مقصد باشد که خطا صادر می شود). البته اسمبلر نمی تواند اندازه عملوند حافظه را تعيين کند مگر اينکه عملوند حافظه ای به صورت يک متغير در برنامه اعلان شده باشد. برای حل اين مشکل از عملگر های byteptr و word ptr برای تعيين اندازه عملوند حافظه ای می توان استفاده کرد. مثال. دستور زيرداده فوری 10h را به اندازه يک کلمه گسترش داده و در محلی که BX به آن اشاره می کند ذخيره می کند. mov word ptr [bx], 10h مثال. دستورات زير داده فوری 40h را در ثبات سگمنت ES ذخيره می کند. ثبات AX به عنوان واسطه بکار رفته است. هر کدام از ثبات همه منظوره را می توان به جای AX بکار برد. mov AX, 40h mov ES, AX دستور mov روی هيچکدام از فلگ ها تاثيری ندارد. XCHG دستورالعمل xchg محتوای دو عملوند خود را جابجا می کند. فرم کلی آن به صورت زير است: xchg Operand1, Operand2 مقدار هردو عملوند در اثر اجرا تغيير می کند. چهار شکل خاص برای اين دستور وجوددارد: xchg register, memory xchg register, register xchg ax, register16 ترتيب علموندها اهمیت ندارد. می توانید xchg mem,reg یا xchg reg,mem را بنویسید نتیجه فرقی ندارد. اکثر اسمبلرها بطور خودکار کد کوتاهتر را انتخاب می کنند. هردو عملوند باید یک اندازه باشند. دستور xchg روی هيچيک از فلگ ها تاثیر نمی گذارد. LEA دستورالعمل (load effective address) lea برای مقداردهی اشاره گرها استفاده می شود. فرم خاص آن به صورت زیر است: lea register16, memory اين دستور آدرس موثر يک محل خاص از حافظه را درون یک ثبات همه منظوره ذخيره می کند. منظور از آدرس موثر آدرس نهائی حافظه بعد از کلیه محاسبات آدرسی است. مثال. دستور زير مقدار 1234h را در ثبات AX قرار می دهد. lea AX, DS:[1234h] دستور mov ax, immediate data هم همین عمل را انجام می دهد. تفاوت آنها در اين است که دستورالعمل lea محاسبه آدرسی و انتقال داده را همزمان انجام می دهد. مثال. دستور زير آدرس حاصل از محاسبه BP+SI+4 را درثبات AX قرار می دهد. ابتدا مقادير را بهم جمع کرده سپس در ثبات منتقل می کند. lea bx, 4[bp+si] دستورالعمل lea روی فلگ ها تاثير ندارد. دستورات اسمبلی - دستورات گسترش داده برای افزايش اندازه يک مقدار به کلمه يا کلمه مضاعف، با حفظ علامت، از دستورات گسترش استفاده می شود. دستورات گسترش بيت علامت ثبات انباشتگر(AL/AX) را بسط می دهند. CBW CWD در بعضی از محاسبات خصوصا تقسيم و ضرب نياز به افزايش اندازه داده از يک بايت به يک کلمه يا از يک کلمه به يک کلمه مضاعف است. اعداد بدون علامت با اضافه کردن صفر در سمت چپ به سادگی گسترش می يابند. ولی برای افزايش طول يک عدد علامتدار بايد بيت علامت در سمت چپ عدد تکرار شود. دستورات تبديل بيت علامت بسط می دهند. CBW دستور (convert byte to word) cbw محتوای بيت شماره هفت ثبات AL را در AX بسط می دهد. يعنی بيت شماره هفت ثبات AL را در کليه بيت های ثبات AH کپی می کند. درنتيجه مقدار رياضی بايت AL به يک کلمهAX گسترش پيدا می کند. دستور هيچ عملوندی ندارد: cbw دستور cbw روی هيچيک از فلگ ها تاثير ندارد. دستور cbw در محاسبه تقسيم يک بايتی کاربرد دارد. CWD دستور (convert word to double word) cwd محتوای بيت شماره پانزده ثبات AX را در کليه بيت های ثبات DX کپی می کند. درنتيجه مقدار رياضی AX به يک کلمه مضاعف در DX:AX گسترش پيدا می کند. دستور هيچ عملوندی ندارد: cwd دستور cbw روی هيچيک از فلگ ها تاثير ندارد. دستور cbw در عمليات تقسيم دوبايتی نقش بازی می کند. مثال. برای گسترش مقدار 8 بيتی AL به يک مقدار 32 بيتی در DX:AX دو دستور پشت سر هم نوشته می شوند. mov AL,85h cbw cwd چون بيت علامت عدد 85h يک است تبديل به عدد FFFFFF85h می شود که 16بيت پايين آن در ثبات AX و 16بيت بالای آن در ثبات DX قرار می گيرد. يعنی AX=FF85h و DX=FFFFh می شود. دستورات اسمبلی - دستورات جمع و تفريق تعدادی از دستورات 8086 که برای انجام عمليات جمع و تفريق بر روی اعداد صحيح بکار می روند عبارتند از: ADD ADC INC SUB SBB DEC NEG CMP ADD اين دستورالعمل حاصل جمع صحيح دو عملوند خود را محاسبه و نتيجه را در عملوند اول قرار می دهد. add dest, src دستورالعمل add محتوای عملوند src را با عملوند dest جمع می کند و نتيجه را در dest ذخيره می کند (dest := dest + src). دستورالعمل add به شکل های زير می تواند استفاده شود: add register, register add register, memory add memory, register add register, immediate data add memory, immediate data add AX/AL, immediate data مثال. دستور زير محتوای ثبات های AX و BX را جمع کرده و حاصل را در ثبات AX ذخيره می کند. add AX,BX فلگ های زير با توجه به نتيجه دستورالعمل add تاثير می پذيرند: • فلگ Overflow اگر يک شوددلالت بر سرريزی در محاسبات علامتدار است. • فلگ Carry اگر يک شود دلالت بر سرريزی در محاسبات بدون علامت دارد. • فلگ Sign اگر يک شود نشان می دهد که نتيجه منفی بوده است. يعنی با ارزش ترين بيت عدد يک است. • فلگ Zero اگر يک شود بيان کننده اين است که نتيجه جمع صفر بوده است. • فلگ Auxiliary Carry شامل سرريزی BCD از نيبل پايين است. • فلگ Parity با توجه به 8 بيت پايين نتيجه تغييرمی کند. اگر تعداد بيت های يک نتيجه زوج باشد اين فلگ يک می شود. و اگر تعداد فردی بيت 1 در نتيجه باشد اين فلگ صفر می شود. روی بقيه فلگ ها اثر ندارند. نکاتی که درمورد دستور mov بايد رعايت شود در مورد دستور add نيز صادق است. علاوه بر اين که با اين دستور نمی توان يک ثبات سگمنت را با مقداری جمع کرد. وقتی داده فوری با يک عملوند بايت يا کلمه جمع می شود داده فوری به اندازه عملوند گسترش پيدا می کند. چون جمع حافظه و حافظه وجود ندارد اگر بخواهيد دو متغير را با هم جمع کنيد بايد عملوندهای حافظه را در ثبات ها منتقل کنيد. مثال. يک حالت ممکن برای انجام عمل جمع متغيرها به صورت J := K + M + N + P; می تواند به شکل زير باشد: mov BX, K mov AX, M add BX, N add AX, P add AX, BX mov J, AX مثال. می توان يک ثبات را با محلی از حافظه جمع کرد. اجباری نيست که هردو عملوند ثبات باشد. mov AX, K add J, AX مثال. می توان يک مقدار ثابت را با حافظه جمع کرد. add J, 2 ADC دستورالعمل (add with carry) adc مشابه دستورالعمل add است با اين تفاوت که حاصل جمع دو عملوند و فلگ Carry را محاسبه می کند (dest :=dest+source+CF). بنابراين اگر Carry صفر باشد نتيجه مشابه add می شود. INC دستورالعمل (increment) inc يک واحد به عملوند خود اضافه می کند. شکل کلی آن به صورت زير است: inc dest اين دستورالعمل عدد 1 را با dest جمع و حاصل را در خود dest ذخيره می کند. دستور inc به شکل های زير می تواند باشد: inc register inc memory عملوند دستور می تواند ثبات يا مکانی از حافظه باشد. اندازه عملوند می تواند 8 يا 16 بيتی باشد. دستور inc فشرده تر و اغلب سريع تر از دستور add است. به استثنای فلگ Carry بقيه فلگ ها مشابه دستورالعمل add تغيير می کنند. توجه کنيد که اين دستور بر روی فلگ Carry تاثير ندارد و برای تاثير روی فلگ Carry بايد از دستورالعمل ADD استفاده شود. افزايش شمارنده حلقه و انديس آرايه يکی از متداولترين کاربردهای دستور inc است. SUB دستورالعمل (subtract) sub حاصل تفريق عملوند دوم از عملوند اول را محاسبه می کند. شکل کلی آن به صورت زير است: sub dest, src دستورالعمل sub مقدار src را از dest کم کرده حاصل را در dest ذخيره می کند. مشابه دستورالعمل add، دستور sub به صورت های زير می تواند باشد: sub register, register sub register, memory sub memory, register sub register, immediate data sub memory, immediate data sub AX/AL, immediate data دستور sub به طريق زير فلگ ها را تغيير می دهد: • اگر نتيجه صفر شود فلگ Zero يک می شود. اين در حالتی اتفاق می افتد که عملوندها با هم برابر باشند. • اگر نتيجه منفی شود فلگ sign يک می شود. • اگر سرريزی رخ دهد فلگ overflow يک می شود. • فلگ Auxiliary Carry در صورت نياز برای عمليات BCD يک می شود. • فلگ Parity با توجه به تعداد بيت های يک نتيجه تنظيم می شود. • فلگ Carry در صورت بروز سرريزی در محاسبات بدون علامت يک می شود. توجه داشته باشيد که تفريق خاصيت جابجائی ندارد. مثال. دستورات زير عمل J := J - K; را انجام می دهند. mov ax, K sub J, ax مثال. دستورات زير عمل J := K - J; را انجام می دهند. mov ax, K sub ax, J mov J, ax بعد از عمل تفريق از مقادير فلگ های Carry، Sign، Overflow و Zero می توان برای بررسی مساوی، نامساوی، بزرگتر يا کوچکتر بودن هر عملوند با ديگری استفاده کرد. جزئيات بيتشر در دستور cmp گفته خواهد شد. SBB دستورالعمل (subtract with borrow) sbb مشابه دستور sub است با اين تفاوت که حاصل تفريق عملوند دوم و CF از عملوند اول را محاسبه می نمايد(dest:=dest-src-CF) DEC دستورالعمل (decrement) dec يک واحد از عملوند خود کم می کند و حاصل را در خود عملوند دخيره می نمايد. dec dest عملوند دستور dec می تواند ثبات يا حافظه باشد. به استثنای فلگ Carry بقيه فلگ ها مشابه دستورالعمل sub تغيير می کنند. NEG دستورالعمل (negate) neg مکمل2 عملوند خود را محاسبه می کند. فرم کلی ان به صورت زير است: neg dest دستور neg حاصل تفريق تنها عملوند خود را از عدد صفر را محاسبه کرده (عملوند را منفی می کند) و نتيجه را در آن ذخيره می کند. درنتيجه اجرای دستور علامت عملوند عکس می شود. عملوند دستور neg می تواند ثبات يا محلی از حافظه باشد: neg register neg memory اين دستور روی فلگ ها به صورت زير تاثيرمی گذارد: • اگر نتيجه برابر با صفر شود فلگ Carry صفر و در غير اين صورت يک می شود. اگر عملوند صفر بوده باشد دستور اثری روی آن نمی گذارد ولی فلگ Carry را صفر می کند. منفی کردن هر مقدار ديگر فلگ Carry را يک می کند. • اگر عملوند يک بايتی و حاوی مقدار -128 باشد و يا دوبايتی و حاوی عدد -32768 باشد، منفی کردن عملوند را تغييير نمی دهد اما فلگ Overflow را يک می کند. • روی فلگ های S، P و Z مانند دستور sub اثر می گذارد. مثال. دستور زير علامت متغير J عکس می شود. neg J مثال. دستورات اسمبلی زير مکمل K را محاسبه و در J ذخيره می کند(J=-k;). mov AX, K neg AX mov J, AX CMP دستوالعمل (compare) cmp مانند دستور sub است با اين تفاوت که حاصل تفريق را ذخيره نمی کند(dest-src). نحوه کلی آن به صورت زير است: cmp dest.src به صورت های زير می تواند استفاده شود: cmp register, register cmp register, memory cmp memory, register cmp register, immediate data cmp memory, immediate data cmp AX/AL, immediate data فلگ ها مانند دستورالعمل sub با توجه به نتيجه تفريق تغيير می کنند. دستور زير را درنظر بگيريد: cmp AX, BX اين دستور حاصل AX-BX را محاسبه می کند و با توجه به حاصل فلگ ها را تنظيم می کند. فلگ ها به صورت زير تغيير می کنند و می توانند برای بررسی نتيجه مقايسه بکار برده شوند: • فلگ Zero يک می شود اگر AX=BX باشد. مساوی يا نامساوی بودن دو عملوند را مشخص می کند. • فلگ Carry وقتی يک می شود که در محاسبات بدون علامت AXBX باشد. يعنی تفريق BX از AX احتياج به رقم قرضی داشته باشد. • فلگ Sign به همراه فلگ Overflow در محاسبات علامتدار نشان می دهد کدام عملوند بزرگتر است. • دستورالعمل cmp روی فلگ های Parity و Auxiliary Carry هم تاثير دارد ولی بندرت هنگام مقايسه مورد بررسی قرار می گيرند.   به طور خلاصه برای مقايسه دو عملوند با توجه به فلگ های زير می توان نتيجه گيری کرد: عملوندهای بدون علامت فلگ ها AX = BX Z=1 AX ≠ BX Z=0 AX C=1 AX >= BX C=0 عملوندهای علامتدار فلگ ها AX = BX Z=1 AX ≠ BX Z=0 AX (S=0 and O=1)or(S=1 and O=0) AX >= BX (S=0) and (O=0) or (S=1) and (O=1)

آموزش اسمبلی بخش 3

کدگذاري ASCII

کد گذاری ASCII(American Standard Code for Information Interchange) به حروف، ارقام، علائم و کاراکترهای مختلف يک عدد باينری 7 بيتی نسبت می دهد و هشتمين بيت را 0 درنظر می گيرد. به اين صورت هر کاراکتر يک بايت را اشغال می کند.

روشن است که اين روش برای نمايش اعداد مناسب نيست، چون در فرمت باينری يک بايت اعداد 0 تا 255 را نمايش می دهد، اما با کد ASCII يک بايت تنها برای نمايش يک رقم کافی است. به همين دليل کلا اين روش برای نمايش متن در حافظه استفاده می شود.


مثال. نمايش عدد 123 با دو فرمت ASCII و باينری


نوع توسعه يافته اين سيستم شامل 8 بيت برای هر کاراکتر است و 256 حالت مختلف را شامل می شود. کدهای 0 تا 127 برای کاراکترهای استاندارد، کدهای کنترلی و ارتباطی و مقادير 128 تا 255 برای نمايش سمبل های گرافيکی و حروف يونانی هستند.


مثال. رشته "ABC123" به صورت 41h 42h 43h 30h 31h 32h نشان داده می شود.


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


مثال. کدگذاری ASCII کد 41h يا 65 را به کاراکتر A می دهد. کدگذاری Unicode کد 0041h هگز را می دهد.


نکته1. تفاوت يک حرف بزرگ با يک حرف کوچک تنها در بيت شماره 5 است؛ اين بيت در حروف بزرگ 0 و در حروف کوچک 1 است. ("m"= 01101101 و "M"= 01001101)
نکته2. ارقام 0 تا 9 کدهای 30
h تا 39h را دارا می باشند.
نکته3. کاراکترهای قابل چاپ بين 20
h تا 7Eh است.
نکته4. کاراکترهای 0 تا 1
Fh و 7Fh کاراکترهای کنترلی نام دارند که قابل رويت نمی باشند.
نکته5. کاراکتر
ESC با کد 1Bh همراه با کاراکترهای ديگر اغلب برای يک عمل خاص به دستگاه های جانبی ارسال می شود.
نکته6. کدهای 41 تا 5
Ah کاراکترهای A تا Z و کدهای 61 تا 7Ah کاراکترهای a تا z هستند.
نکته7. کاراکتر
CR و LF با کدهای 0Dh و 0Ah به ترتيب باعث حرکت مکان نما به شروع خط جاری و خط بعد می شود.


کاهش و افزايش طول داده

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

افزايش داده پيچيده تر از کاهش است. عدد هگز FF را اندازه بگيريد. گسترش آن بستگی دارد که آن را چطور تفسير کنيم. اگر آن را يک عدد بدون علامت درنظربگيريم(يعنی عدد 255)، به صورت 00FF گسترش داده می شود و اگر علامت دار باشد(يعنی 1-) به صورت FFFF. به طور کلی برای گسترش اعداد بدون علامت کليه بيت های جديد عدد گسترش يافته صفر می شوند ولی برای گسترش يک عدد علامت دار بايد بيت علامت را بسط داد، به اين معنا که.بيت های جديد بيت علامت را در خود کپی می کنند.

ساختمان کامپيوتر


اين بخش شرح مختصری پيرامون اجزاي اصلی يک سيستم کامپيوتری (حافظه و پردازنده)می دهد، که باعث می شود کاربر درک بيشتری نسبت به مفاهيمی که در طی برنامه اسمبلی با آن ها سرو کار دارد پيدا کند.

حافظه
واحدپردازش مرکزي


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

اجزای اصلی سخت افزار يک ريز کامپيوتر شامل:

• پردازنده مرکزی
• حافظه
• صفحه کليد به عنوان ورودی
• صفحه نمايش به عنوان خروجی
• يک يا چند ديسک درايو برای ذخيره برنامه ها و داده ها


حافظه

حافظه مکان ذخيره برنامه ها و داده ها با امکان دسترسی مجدد به آنها است. حافظه اصلی از ديد برنامه نويس از تعدادی بيت تشکيل شده است که قادر به نگهداری صفر يا يک است. مکانيسم دسترسی به اطلاعات درون حافظه آدرس دهی است. بيت هائی که دارای آدرس يکسان هستند را سلول حافظه (cell) می نامند. هر سلول تنها می تواند شامل يک مقدارعددی باشد. طول سلول(Lc) توسط تعداد بيت های سلول مشخص می شود. در ريز کامپيوترها طول سلول هشت بيت است که به آن بايت نيز گفته می شود. خاصيت مهم سلول آدرس پذيری است، يعنی هر سلول دارای يک آدرس منحصر بفرد است. بنابراين هر بايت درحافظه نيز دارای يک آدرس منحصر بفرد است.

اغلب حافظه ها در اندازه های بزرگتراز بايت نظير کيلوبايت (1KB=210=1,024 bytes)، مگابايت (1MB=220= 1,048,576 bytes) و گيگابايت (1GB=230=1,073,741,824 bytes) بيان می شوند. يک کامپيوتر با 32 مگابايت حافظه قادر است تقريبا 32 ميليون بايت از اطلاعات را نگهداری کند.


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


فضای آدرسی

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

اگر آدرسی m بيت طول داشته باشد بيشترين تعداد سلول های قابل آدرس دهی 2m خواهد بود.


واحد پردازش مرکزی

پردازنده يا واحد پردازش مرکزی (Central Processing Unit) يا (CPU) از واحد کنترل و واحد محاسبات و منطق ساخته شده است. وظيفه آن خواندن و نوشتن محتويات سلول حافظه، انتقال داده بين سلول های حافظه و ثبات های خاص، رمزبرداری و اجرای دستورالعمل های ذخيره شده در حافظه اصلی است.

CPU هر دستورالعمل را در يک سری مراحل اجرا می کند و برای همگام کردن سيکل اجرای دستورالعمل از يک ساعت (Clock) استفاده می کنند. ساعت در يک فرکانس ثابت پالس می زند که سرعت ساعت ناميده می شود. اين ساعت دقيقه و ثانيه را نگه نمی دارد بلکه فقط در نرخ ثابتی ضربان دارد. مدارهای الکترونيکی کامپيوتر از اين ضربان ها برای انجام صحيح عمليات خود استفاده می کنند. تعداد ضربه ها يا اصطلاحا سيکل های مورد نياز يک دستورالعمل بستگی به نسل و مدل CPU دارد.


مثال. وقتی يک کامپيوتر 1.5GHz می خريد، 1.5 GHz فرکانس اين ساعت است. يعنی در هر ثانيه 1.5 ميليارد پالس می زند (گيگاهرتز GHz يا يک ميليارد سيکل در ثانيه است).


مجموعه دستورالعمل ها

مجموعه ای از تمام دستورالعمل هائی که يک نوع پردازنده می تواند اجرا می کند مجموعه دستورالعمل (Instruction Set) ناميده می شوند که درواقع زبان ماشين آن نوع پردازنده را شکل می دهد. دستورالعمل های زبان ماشين به صورت اعداد رمز می شوند و عموما ساده هستند. زيرا زبان ماشين با اين هدف طراحی می شود که پردازنده قادر باشد مقصود دستورالعمل را سريع کشف کند تا بتواند به طور موثر آن را اجرا کند..

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


مجموعه ثبات ها

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


انواع پردازنده ها

پردازنده ها به گروه های زير دسته بندی می شوند:

1. Complex Instruction Set Computers - CISC
     • پردازنده هائی که مجموعه دستورالعمل کاملی با پشتيبانی سخت افزاری برای انواع وسيعی ازعمليات را دارند. در عمليات علمی، مهندسی و رياضی معمولا اکثر کارها را در کوتاهترين زمان انجام می دهند.
2.
Reduced Instruction Set Computers - RISC
     • پردازنده هائی که مجموعه دستورالعمل فشرده و کوچکی دارند. در کاربردهای تجاری و برنامه هائی که توسط کامپايلر ايجاد شده اند معمولا اکثر کارها را در کوتاهترين زمان انجام می دهند.
3.
Hybrid
     • پردازنده هائی که ترکيبی از روش
CISC و RISC هستند و سعی دارند تعادلی بين مزايای هر دو روش برقرار کنند.
4.
Special purpose
     • پردازند هائی که برای وظايف خاصی بهينه شده اند.
Digital signalprocessors و انواع co-processors نوع متعارف اين دسته هستند.
5.
Hypothetical
     • پردازنده هائی که هنوز وجود ندارند يا هرگز وجود نداشته اند. پردازنده هائی که در فاز طراحی هستند يا برای کارهای نظری درنظر گرفته شده اند. معروف ترين آنها
MIX است که يک پردازنده فرضی آموزش ساخته شده توسط Donald E. Knuth برای ارائه الگوريتم های کامپيوتری است.

ريزپزدازنده های اينتل


با توجه به اينکه دستورات اسمبلي 8086 در قسمت ها آينده بررسي مي شوند، در اين صفحه با نحوه آدرس دهي حافظه و ثبات هاي اين پردازنده آشنا خواهيد شد.

خانواده 80x86
آدرس دهي سگمنتي
مدهاي اجرا
مجموعه ثبات ها


خانواده 80x86

کليه کامپيوترهای شخصی IBM پردازنده ای از خانواده 80×86 دارند. پردازند های اين خانواده همگی دارای ويژگي های مشترکی ازجمله زبان ماشين پايه يکسان هستند. البته اعضای جديد ويژگي های خود را به ميزان زيادی افزايش داده اند.

تعدادی از پردازنده های اين خانواده بدين شرح می باشند:

(1979)8088,(1978)8086
     • اين
CPU ها، که از ديدگاه برنامه نويسی برابر هستند، پردازنده هائی بودند که روی اولين کامپيوترهای شخصی به کار رفته اند. دارای ثبات های 16 بيتی (AX، BX، CX، DX، SI، DI، BP، SP، CS، DS، SS، ES، IP و FLAGS ) هستند و تنها در مد حقيقی عمل می کردند. 8086 دارای گذرگاه داده 16 بيتی و گذرگاه آدرس 20 بيتی بود و بنا براين قابليت آدرس دهی تا 1 مگابايت حافظه را داشت و می توانست با داده های 8 يا 16 بيتی همزمان کار کند. 8088 با گذرگاه داده 8 بيتی به طراحان اجازه پيچيدگی کمتر و ارزانتر سيستم های کامپيوتری را می داد.
(1983)80286
     • اين پردازنده، که در کامپيوترهای شخصی کلاس
AT استفاده شد، دستورالعمل های جديدی را به زبان ماشين 8086/88 اضافه کرد. اما ويژگی اصلی آن مد محافظت شده 16 بيتی بود که در اين حالت می توانست تا 16 مگابايت حافظه را دسترسی پيدا کند. البته برنامه ها همچنان به سگمنت هائی تقسيم بندی می شدند که نمی توانستند بيشتر از 64K باشند.
(1986)80386
     • اولين پردازنده 32 بيتی که توسط اينتل معرفی شد 80386
DX بود که علاوه بر حفظ سازگاری با پردازنده های قبلی اجرای عالی داشت. اين پردازنده چند ثبات را به 32 بيتی گسترش داد (EAX, EBX, ECX, EDX, ESI, EDI, EBP,ESP, EIP) و دو ثبات جديد 16 بيتی FS و GS را اضافه کرد. دارای گذرگاه های آدرس 32 بيتی بود و در مد محافظت شده 32 بيتی می توانست تا 4 گيگابايت حافظه فيزيکی را آدرس دهی کند. برنامه ها دوباره به سگمنت ها تقسيم می شدند اما اندازه هر سگمنت می توانست تا 4 گيگا بايت باشد. نسخه 16 بيتی آن 80386 SX با گذرگاه آدرس 24 و داده 16 بيتی در 1988 بيرون آمد که تنها تا 16 مگابايت را دسترسی داشت.
(1989)80486
     • 80486
DX دارای حافظه نهان و کمک پردازنده رياضی در يک تراشه بود که حدود 50% سريع تر از 80386 بود. 80486 SX را هم معرفی شد که تنها پيوند آن با ميکروپروسسور رياضی وجود نداشت.
( 1993)
Pentium/Pentium Pro
     • پردازنده های 64 بيتی پنتيوم، که چند دستورالعمل را در يک زمان اجرا می کند، سرعت اجرای دستورالعمل ها را بالابردند. اين پردازنده ها دارای گذرگاه داده 64بيتی و گذرگاه آدرس 32 بيتی هستند. پنتيوم از نظر کارائی دوبار سريع تر از 80486 است و عمليات مميزشناور را سريع تر انجام می دهد درعين حال که کاملا با قبلی ها سازگاری دارد.
PentiumMMX
     • اين پردازنده دستورات
MMX (MultiMedia eXtensions) را به پنتيوم اضافه کرد. اين دستورالعمل ها می توانند عمليات گرافيکی معمول را سرعت ببخشند.
(1997)
Pentium II
     • اين پردازنده توسعه يافته پنتيوم است که قادر است 4 پردازنده را همزمان پشتيبانی کند و به 64 گيگابايت حافظه دسترسی دارد. درواقع يک پردازنده پنتيوم پرو همراه با دستورالعمل های
MMX است.
(1999)
Pentium III/(2002)Pentium IV
     • اين پردازنده ها تنها سرعت اجرای دستورالعمل ها را بالا بردند.


بلوک دياگرام ريزپردازنده 8086


آدرس دهی سگمنتی

پردازنده های 8086 دارای گذرگاه 20 بيتی هستند، بنابراين می تواند تا 1 مگابايت حافظه را آدرس دهد(از آدرس 00000 تا 1MB=1048575=FFFFF). اين آدرس ها به يک عدد 20 بيتی احتياج دارند. روشن است که يک عدد 20 بيتی را نمی توان در ثبات های 16 بيتی 8086 جا داد. اينتل اين مشکل را با آدرس دهی سگمنتی (segment addressing) حل کرد. سگمنت يک تکه از حافظه با اندازه 64 کيلوبايت است. يک محل از حافظه با يک آدرس سگمنت و يک آفست (offset) مشخص می شود که به صورت دو عدد 16 بيتی نشان داده می شوند. آدرس سگمنت به سگمنتی در حافظه اشاره می کند که حاوی محل مورد نظر است. هر سگمنت از يک پاراگراف می تواند شروع شود. هر پاراگراف 16 بايـت دارد، بنابراين سگمنت از آدرسی که مضربی از 16 است شروع می شود. يعنی سگمنت اول از آدرس 00000، سگمنت دوم از آدرس 00010، بعدی از آدرس 00020 و الی آخر شروع می شود. در نتيجه آدرس شروع هر سگمنت از سمت راست به صفر ختم می شود که از آن صرفنظر می شود. بنابراين آدرس سگمنت هميشه به صورت يک عدد 4 رقمی هگز نوشته می شود.
آفست فاصله بايت مورد نظر از ابتدای سگمنت را مشخص می کند. با توجه به اينکه هر سگمنت 64
KB حافظه دارد، آفست می تواند بين 0000 تا ffff باشد. بنابراين آفست نيز هميشه يک عدد 4 رقمی هگز است.


آدرس سگمنتی به صورت آفست:سگمنت نوشته می شود.


مثال 1. آدرس فيزيکی 18A3:5B27 به بايتی در سگمنت 18A30 اشاره دارد که از ابتدای اين سگمنت 5B27 بايت فاصله دارد.

مثال 2. آدرس فيزيکی 04808 می تواند توسط 047C:0048 رجوع شود.


آدرس های سگمنت-آفست يک آدرس منطقی را تعيين می کنند. برای ساختن آدرس فيزيکی 20 بيتی طبق فرمول زير محتوای آدرس سگمنت را در 16 ضرب کرده با آدرس آفست جمع می کنيم:

16× segment + offset

ضرب در 16 آسان است کافی است يک صفر در سمت راست عدد گذاشته شود.


مثال 3. آدرس فيزيکی رجوع شده توسط 047C:0048 برابر است با:

047C0+0048=04808


سگمنت ها روی هم می توانند قرار بگيرند بنابراين يک بايت، با داشتن آدرس فيزيکی منحصر بفرد در حافظه، می تواند از طريق چندين ترکيب سگمنت:آفست بدست می آيد.


مثال 4. آدرس فيزیکی 04808 می تواند توسط 047C:0048، 047D:0038، 047E:0028 يا 047B:0058 رجوع شود.


سگمنت های برنامه

سه ناحيه از سگمنت های حافظه که هر يک می توانند 64KB باشند برای يک برنامه در نظر گرفته می شوند:

1. سگمنت کد
     • شامل دستورالعمل های زبان ماشين برنامه ای که دارد اجرا می شود. اولين دستور اجرائی برنامه در ابتدای اين سگمنت قرار دارد و سيستم عامل
CPU را برای اجرای برنامه به اين محل ارجاع می دهد.
2. سگمنت داده
     • شامل داده های تعريف شده و ناحيه کاری که برنامه نياز دارد.
3. سگمنت پشته
     • شامل آدرس های برگشتی از زيربرنامه ها و داده های محلی است .

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

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


مدهای اجرا

پردازنده های 80286 به بعد دارای دو مد حقيقی (real mode) و محافظت شده (protected mode) براي اجرا هستند. تعاريف سگمنت داده شده در قسمت بالا بر اساس مد حقيقی است.

در مد حقيقی پردازنده مانند 8086 عمل می کند. ارجاع به حافظه توسط يک آفست 16 بيتی درون يک سگمنت تعيين می شود. آدرس فيزيکی 20بيتی طبق فرمول آفست + 16×سگمنت بدست می آيد. به اين طريق تا يک 1MB حافظه قابل آدرس دهی است. اما در هر لحظه فقط تا 64KB را می توان آدرس داد. در اين حالت يک برنامه به هر آدرسی از حافظه دسترسی دارد حتی حافظه برنامه های ديگر که باعث می شود اشکالزدائی و امنيت بسيار دشوار بشود.

کليه برنامه های تحت DOS در مد مجازی اجرا می شوند.

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

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


مجموعه ثبات ها

پردازنده 8086 دارای 14 ثبات 16 بيتی با کاربردهای متفاوت است. اين ثبات ها را می توان به صورت زير گروه بندی کرد:

1. ثبات های همه منظوره : AX، BX، CX و DX
2. ثبات های ايندکس :
SIو DI
3. ثبات های آدرسی :
BP،SP و IP
4. ثبات های سگمنت :
CS، DS، SS و ES
5. ثبات های وضعيتی :
Flag


دياگرام ثبات هاي ريزپردازنده 8086


ثبات های همه منظوره

CPU اوليه 8086 با چهار ثبات همه منظوره طراحی شد که در دستورات محاسباتی و ورودی/خروجی استفاده می شوند. هرکدام از اين ثبات ها يک يا چند وظيفه خاص هم دارند.

ثبات های همه منظوره می توانند به صورت 8 يا 16 بيتی استفاده شوند.هر کدام از آنها از دو بايت تشکيل شده اند؛ بايت سمت چپ را Low Order و سمت راست را High Order می نامند.

AX

Accumulator Register

همه منظوره ترين ثبات است و معمولا برای هر کاری از جمله عملياتورودی/خروجی،رشته ای و محاسباتی به کار می رود از دو جزء AL و. AH تشکيل شدهاست

BX

Base Register

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

CX

Count Register

بعنوان شمارنده در کنترل تعداد دفعات تکرار در دستور حلقه استفادهمی شود. دارای دو قسمت CL و CH است

DX

Data Register

در اعمال ورودی/خروجی و عمليات ضرب و تقسيم استفاده می شود. دارایدو بخش DL و DH است

ثبات های سگمنت

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

CS

Code Segment

شامل آدرس شروع سگمنت کد، که به CPU می فهماند دستورالعمل هایبرنامه در کجا قرار دارند

DS

Data Segment

شامل آدرس شروع سگمنت داده که به پردازنده می فهماند داده ها وفضای کاری در کجا قرار دارد

SS

Stack Segment

آدرس شروع سگمنت پشته را در خود ذخيره می کند

ES

Extra Segment

آدرس شروع سگمنت اضافی، سگمنت دادهدوم

ثبات های ايندکس

دو ثبات 16 بيتی ايندکس وجود دارد که اغلب به عنوان اشاره گر به همراه DS به کار می روند تا به داده های موجود در سگمنت داده دسترسی شود. اما می توانند به همان منظورهای ديگر، مانند ثبات های همه منظوره، هم استفاده شود؛ گرچه نمی توانند به دو بخش 8 بيتی تجزيه شوند.

SI

Source Index

برای آدرس دهی و در عمليات رشته ای بعنوان مبدا استفاه میشود

DI

Destination Index

برای آدرس دهی و در عمليات رشته ای بعنوان مقصد استفاه میشود

ثبات های اشاره گر

ثبات های اشاره گر نگهدارنده بخش آفست در آدرس دهی هستند و همراه با يکی از ثبات های سگمنت به محلی از حافظه اشاره دارند. طبق پيش فرض ثبات های همه منظوره و ايندکس همراه با DS و ثبات های پشته همراه با SS و IP همراه با CS استفاده می شوند.

IP

Instruction Pointer

همراه با ثبات CS به دستورالعمل بعدی که بايد توسط CPU اجرا شوداشاره می کند

SP

Stack Pointer

آفست مکانی از سگمنت پشته که عمل قرار گرفتن داده در پشته صورت میگيرد. به عبارت ديگرSS:SP به بالای پشته اشاره دارد

BP

Base Pointer

برای دسترسی به متغيرهای محلی که در پشته قرار دارند استفاده میشود

ثبات FLAGS

فلگ ها اطلاعاتی درباره نتايج اجرای دستورالعمل قبلی را نگه می دارند. اين نتايج به صورت بيت های مجزا در ثبات وضعيت FLAGS ذخيره می شوند. 9 بيت از 16 بيت اين ثبات برای تعيين وضعيت فعلی ماشين و نتيجه اجرای دستورالعمل به کار می روند. هر کدام از اين بيت ها هم فلگ ناميده می شوند زيرا می توانند 1 (Set) يا 0 (Not Set) باشند. بسياری از دستورالعمل ها وضعيت اين بيت ها را تغيير می دهند. گرچه کليه دستورات روی فلگ تاثير نمی گذارند.
اين ثبات فاقد آدرس است و به طور مستقيم توسط برنامه نويس قابل دسترس نمی باشد.

CF

Carry Flag

محتوی رقم نقلی بوجود آمده از باارزش ترين بيت در عمليات محاسباتیيا چرخش

PF

Parity Flag

برای کنترل صحت انتقال داده. اگر صفر باشد تعداد بيتهای انتقالیفرد است و اگر يک باشد زوج است

AF

Auxiliary Carry

محتوی رقم نقلی از بيت سوم به چهارم در يک بايت است. در عمليات BCD کاربرد دارد

ZF

Zero Flag

اگر نتيجه عمليات محاسباتی صفر باشد اين بيت 1 است در غير اينصورتصفر است

SF

Sign Flag

در صورت منفی بودن نتيجه عمليات اين بيت 1 است در غير اينصورت صفزاست

TF

Trap Flag

برای اجرای دستورالعمل به صورت دستور به دستور اين بيت بايد 1باشد

IF

Interrupt Flag

اگر 1 باشد وقفه فعال است و اگر صفر باشد وقفه غير فعال است يعنیسيستم وقوع وقفه را ناديده می گيرد

DF

Direction Flag

اگر 1 باشد عمل مقايسه يا انتقال داده از سمت راست به چپ صورت میگيرد در غير اينصورت از چپ به راست

OF

Overflow Flag

اگر در باارزش ترين بيت سرريزی وجود داشته باشد اين بيت يک می شود. (توضيحات بيشتر درمحاسبات مکمل 2داده شدهاست)


آموزش اسمبلی بخش 2

سيستم عددی هگز (Hexadecimal)

 

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

اعداد هگز مبنای 16 را استفاده می کنند و از 16 رقم (0-15) تشکيل شده اند. برای نمايش ارقام بعد از 9 از حروف A تا F استفاده می شود. به عبارت ديگر 16 رقم هگز شامل 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F است که حروف A-F ارقام 10-15 را نشان می دهند (A=10، B=11، C=12، D=13، E=14 وF=15).

هر رقم هگز معادل چهار بيت باينری است. يک عدد چهار بيتی يک نيبل (Nibble) نام دارد. پس هر رقم هگز معادل يک نيبل است. دو نيبل يک بايت (Byte) را می سازد بنابراين هر بايت می تواند دو رقم هگز را نشان بدهد. مقدار يک بايت می تواند از 00000000 تا 11111111 باينری، 00 تا FF در هگز و 0 تا 255 در دسيمال باشد.


تبديل هگز به اعشاری

ارزش هر رقم هگز با توانی از 16 مشخص می شود. برای تبديل اعداد از مبنای 16 به 10 هر رقم عدد در ارزش مکانی اش ضرب می شود.


جدول توان های 16 در مبنای 10

جدول اعداد 0 تا 15 به صورت باينري و هگز


مثال. تبديل عدد 3BA4h به مبنای 10.

Hex : 3BA4
Decimal: 3×16^3 + 11×16^2 + 10×16^1 + 4× 16^0
     = 3×4096 + 11×256 + 10×16 + 4×1
     = 15268


تبديل اعشاري به هگز

برای تبديل دسيمال به هگز مانند باينری تقسيم های متوالی بر 16 انجام می شود.

مثال. تبديل عدد 589 به هگز

عدد

خارج قسمت

باقيمانده

589 ÷ 16

36

13

36 ÷ 16

2

4

2 ÷ 16

0

2

با قرار دادن باقيمانده های تقسيم از پايين به بالا عدد باينری 24D بدست می آيد.


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

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


مثال. تبديل عدد 160794h به باينری.

Hex: 1 6 0 7 9 4
Binary: 0001 0110 0000 0111 1001 0100

توجه کنيد که صفرهای ابتدای چهار بيت اهميت دارند. اگر اين صفرها برای ارقام ميانی قرار نگيرند حاصل اشتباه است.


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

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

مثال. تبديل عدد 101100000011110010100b به هگز

Binary: 0001 0110 0000 0111 1001 0100
Hex : 1 6 0 7 9 4
= 160794h

جمع اعداد در هگزادسيمال

چند جمع ساده در مبنای 16.

7 + 3 = A
6 + 7= D
F + 1 = 10
10 + 30 = 40
F + F = 1E
38 + 18 = 50
FF + 1 = 100

جمع دو عدد هگز


1


1



7

E

C

6

+

3

4

0

A

 

B

2

D

0


6 + A = 6 + 10 = 16 => 10h
C + 0 + 1 = 12 + 0 + 1 = 13 => Dh
E + 4 = 14 + 4 = 18 => 12h
7 + 3 + 1 = 11 => Bh

نمايش داده ها


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

واحدهای اطلاعاتی
روش های نمايش داده ها
نمايش اعداد صحيح
کدگذاری ASCII
کاهش و افزايش طول داده


واحدهای اطلاعاتی

هر رقم در يک عدد باينری يک بيت (bit) ناميده می شود. بيت کوچکترين واحد اطلاعاتی در کامپيوتر است.

بيت ها به گروه های بزرگتری سازماندهی می شوند؛ در کامپيوترهای امروزی هر هشت بيت يک بايت (Byte) درنظر گرفته می شود که کوچکترين مکان آدرس پذير حافظه است (که می تواند متفاوت از مقدار حافظه واکشی شده درهربار مراجعه باشد). يک بايت می تواند حاوی يک دستورالعمل ماشين، يک کاراکتر، يا يک عدد باشد.

يک نيبل (nibble) نيمه يک بايت يا چهار بيت است.

نوع بزرگتر ذخيره سازی يک کلمه (word) است که روی پردازنده های اينتل 2 بايت (16 بيت) است. يک کلمه طول پيش فرض داده است که توسط طراح پردازنده انتخاب شده است و منعکس کننده برخی نکات سخت افزاری نظير گذرگاه های درونی و بيرونی است. کاميپوترهای شخصی اوليه با پردازنده های اينتل دارای عملوندهای 16 بيتی بودند به همين دليل کلمه به صورت 16 بيتی تعريف شد. در پردازنده های ديگر طول کلمه الزاما 2 بايت نيست.

يک کلمه مضاعف (doubleword) چهار بايت يا 32 بيت طول دارد و يک کلمه چهارگانه (quadword) دارای هشت بايت يا 64 بيت است.


endian

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

1. Big-endian
    • داده ها را به ترتيب طبيعی خودشان ذخيره می کند. بايت با ارزش در کمترين آدرس قرار می گيرد. اکثر پردازنده های
RISC و Motorola 68300 از اين دسته هستند.
2.
Little-endian
    • بايت با ارزش کمتر در آدرس های پائين تر حافظه ذخيره می شود. پردازنده های
Intel x86 و Pentium از اين نمونه هستند.
3.
Bi-endian
    • پردازنده هائی مانند
Motorola/IBM PowerPC می تواند در مد big-endian يا little-endian تحت کنترل نرم افزار کار کند.


مثال. داده چهار بايتی هگزAABBCCDD را درنظر بگيريد. نحوه تخصيص حافظه به اين داده در دو فرمت endian به صورت زير نشان داده شده است. مشاهده می شود که اين دو فرمت عکس يکديگر هستند.


روش های نمايش داده ها

اطلاعات معمولا به دو صورت استفاده می شوند: داده عددی ( صحيح و مميرشناور) و داده حرفی. نحوه نگهداری اطلاعات در حافظه را نمايش داده می گويند. روش های نگهداری داده ها بسته به نوع آنها متفاوت است.


نمايش اعداد صحيح (Integer)

اعداد صحيح باينری به دو شکل ديده می شوند:

1. اعداد صحيح بدون علامت (unsigned Integer) که شامل اعداد صحيح غير منفی هستند.
2. اعداد صحيح علامت دار (
signed Integer) که می توانند مثبت يا منفی باشند.


نمايش اعداد صحيح بدون علامت

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


جدول تعداد بيت ها و محدوده مقادير ممکن داده های عددی صحيح بدون علامت


نمايش اعداد صحيح علامت دار

اعداد صحيح علامت دار ممکن است مثبت يا منفی باشند. برای تشخيص علامت عدد يکی از بيت ها را به بيت علامت اختصاص می دهند. سه تکنيک برای نمايش علامت عدد وجود دارد که در نمايش اعداد صحيح علامت دار در حافظه استفاده می شده اند. در کليه اين روش ها با ارزش ترين بيت (سمت چپ ترين بيت) را به عنوان بيت علامت (sign bit) درنظر می گيرند. اگر اين بيت 0 باشد عدد مثبت و اگر 1 باشد عدد منفی است.

روش های نمايش اعداد صحيح علامت دار عبارتند از:

1. علامت مقدار
2.
مکمل 1
3. مکمل 2


روش نمايش مکمل2

دو روش اول در کامپيوترهای اوليه به کار می رفتند. کامپيوترهای امروزی روش سوم به نام نمايش مکمل 2 را استفاده می کنند. مکمل2 يک عدد در دو مرحله بدست می آيد:

1- پيداکردن مکمل1 عدد
2- اضافه کردن 1 واحد به نتيجه مرحله اول


مثال. برای بدست آوردن مکمل2 عدد 56 ابتدا مکمل1 آن محاسبه سپس يک واحد به آن اضافه می شود.

56 = 00111000b
1’s complement = 11000111
2’s Complement = 11000111+1
     =11001000


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


مثال. نمايش عدد +1116 به فرم مکمل2 در يک کلمه به صورت زير نمايش داده می شود.

+1116 = 0000 0100 0101 110b =045ch

مثال: نمايش عدد -97 به فرم مکمل2 در يک بايت

Decimal: -97
Binary: 01100001b
1’Complement: 10011110
2’Complement: 10011111
     = 9fh


عمل مکمل گيری مانند عمل منفی کردن است، به همين دليل با گرفتن مکمل2 از يک عدد منفی به عدد مثبت مطابق آن می رسيم و برعکس.


مثال. بنابراين عدد 11001000 نمايش عدد -56 در مثال اول است.


برای اعداد مثبت بيت علامت بايد صفر باشد يعنی آخرين رقم هگز عدد بين 0 تا7 خواهد بود. عدد منفی به بيت علامت 1 ختم می شود و می تواند ارقام بين 8 تا f را در آخرين رقم هگز داشته باشد. وقتی علامت عدد مکمل2 بدست آمد پيدا کردن عدد در مبنای 2 مشکل نيست برای اعداد مثبت کافی است به مبنای 10 برده شود و اعداد منفی ابتدا مکمل2 گرفته می شود سپس به مبنای 10 برده می شود.

مثال. 0d43h يک عدد مکمل 2 با طول 16 بيت است که معادل مبنای 10 آن عدد +3395 است.

مثال. b2ebh نمايش يک عدد صحيح در سيستم مکمل 2 است معادل آن در مبنای 10 به صورت زير بدست می آيد.

b2ebh =1011001011101011b
2’s Compl. =0100110100010101
    =19733 ==> -19733


روش مکمل2 اشکالات روش های قبل را برطرف می کند و تنها يک نمايش برای صفر وجود دارد و اين محاسبات مکمل2 را ساده تر از روش های قبلی می کند. جمع و تفريق نمايش مکمل 2 همانند اعداد باينری انجام می شود. نمايش مکمل دو باعث می شود اعداد منفی يکی بيشتر از اعداد مثبت باشند(وقتی همه بيت ها 1 است).

در روش مکمل2 اعداد قابل نمايش با طول m بيت در بازه [-(2m-1),2m-1-1]قرار می گيرند.


جدول تعداد بيت ها و محدوده مقادير ممکن داده های عددی صحيح علامت دار به روش مکمل2


نکته 1. تعداد بيت های عدد قبل از عمل مکمل گيری حتما بايد برابر طول نمايش باشد. در غير اينصورت عدد حاصل اشتباه است.
نکته 2. اگر طول بيت ها برابر با طول نمايش باشد و نتوان آنرا افزايش داد حاصل اشتباه می شود.
نکته 3. از مبنای 16 برای ساده تر بيان کردن اعداد مبنای 2 استفاده می شود.


محاسبات در مکمل2


آموزش اسمبلی بخش اول

آموزش زبان اسمبلی

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

مقدمه


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

مقايسه زبان اسمبلی و زبان های سطح بالا
زبان ماشين
زبان اسمبلي چيست؟
اسمبلر
هدف از یادگيری زبان اسمبلی


مقايسه زبان اسمبلی و زبان های سطح بالا

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

1. زبان های سطح بالا
    • مانند
C++، Pascal، Java و Visual Basic.
2. زبان های سطح پايين
    • زبان ماشين
    • زبان اسمبلی

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

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


زبان ماشين

هر خانواده ای از پردازنده ها دارای مجموعه ای از دستورالعمل های منحصر بفرد است که زبان ماشين ناميده می شود. مجموعه دستورالعمل های يک پردازنده (Instruction Set) مجموعه ای از اعداد دودوئی است که ماشين می تواند آنها را درک و اجرا کند. هر نوع CPU تنها زبان مخصوص خود را درک می کند و دارای مفسری بنام microprogram است که دستورات زبان ماشين را به سيگنال های سخت افزاری تفسير و ترجمه می کند.


مثال 1. اعداد دودئی زير يک دستورالعمل ماشين اينتل است که عدد 5 را در ثبات AL قرار می دهد.

1011 0000 0000 0101

مثال 2. دستور زير ثبات های EAX و EBX را جمع کرده و حاصل را در ثبات EAX ذخيره کند.

0000 0011 1100 0011


هر دستورالعمل زبان ماشين شامل کد منحصر بفردی دارد که کدعملياتی (OperationCode) يا Opcode ناميده می شود. Opcode هميشه در ابتدای دستورالعمل قرار می گيرد. اکثر دستورات شامل داده هم هستند که توسط دستورالعمل استفاده می شود و عملوند (Operand) نام دارند.

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


زبان اسمبلی چيست؟

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

يک برنامه اسمبلی مانند برنامه های سطح بالا به صورت text نوشته می شود. هر دستورالعمل زبان اسمبلی يک نمايش نمادی (يک کد الفبائی کوتاه) از يک دستورالعمل ماشين است، که به اين صورت معنی دستور واضح تر از کد زبان ماشين می شود.


مثال 1. کلمه mov نمادی برای عمل انتقال داده است. دستور اسمبلی زير جمع ثبات AL و عدد 5 را نشان می دهد.

mov AL,5

مثال 2. کلمه add يک نماد برای دستورالعمل جمع است. دستور جمع ثبات های EAX و EBX به صورت زير نوشته می شود.

add EAX, EBX

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

مثال 3. دستوری که عملوندی ندارد و فلگ carry را صفر می کند.

clc

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

inc AX

مثال 5. دستور جمع مقدار متغير Count با محتوای ثبات به صورت زير است.

mov AX,Count


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

mnemonic operand(s)

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

• ثبات. عملوندهائی که مستقيما به محتوای ثبات های پردازنده مراجعه می کنند. مانند ثبات AL در مثال 1.
• متغير يا حافظه ای. عملوندهائی که به داده ای در حافظه اشاره دارند. مانند متغير
Count در مثال 5.
• فوری. اين عملوندها مقادير ثابتی هستند که در داخل دستورالعمل قرار می گيرند. در مثال 1عدد 5 يک عملوند فوری است.
• ضمنی. عملوندهائی که صريحا در دستور ذکر نمی شوند. در مثال 4عدد يک با ثبات
AL جمع می شود. عدد يک عملود ضمنی است.


اسمبلر

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

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

محبوب ترين اسمبلرها برای پردازنده ها ی خانواده اينتل عبارتند از:

• ماکرواسمبلر Microsoft’s Assembler MASM
• توربو اسمبلر
Borland’sAssembler TASM
• و
ASM86

برنامه ديگری که برای رديابی اجرای برنامه و بررسی محتوای حافظه کاربرد دارد ديباگر(Debugger) است که استفاده از آن بهترين راه برای يادگيری برنامه های اسمبلی و روند اجرای آنهاست. ديباگر برنامه ای است که اجازه بررسی ثبات ها و حافظه را بعد از اجرای هر دستور برنامه می دهد و خصوصا برای تست برنامه های اسمبلی مفيد است.

برنامه Debug از جمله ساده ترين ديباگرهاست که توسط MS-DOS عرضه شده است. CodeView همراه با ميکروسافت اسمبلر می آيد که اجازه می دهد کد منبع برنامه ها، بلاک های حافظه و ثبات ها را مشاهده کنيد. Turbo Debugger بورلند هم به همين صورت است.

يک برنامه ديگر همراه با اسمبلر برنامه لينکر(Linker) است که فايل های مجزای توليد شده توسط اسمبلر يا کامپايلر را به يک برنامه اجرائی تبديل می کند. برنامه Link که همراه فايل های MS-DOS می باشد يکی از متداولترين برنامه های لينکر می باشد.


هدف از يادگيري زبان اسمبلي

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

• زبان اسمبلی وسيله خوبی برای يادگيری نحوه کار کامپيوتر، کامپايلرها و زبان های سطح بالا است و به درک عميق تر معماری کامپيوتر، مفاهيم سيستم عامل، نمايش داده ها و دستگاه های سخت افزاری کمک می کند که دانستن آنها باعث می شود برنامه نويس از عهده اشکالزدائی و رفع مسائل برنامه نويسی در سطح بالا بهتر برآيد و نرم افزارهای پربارتری را در زبان های سطح بالا مانند ++C پياده سازی کند.
• برنامه های اسمبلی سريع تر، کوچکتر و با توانائی های بيشتر از زبان های ديگر هستند و معمولا حافظه و زمان اجرای کمتری را نياز دارد. گاهی نوشتن کد در اسمبلی سريعتر و کوتاهتر از کدکامپايل شده می شود. يک برنامه ويژوال بيسيک می تواند زيربرنامه های
DLL نوشته شده در زبان اسمبلی را برای افزايش سرعت برنامه در حالات بحرانی فراخوانی کند.
• برنامه های اسمبلی می توانند براحتی از محدوديت های موجود در زبان های سطح بالا عبور کنند و کنترل بيشتری نسبت به نيازمندی های سخت افزاری خاص ارائه دهند. برخی از اعمال در زبان های سطح بالا دشوار يا غير ممکن است، مانند ارتباط با سيستم عامل يا دسترسی مستقيم به کنترلرها. يک برنامه نويس مجرب می تواند با نوشتن کد بيشتر راهی برای گذشتن از اين محدوديت ها پيدا کند اما خوانائی برنامه کاهش پيدا می کند. زبان اسمبلی، در مقابل، محدوديت های کمی دارد و تقريبا همه چير را به نظر برنامه نويس واگذار می کند.

اين دلايل نشان می دهند که فراگرفتن اسمبلی می تواند مفيد باشد حتی اگر هيچوقت با آن برنامه ای نوشته نشود.

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

البته زبان برنامه نويسی C کيفيت منحصر به فردی در عرضه کردن مصالحه بين ساختار سطح بالا و جزئيات سطح پايين دارد. اکثر کامپايلرهای C توانائی توليد کد منبع اسمبلی را دارند. برنامه نويسان اغلب ترکيب C و اسمبلی را در برنامه های کاربردی به کار می برند.

سيستم هاي عدد نويسي


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

سيستم هاي عدد نويسي
سيستم عددی اعشاری
سيستم عددی دودوئی
سيستم عددی هگز


سيستم هاي عدد نويسي

در کارهای روزمره از سيستم عددی اعشاری يا مبنای 10 استفاده می شود. اين سيستم برای کامپيوتر مناسب نيست و برای سادگی سخت افزار، کليه اطلاعات به شکل بيت های روشن و خاموش رمز می شوند. بنابراين سيستم عددی باينری که تنها شامل ارقام صفر و يک است برای اين منظور بسيار مناسب است. عدد 1 (on) مشخص کننده +5 ولت و عدد صفر (off) مشخص کننده 0.5 ولت است.

برای تعيين مبنای عدد يک حرف کوچک در انتهای آن قرار می گيرد. مثاال 45h به معنی عدد 45 در مبنای شانزده است. و 11010011b يعنی اين عدد در مبنای 2 است. اين روشی است که اسمبلر اعداد را در برنامه های اسمبلی تشخيص می دهد.


سيستم عددی اعشاری (Decimal)

اعداد اعشاری يا مبنای 10 از 10 رقم (0 تا 9) تشکيل شده اند. هر رقم به توانی از 10 مرتبط است که نشان دهنده ارزش مکانی رقم در عدد است.

234 = 2 × 102 + 3 × 101 + 4 × 100
     = 200 + 30 + 4


سيستم عددی دودوئي (binary)

سيستم باينری بر اساس تنها دو وضعيت است: روشن (1) يا خاموش (0)، بنابراين درمبنای 2 است. يک رقم باينری يک بيت ناميده می شود (در واقع کلمه Bit مخفف BinaryDigit است).


جدول توان هاي 2
جدول نمايش اعداد 0 تا 15 در مبنای دو


تبديل باينری به اعشاری

مقدار يک عدد باينری بر اساس بيت های 1 و ارزش مکانی آنها بدست می آيد. ارزش مکانی هر بيت توانی از 2 است. برای محاسبه مقدار اعشاری يک عدد باينری، کافی است هر رقم از راست به چپ در ارزش مکانی اش ضرب شده سپس کليه اعداد با هم جمع شوند.


مثال 1. تبديل عدد 11001b به مبنای 10.

Binary: 11001
Decimal: 1 × 2^4 + 1 × 2^3 + 0 × 2^2 + 0 × 2^1 + 1 × 2^0
     = 16 + 8 + 0 + 0 + 1
     = 25

مثال 2. تبديل عدد باينری 10010000 به مبنای 10.

Binary: 1 0 0 1 0 0 0 0
Decimal: 1×2^7 + 0×2^6 + 0×2^5 + 1×2^4 + 0×2^3 + 0×2^2 + 0×2^1 + 0×2^0
     =128 + 0 + 0 + 16 + 0 + 0 + 0 + 0
     =144

کاراکتر ^ نشان دهنده عمل توان است.


تبديل اعشاری به باينری

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


مثال. تبديل عدد 43 به مبنای 2

عدد

خارج قسمت

باقيمانده

43 ÷ 2

21

1

21 ÷ 2

10

1

10 ÷ 2

5

0

5 ÷ 2

2

1

2 ÷ 2

1

0

1 ÷ 2

0

1

با قرار دادن باقيمانده های تقسيم از پايين به بالا عدد باينری 101011 بدست می آيد.

جمع اعداد باينری

جمع باينری ساده به صورت زير محاسبه می شود:

0 + 0 = 0
0 + 1 = 1
1 + 1 = 10
1 + 1 + 1 = 11

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




1

1



1

1

0

1

1

+

0

0

0

0

1

 

1

1

1

0

0


شمردن حروف بزرگ در اسمبلی

.MODEL SMALL
.STACK 100H
.DATA
MSG1 DB 'PLEASE ENTER YOUR SENTENCE: $'
MSG2 DB 0DH,0AH,'THE NUMBER OF CAPITAL LETTERS IN SENTENCE :$'
MSG3 DB 0DH,0AH,'You have entered more than 19 CAPITAL LETTERS $'
.CODE
MAIN PROC
MOV AX,@DATA ;initialize DS
MOV DS,AX
;DISPLAY MSG1
LEA DX,MSG1 ;GET MSG1
MOV AH,9 ;DISPLAY STRING
INT 21H ;DISPLAY MSG1
; start of loop reading the input
; one character at a time
mov bX,0 ; initialize CAPITAL LETTERS counter
loop_top:
MOV AH,1 ;READ A CHARACTDR
INT 21h
cmp al,0dh ; end of line?
je Display1 ; yes, then stop
; test for capital letters
cmp al,'A'
JL loop_end ; if char < 'A' then go to the end of the loop
CMP al,'Z'
JG loop_end ; if char > 'Z' then go to the end of the loop
INC bx ; increment capital letter counter
cmp bx,19
JG Display2 ; more than 19 capital letters
loop_end:
jmp loop_top
Display1:
LEA DX,MSG2 ;GET MSG1
MOV AH,9 ;DISPLAY STRING
INT 21H ;DISPLAY MSG1
CMP bx,9
JLE Display3
mov ah,2
mov dl,'1'
int 21h
mov dl,bl ; print the number of characters
add dl,'0' ;To convert the number of characters from ASCII to decimal number
SUB dl,10
MOV AH,2 ;display the number of character that is in dl
INT 21H
jmp exit_program
Display3:
mov dl,bl ; print the number of characters
add dl,'0' ;To convert the number of characters from ASCII to decimal number
MOV AH,2 ;display the number of character that is in dl
INT 21H
jmp exit_program ; all done
Display2:
LEA DX,MSG3 ;GET MSG3
MOV AH,9 ;DISPLAY STRING
INT 21H ;DISPLAY MSG3
exit_program:
MOV AH,4CH ; return control to DOS
INT 21H
MAIN ENDP
END MAIN
ENDF

گرافیک در اسمبلی - کشیدن دایره

.model small
.stack 640
.data
    ray dw 20
    x   dw ?
    y   dw ?
    help1 dw ?
       
.code

circle proc
   mov ah,0
   mov al,13h
   int 10h
    mov ax,1
    mul ray
    mov ray,ax ;ray*=10
    ;mov ax,ray
    mul ray
    mov ray,ax ;ray=ray^2
    ; - - - - - -
    mov dx,0
l1c:    mov cx,0;payine safhe - sotun
l2c:    push cx
    push dx
    mov x,20
    sub cx,x
    mov ax,cx
    mul cx  ;ax=cx^2
    mov help1,ax
    ; - - - - - -
    mov y,20
    pop dx
    push dx
    sub dx,y
    mov ax,dx
    mul dx  ;ax=dx^2
    add help1,ax
    mov ax,help1 ;ax=dx^2 + cx^2
    ; - - - - - -
    pop dx
    pop cx
    cmp ray,ax
    jb l3c
    ; - - - paint pixel - - -
    mov al,12
    mov ah,0ch
    mov bh,0
    int 10h
    ; - - - - - -
l3c:   
    inc cx
        cmp cx,40
        jne l2c
    inc dx
        cmp dx,40
        jne l1c
    ret      
circle endp   
main proc far
    mov ax,@data
    mov ds,ax
    call circle
    mov ah,8
    int 21h
    mov ax, 4c00h
    int 21h
main endp
end main

برنامه تمام سرتها

#include iostream.h>
#include stdio.h>
#include conio.h>
void print(int x[], int first)
 {
            for(int k=0;k < first;k++)
                 printf("%d   ,",x[k]);
                cout << endl << endl;
 }
int getarray(int x[],int first)
  {
    int i;
    for(i=0;i    {
        printf("Enter element  %d : ",i+1);
        scanf("%d",&x[i]);
    }
    return (x[first]);
  }
/////////////Shell sort//////////////////////////////////////
void shell_sort (int *a, int n) {
    int h, i, j, k;
    for (h = n; h /= 2;) {
        for (i = h; i < n; i++) {
            k = a[i];
            for (j = i; j >= h && k < a[j - h]; j -= h) {
                a[j] = a[j - h];
            }
            a[j] = k;
        }
    }
}
/////////////Shell sort//////////////////////////////////////
/////////////Merge sort//////////////////////////////////////
void merge(int,int,int,int x[]);
void merge_sort(int low,int high,int x[])
{
 int mid;
 if(low {
  mid=(low+high)/2;
  merge_sort(low,mid,x);
  merge_sort(mid+1,high,x);
  merge(low,mid,high,x);
 }
}
void merge(int low,int mid,int high,int x[])
{
 int h,i,j,b[50],k;
 h=low;
 i=low;
 j=mid+1;

 while((h<=mid)&&(j<=high))
 {
  if(x[h]<=x[j])
  {
    b[i]=x[h];
   h++;
  }
  else
  {
    b[i]=x[j];
   j++;
  }
  i++;
 }
 if(h>mid)
 {
  for(k=j;k<=high;k++)
  {
    b[i]=x[k];
    i++;
  }
 }
 else
 {
  for(k=h;k<=mid;k++)
  {
    b[i]=x[k];
   i++;
  }
 }
 for(k=low;k<=high;k++) x[k]=b[k];
}
/////////////Merge sort//////////////////////////////////////
/////////////Radix sort//////////////////////////////////////
void radixsort(int a[],int n)
{
        int i,b[8],m=0,exp=1;
        for(i=0;i        {
            if(a[i]>m)
                m=a[i];
        }

        while(m/exp>0)
        {
            int bucket[10]={0};
            for(i=0;i                bucket[a[i]/exp%10]++;
            for(i=1;i<10;i++)
                bucket[i]+=bucket[i-1];
            for(i=n-1;i>=0;i--)
                b[--bucket[a[i]/exp%10]]=a[i];
            for(i=0;i                a[i]=b[i];
            exp*=10;

          }
    }
/////////////Radix sort//////////////////////////////////////
/////////////Heap sort//////////////////////////////////////
void siftDown(int numbers[], int root, int bottom) {
  int maxChild = root * 2 + 1;

  // Find the biggest child
  if(maxChild < bottom) {
     int otherChild = maxChild + 1;
     // Reversed for stability
     maxChild = (numbers[otherChild] > numbers[maxChild])?otherChild:maxChild;
  } else {
     // Don't overflow
     if(maxChild > bottom) return;
  }

  // If we have the correct ordering, we are done.
  if(numbers[root] >= numbers[maxChild]) return;

  // Swap
  int temp = numbers[root];
  numbers[root] = numbers[maxChild];
  numbers[maxChild] = temp;

  // Tail queue recursion. Will be compiled as a loop with correct compiler switches.
  siftDown(numbers, maxChild, bottom);
}
void heapSort(int numbers[], int array_size) {
  int i, temp;

  for (i = (array_size / 2); i >= 0; i--) {
     siftDown(numbers, i, array_size - 1);
  }

  for (i = array_size-1; i >= 1; i--) {
     // Swap
     temp = numbers[0];
     numbers[0] = numbers[i];
     numbers[i] = temp;

     siftDown(numbers, 0, i-1);
  }
}
/////////////Heap sort//////////////////////////////////////
/////////////Quick sort//////////////////////////////////////
  void quickSort(int arr[], int left, int right)
  {

        int i = left, j = right;

        int tmp;

        int pivot = arr[(left + right) / 2];



        /* partition */

        while (i <= j) {

                while (arr[i] < pivot)

                        i++;

                while (arr[j] > pivot)

                        j--;

                if (i <= j) {

                        tmp = arr[i];

                        arr[i] = arr[j];

                        arr[j] = tmp;

                        i++;

                        j--;

                }

        };

        /* recursion */

        if (left < j)

                quickSort(arr, left, j);

        if (i < right)

                quickSort(arr, i, right);

}
/////////////Quick sort//////////////////////////////////////
/////////////tree sort//////////////////////////////////////
struct tree{

     int info;

     tree *Left, *Right;

};

tree *root;

class TreeSort{

     public:

          int no_of_elements;

          int elements[10];

     public:

          void getarray();

          void sortit();

          void insert1(int);

          tree *insert2(tree *, tree *);

          void display(tree *);

};

void TreeSort::getarray(){

     cout<<"How many elements? ";

     cin>>no_of_elements;

     cout<<"Insert array of element to sort: ";

     for(int i=0;i
          cin>>elements[i];

     }

}

void TreeSort::sortit(){

     for(int i = 0; i  < no_of_elements; i++){

          insert1(elements[i]);

     }

}

tree* TreeSort::insert2(tree *temp,tree *newnode){

     if(temp==NULL){

          temp=newnode;

     }

     else if(temp->info < newnode->info){

          insert2(temp->Right,newnode);

          if(temp->Right==NULL)

                temp->Right=newnode;

     }

     else{

          insert2(temp->Left,newnode);

          if(temp->Left==NULL)

                temp->Left=newnode;

     }

     return temp;

}

void TreeSort::insert1(int n){

     tree *temp=root,*newnode;

     newnode=new tree;

     newnode->Left=NULL;

     newnode->Right=NULL;

     newnode->info=n;

     root=insert2(temp,newnode);

}

/* Inorder traversal */

void TreeSort::display(tree *t = root){

     if(root==NULL){

          cout<<"Nothing to display";

     }else

     if(t!=NULL){

          display(t->Left);

          cout<info<<" ";

          display(t->Right);

     }

}
/////////////tree sort//////////////////////////////////////

int main(){
int a=1;
while(a>=1){
  cout<<"What kind Of sort You Want :"<     <<"1-  Selection Sort "<     <<"2-  Bubble Sort "<     <<"3-  Insertion Sort "<     <<"4-  Exchange Sort "<     <<"5-  Quick Sort "<     <<"6-  Heap Sort "<     <<"7-  Merge Sort "<     <<"8-  Tree Sort "<     <<"9-  Radix Sort "<     <<"10- Shell Sort "<     <<"0-  Exit"<cin>>a;
    switch (a)
    {
        case 1:
                 {
                         int* x;
                         int first,i,j,max,index;
                         printf("Enter Size of aray : ");
                         scanf("%d",&first);
                         x=new int[first];
                         getarray(x,first);
                             for(i=first-1;i>=0;i--)
                                    {
                                        max=x[0];
                                        index=0;
                                        for(j=0;j<=i;j++)
                                             if(x[j]>max)
                                                  {
                                                     max=x[j];
                                                     index=j;
                                                  }
                                        x[index]=x[i];
                                        x[i]=max;
                                    }
                        print(x,first);
                 }
        break;
        case 2:
                 {
                         int* x;
                         int first,i,temp;
                         printf("Enter Size of aray : ");
                         scanf("%d",&first);
                         x=new int[first];
                         getarray(x,first);
                         int flag = 0;
                         int pass=1;
                         while(pass<=first&&flag==0)
                             {
                                 flag=1;
                                 for(i=0;i<=first-pass-1;i++)
                                     if(x[i]>x[i+1])
                                            {
                                              flag=0;
                                              temp=x[i];
                                              x[i]=x[i+1];
                                              x[i+1]=temp;
                                            }
                              pass=pass+1;
                             }
                         print(x,first);
                 }
        break;
        case 3:
                 {
                         int* x;
                         int first,i,j,y;
                         printf("Enter Size of aray : ");
                         scanf("%d",&first);
                         x=new int[first];
                         getarray(x,first);
                     for(i=1;i<=first-1;i++)
                            {
                              y=x[i];
                              j=i-1;
                              while(j>=0&&y                                     {
                                         x[j+1]=x[j];
                                         j=j-1;
                                     }
                              x[j+1]=y;
                            }
                        print(x,first);
                 }
        break;
        case 4:
                 {
                         int* x;
                         int first,i,j,temp;
                         printf("Enter Size of aray : ");
                         scanf("%d",&first);
                         x=new int[first];
                         getarray(x,first);
                     for(i=0;i<=first-1;i++)
                            for(j=i+1;j                                if(x[i]>x[j])
                                        {
                                            temp=x[i];
                                            x[i]=x[j];
                                            x[j]=temp;
                                        }
                        print(x,first);
                 }
        break;
        case 5:
                 {
                         int* x;
                         int first,left=0;
                         printf("Enter Size of aray : ");
                         scanf("%d",&first);
                         x=new int[first];
                         getarray(x,first);
                        quickSort(x,left,first);
                        print(x,first);
                 }
        break;
        case 6:
                 {
                         int* x;
                         int first;
                         printf("Enter Size of aray : ");
                         scanf("%d",&first);
                         x=new int[first];
                         getarray(x,first);
                    heapSort(x,first);
                    print(x,first);
                 }
        break;
        case 7:
                 {
                         int* x;
                         int first;
                         printf("Enter Size of aray : ");
                         scanf("%d",&first);
                         x=new int[first];
                         getarray(x,first);
                    merge_sort(0,first,x);
                    print(x,first);
                 }
        break;
        case 8:
                 {
                    TreeSort TS;
                    TS.getarray();
                    TS.sortit();
               cout<                    TS.display();
               cout<                 }
        break;
        case 9:
                 {
                         int* x;
                         int first;
                         printf("Enter Size of aray : ");
                         scanf("%d",&first);
                         x=new int[first];
                         getarray(x,first);
                         radixsort(x,first);
                         print(x,first);
                 }
        break;
        case 10:
                 {
                         int* x;
                         int first;
                         printf("Enter Size of aray : ");
                         scanf("%d",&first);
                         x=new int[first];
                         getarray(x,first);
                         shell_sort(x,first);
                         print(x,first);
                 }
        break;
        default: break;
     
     }
}
    getch();
     return 0;

}

سرت درختی Tree Sort

#include iostream.h>
#include stdio.h>
#include conio.h>
/////////////tree sort//////////////////////////////////////
struct tree{

     int info;

     tree *Left, *Right;

};

tree *root;

class TreeSort{

     public:

          int no_of_elements;

          int elements[10];

     public:

          void getarray();

          void sortit();

          void insert1(int);

          tree *insert2(tree *, tree *);

          void display(tree *);

};

void TreeSort::getarray(){

     cout<<"How many elements? ";

     cin>>no_of_elements;

     cout<<"Insert array of element to sort: ";

     for(int i=0;i
          cin>>elements[i];

     }

}

void TreeSort::sortit(){

     for(int i = 0; i  <  no_of_elements; i++){

          insert1(elements[i]);

     }

}

tree* TreeSort::insert2(tree *temp,tree *newnode){

     if(temp==NULL){

          temp=newnode;

     }

     else if(temp->info < newnode->info){

          insert2(temp->Right,newnode);

          if(temp->Right==NULL)

                temp->Right=newnode;

     }

     else{

          insert2(temp->Left,newnode);

          if(temp->Left==NULL)

                temp->Left=newnode;

     }

     return temp;

}

void TreeSort::insert1(int n){

     tree *temp=root,*newnode;

     newnode=new tree;

     newnode->Left=NULL;

     newnode->Right=NULL;

     newnode->info=n;

     root=insert2(temp,newnode);

}

/* Inorder traversal */

void TreeSort::display(tree *t = root){

     if(root==NULL){

          cout<<"Nothing to display";

     }else

     if(t!=NULL){

          display(t->Left);

          cout<info<<" ";

          display(t->Right);

     }

}
/////////////tree sort//////////////////////////////////////

int main()
 {
     
           TreeSort TS;
           TS.getarray();
           TS.sortit();
               cout < < endl;
           TS.display();
               cout < < endl < < endl;
                 
               getch();
           return 0;
 }

سرت صدفی Shell Sort

#include iostream.h>
#include stdio.h>
#include conio.h>
int getarray(int x[],int first)
  {
    int i;
    for(i=0;i    {
        printf("Enter element  %d : ",i+1);
        scanf("%d",&x[i]);
    }
    return (x[first]);
  }
/////////////Shell sort//////////////////////////////////////
void shell_sort (int *a, int n) {
    int h, i, j, k;
    for (h = n; h /= 2;) {
        for (i = h; i < n; i++) {
            k = a[i];
            for (j = i; j >= h && k < a[j - h]; j -= h) {
                a[j] = a[j - h];
            }
            a[j] = k;
        }
    }
}
/////////////Shell sort//////////////////////////////////////
int main()
 {
     
        int* x;
            int first;
        printf("Enter Size of aray : ");
        scanf("%d",&first);
        x=new int[first];
        getarray(x,first);
        shell_sort(x,first);
        for(int k=0;k <=first;k++)
             printf("%d   ,",x[k]);
       cout< < endl < < endl;
        getch();
        return 0;
 }

سرت انتخابی Selection Sort

#include iostream.h>
#include stdio.h>
#include conio.h>
int getarray(int x[],int first)
  {
    int i;
    for(i=0;i    {
        printf("Enter element  %d : ",i+1);
        scanf("%d",&x[i]);
    }
    return (x[first]);
  }
int main()
 {
     
        int* x;
            int first,left=0,i,j,max,index;
        printf("Enter Size of aray : ");
        scanf("%d",&first);
        x=new int[first];
        getarray(x,first);
                for(i=first-1;i>=0;i--)
           {
            max=x[0];
            index=0;
            for(j=0;j<=i;j++)
                   if(x[j]>max)
                 {
                 max=x[j];
                 index=j;
                 }
            x[index]=x[i];
            x[i]=max;
           }
for(int k=0;k <=first;k++)

     printf("%d   ,",x[k]);
       cout< < endl < < endl;
       
getch();
            return 0;
 }

سرت رادیویی Radios Sort

#include iostream.h>
#include stdio.h>
#include conio.h>
int getarray(int x[],int first)
  {
    int i;
    for(i=0;i    {
        printf("Enter element  %d : ",i+1);
        scanf("%d",&x[i]);
    }
    return (x[first]);
  }
/////////////Radix sort//////////////////////////////////////
void radixsort(int a[],int n)
{
        int i,b[8],m=0,exp=1;
        for(i=0;i        {
            if(a[i]>m)
                m=a[i];
        }

        while(m/exp>0)
        {
            int bucket[10]={0};
            for(i=0;i                bucket[a[i]/exp%10]++;
            for(i=1;i<10;i++)
                bucket[i]+=bucket[i-1];
            for(i=n-1;i>=0;i--)
                b[--bucket[a[i]/exp%10]]=a[i];
            for(i=0;i                a[i]=b[i];
            exp*=10;

          }
    }
/////////////Radix sort//////////////////////////////////////

int main()
 {
     
        int* x;
            int first;
        printf("Enter Size of aray : ");
        scanf("%d",&first);
        x=new int[first];
        getarray(x,first);
        radixsort(x,first);
for(int k=0;k <=first;k++)

     printf("%d   ,",x[k]);
       cout< < endl < < endl;
       
getch();
            return 0;
 }

سرت سریع  Quick Sort

#include iostream.h>
#include stdio.h>
#include conio.h>
int getarray(int x[],int first)
  {
    int i;
    for(i=0;i    {
        printf("Enter element  %d : ",i+1);
        scanf("%d",&x[i]);
    }
    return (x[first]);
  }

/////////////Quick sort//////////////////////////////////////
  void quickSort(int arr[], int left, int right)
  {

        int i = left, j = right;

        int tmp;

        int pivot = arr[(left + right) / 2];



        /* partition */

        while (i <= j) {

                while (arr[i] < pivot)

                        i++;

                while (arr[j] > pivot)

                        j--;

                if (i <= j) {

                        tmp = arr[i];

                        arr[i] = arr[j];

                        arr[j] = tmp;

                        i++;

                        j--;

                }

        };

        /* recursion */

        if (left < j)

                quickSort(arr, left, j);

        if (i < right)

                quickSort(arr, i, right);

}
/////////////Quick sort//////////////////////////////////////
int main()
 {
     
        int* x;
            int first,left=0;
        printf("Enter Size of aray : ");
        scanf("%d",&first);
        x=new int[first];
        getarray(x,first);
        quickSort(x,left,first);
        for(int k=0;k < first; k++)
             printf("%d   ,",x[k]);
        cout< < endl < < endl;
                getch();

            return 0;
 }

سرت ادغامی Merge Sort

#include iostream.h>
#include stdio.h>
#include conio.h>
int getarray(int x[],int first)
  {
    int i;
    for(i=0;i    {
        printf("Enter element  %d : ",i+1);
        scanf("%d",&x[i]);
    }
    return (x[first]);
  }
/////////////Merge sort//////////////////////////////////////
void merge(int,int,int,int x[]);
void merge_sort(int low,int high,int x[])
{
 int mid;
 if(low {
  mid=(low+high)/2;
  merge_sort(low,mid,x);
  merge_sort(mid+1,high,x);
  merge(low,mid,high,x);
 }
}
void merge(int low,int mid,int high,int x[])
{
 int h,i,j,b[50],k;
 h=low;
 i=low;
 j=mid+1;

 while((h<=mid)&&(j<=high))
 {
  if(x[h]<=x[j])
  {
    b[i]=x[h];
   h++;
  }
  else
  {
    b[i]=x[j];
   j++;
  }
  i++;
 }
 if(h>mid)
 {
  for(k=j;k<=high;k++)
  {
    b[i]=x[k];
    i++;
  }
 }
 else
 {
  for(k=h;k<=mid;k++)
  {
    b[i]=x[k];
   i++;
  }
 }
 for(k=low;k<=high;k++) x[k]=b[k];
}
/////////////Merge sort//////////////////////////////////////


int main()
 {
     
        int* x;
            int first;
        printf("Enter Size of aray : ");
        scanf("%d",&first);
        x=new int[first];
        getarray(x,first);
        merge_sort(0,first,x);
        for(int k=0;k < first ;k++)
             printf("%d   ,",x[k]);
        cout < < endl < < endl;
                getch();
            return 0;
 }

سرت درجی  Insertion Sort

#include iostream.h>
#include stdio.h>
#include conio.h>
int getarray(int x[],int first)
  {
    int i;
    for(i=0;i    {
        printf("Enter element  %d : ",i+1);
        scanf("%d",&x[i]);
    }
    return (x[first]);
  }
int main()
 {
     
        int* x;
            int first,left=0,i,j,y;
        printf("Enter Size of aray : ");
        scanf("%d",&first);
        x=new int[first];
        getarray(x,first);
        for(i=1;i<=first-1;i++)
           {
             y=x[i];
             j=i-1;
             while(j>=0&&y             {
                      x[j+1]=x[j];
                 j=j-1;
             }
            x[j+1]=y;
           }
        for(int k=0;k < first ;k++)
             printf("%d   ,",x[k]);
        cout < < endl < < endl;
                getch();
            return 0;
 }

هیپ سرت Heap Sort

#include iostream.h>
#include stdio.h>
#include conio.h>
int getarray(int x[],int first)
  {
    int i;
    for(i=0;i    {
        printf("Enter element  %d : ",i+1);
        scanf("%d",&x[i]);
    }
    return (x[first]);
  }
/////////////Heap sort//////////////////////////////////////
void siftDown(int numbers[], int root, int bottom) {
  int maxChild = root * 2 + 1;

  // Find the biggest child
  if(maxChild < bottom) {
     int otherChild = maxChild + 1;
     // Reversed for stability
     maxChild = (numbers[otherChild] > numbers[maxChild])?otherChild:maxChild;
  } else {
     // Don't overflow
     if(maxChild > bottom) return;
  }

  // If we have the correct ordering, we are done.
  if(numbers[root] >= numbers[maxChild]) return;

  // Swap
  int temp = numbers[root];
  numbers[root] = numbers[maxChild];
  numbers[maxChild] = temp;

  // Tail queue recursion. Will be compiled as a loop with correct compiler switches.
  siftDown(numbers, maxChild, bottom);
}
void heapSort(int numbers[], int array_size) {
  int i, temp;

  for (i = (array_size / 2); i >= 0; i--) {
     siftDown(numbers, i, array_size - 1);
  }

  for (i = array_size-1; i >= 1; i--) {
     // Swap
     temp = numbers[0];
     numbers[0] = numbers[i];
     numbers[i] = temp;

     siftDown(numbers, 0, i-1);
  }
}
/////////////Heap sort//////////////////////////////////////
int main()
 {
    
        int* x;
            int first;
        printf("Enter Size of aray : ");
        scanf("%d",&first);
        x=new int[first];
        getarray(x,first);
        heapSort(x,first);
        for(int k=0;k < first ;k++)
             printf("%d   ,",x[k]);
        cout < < endl < < endl;
                getch();
            return 0;
 }

سرت تعویضی Exchange Sort

#include iostream.h>
#include stdio.h>
#include conio.h>
int getarray(int x[],int first)
  {
    int i;
    for(i=0;i    {
        printf("Enter element  %d : ",i+1);
        scanf("%d",&x[i]);
    }
    return (x[first]);
  }
int main()
 {
     
        int* x;
            int first,left=0,i,j,temp;
        printf("Enter Size of aray : ");
        scanf("%d",&first);
        x=new int[first];
        getarray(x,first);
        for(i=0;i<=first-1;i++)
            for(j=i+1;j            if(x[i]>x[j])
              {
                temp=x[i];
                x[i]=x[j];
                x[j]=temp;
                  }
        for(int k=0;k < first;k++)
             printf("%d   ,",x[k]);
        cout< < endl < < endl;
                getch();

            return 0;
 }

سرت حبابی Bubble sort

#include iostream.h>
#include stdio.h>
#include conio.h>
int getarray(int x[],int first)
  {
    int i;
    for(i=0;i    {
        printf("Enter element  %d : ",i+1);
        scanf("%d",&x[i]);
    }
    return (x[first]);
  }
int main()
 {
     
        int* x;
            int first,left=0,temp,i;
        printf("Enter Size of aray : ");
        scanf("%d",&first);
        x=new int[first];
        getarray(x,first);
        int flag = 0;
        int pass=1;
        while(pass<=first&&flag==0)
         {
            flag=1;
            for(i=0;i<=first-pass-1;i++)
             if(x[i]>x[i+1])
                    {
                flag=0;
                temp=x[i];
                x[i]=x[i+1];
                x[i+1]=temp;
                }
            pass=pass+1;
         }
        for(int k=0;k < first;k++)
             printf("%d   ,",x[k]);
        cout < < endl < < endl;
                getch();

            return 0;
 }

رمز گذاری یک عدد

رمز گذاری یک عدد - یعنی یک عدد را میدهیم و بعد یک عدد کلیدی می دهیم سپس آنرا تبدیل به کد میکند و یک رمز میدهد که با اولی کلی فرق دارد و برای رمز گذاری یک متن میتوان بکار برد .

ضمنا در آن از دستور Rotate هم استفاده میشود .


.model small

.stack 64
.data
    n db ?
    yekan db ?
    strlist label byte
        max db 80
        len db ?
        buf db 80 dup("$")
       
    strlist1 label byte
        ma1 db 80
        len1 db ?
        buf1 db 80 dup("$")
    linef db 13,10,"$"
.code
newline proc
    mov ah,9
    lea dx,linef
    int 21h
    ret
newline endp

main proc far
    mov ax,@data
    mov ds,ax
  ;get strlist
    mov ah,0ah
    lea dx,strlist
    int 21h
  ;new line        
    call newline
  ;get strlist1 
    mov ah,0ah
    lea dx,strlist1
    int 21h
  ;new line
    call newline
   
    cmp len1,1
    jz l1
   
    lea bx,buf1
    mov al,[bx]
    sub al,48
   
    mov cl,10
    mul cl
    add al,[bx+1]
    sub al,48
    mov ah,[bx+1]
    sub ah,48
    mov yekan,ah
    jmp l2
    l1:
    lea bx,buf1
    mov al,[bx]
    sub al,48
    mov yekan,al
    l2:
    mov n,al
    lea bx,buf
    mov ch,0
    l3:
    cmp ch,len
    jz l4   
   
    mov al,[bx]
    add al,n
   
    mov cl,yekan
    ror al,cl
   
    mov ah,2
    mov dl,al
    int 21h
    inc bx
    inc ch
    jmp l3
   
    l4:
     
    mov ah,8
    int 21h
    mov ax, 4c00h
    int 21h
main endp
end main

استفاده از توابع و فراخوانی آن در اسمبلی

برنامه فوق استفاده از توابع و فراخوانی آن در اسمبلی است و ضمنا ب م م دو عدد را هم به اسمبلی محاسبه می کند

.model small
.stack 64
.data
    n dw ?
    m dw ?
    res dw ?
    sign db 1
    linef db 13,10,"$"
    str db 7 dup("$")
    strlist label byte
        max db 6
        len db ?
        buf db 6 dup("$")
        dolar db "$"
    strlist1 label byte
        max1 db 6
        len1 db ?
        buf1 db 6 dup("$")
        dolar1 db "$"       
.code
getnum proc
    mov ah,0ah
    int 21h
   
    mov ax,0; javab
    mov bx,dx
    add bx,2
    cmp [bx],'-'
    jnz g1
    mov sign,-1
    inc bx
    g1:
   
    cmp [bx],'0'
    jb g2
    cmp [bx],'9'
    ja g2
   
    mov cx,10
    mul cx
   
    mov cl,[bx]
    sub cl,48
    mov ch,0
    add ax,cx
   
    inc bx
    jmp g1
    g2:
    cmp sign,1
    jz g3
    neg ax
    g3:
   
    ret
getnum endp
bmm proc
   
    n1:
    cmp m,0
    jz n2
    mov ax,n
    mov dx,0
    mov si,m
    div si
    mov ax,m
    mov n,ax
    mov m,dx
    jmp n1
    n2:
    mov ax,n
    ret
bmm endp

printnum proc
    lea bx,str
   
    cmp ax,0
    jg p1
    mov [bx],'-'
    neg ax
    inc bx
    p1:
    mov cx,0
    p4:
    cmp ax,0
    jz p2
    mov dx,0
    mov si,10
    div si
    push dx
    inc cx
    jmp p4
    p2:
    cmp cx,0
    jz p3
    pop dx
    add dl,48
    mov [bx],dl
    inc bx
    dec cx
    jmp p2
    p3:
    mov ah,9
    lea dx,str
    int 21h
    ret
printnum endp
main proc far
    mov ax,@data
    mov ds,ax
          
    lea dx,strlist      
    call getnum
    mov n,ax
   
    mov ah,9
    lea dx,linef
    int 21h
   
    lea dx,strlist1
    call getnum
    mov m,ax
         
    call bmm
   
    mov res,ax
   
    mov ah,9
    lea dx,linef
    int 21h
       
    mov ax,res
    lea dx,str
    call printnum     
         
    mov ah,8
    int 21h
    mov ax, 4c00h
    int 21h
main endp
end main

برنامه میانگین دو عدد

برنامه میانگین دو عدد

.model small

.stack 64
.data
    n dw ?
    m dw ?
    q dw ?
    res dw ?
    sign db 1
    linef db 13,10,"$"
    str db 7 dup("$")
    str1 db ".5" , '$'
    strlist label byte
        max db 6
        len db ?
        buf db 6 dup("$")
        dolar db "$"
    strlist1 label byte
        max1 db 6
        len1 db ?
        buf1 db 6 dup("$")
        dolar1 db "$"       
.code
getnum proc
    mov ah,0ah
    int 21h
   
    mov ax,0; javab
    mov bx,dx
    add bx,2
    cmp [bx],'-'
    jnz g1
    mov sign,-1
    inc bx
    g1:
   
    cmp [bx],'0'
    jb g2
    cmp [bx],'9'
    ja g2
   
    mov cx,10
    mul cx
   
    mov cl,[bx]
    sub cl,48
    mov ch,0
    add ax,cx
   
    inc bx
    jmp g1
    g2:
    cmp sign,1
    jz g3
    neg ax
    g3:
   
    ret
getnum endp
avrg proc

    mov ax,n
    mov si,2
    mov cx,m
    add ax,cx
    div si
    mov q,dx
    ret
avrg endp

printnum proc
    lea bx,str
   
    cmp ax,0
    jg p1
    mov [bx],'-'
    neg ax
    inc bx
    p1:
    mov cx,0
    p4:
    cmp ax,0
    jz p2
    mov dx,0
    mov si,10
    div si
    push dx
    inc cx
    jmp p4
    p2:
    cmp cx,0
    jz p3
    pop dx
    add dl,48
    mov [bx],dl
    inc bx
    dec cx
    jmp p2
    p3:
    mov ah,9
    lea dx,str
    int 21h
    ret
printnum endp

main proc far
    mov ax,@data
    mov ds,ax
          
    lea dx,strlist      
    call getnum
    mov n,ax
   
    mov ah,9
    lea dx,linef
    int 21h
   
    lea dx,strlist1
    call getnum
    mov m,ax
         
    call avrg
   
    mov res,ax
   
    mov ah,9
    lea dx,linef
    int 21h
   
    mov ax,res
    lea dx,str
    call printnum     
  ;momaiez
    cmp q,0
    jbe o1
    mov ah,9
    lea dx,str1
    int 21h
    o1:
  ;end momayez  
    mov ah,8
    int 21h
    mov ax, 4c00h
    int 21h
main endp
end main

نوشتن اسمبلی در کامپایلر Borland C++ 4.5

نوشتن اسمبلی در کامپایلر Borland C++ 4.5

#include
#include
#include
int main()
  {
      int n , k , i=0 , l , m;
      scanf("%d",&n);
      int *a = new int [n];
      int index1 , index2 ;
      int bindex1 , bindex2 ;
      int r=1 , bestr=1 ;
      index1 = index2 = 0 ;

      /*  Code C++
      for (int i = 0 ; i < n ; i++ ){
            scanf("%d", &a[i]);
      if(!a[i]) {
          if(r>bestr){
              bindex1 = index1 ;
              bindex2 = index2 - 1;
              bestr = r ;
              }
          r=1  ;
          index1 = index2 = i+1 ;
                    }
      else {
              r *= a[i] ;
              index2 ++ ;
             }
                                                 }
     for(i = bindex1 ; i< bindex2 ; i++ )
          printf( " %d * ", a[i] );
     printf( "  %d = %d ", a[bindex2], bestr );


      */

      __asm {
             MOV AX , r
                 MOV CX , 0
                 L1:
                 MOV CX , i
                 CMP CX , n
                 JAE L2

                }
      scanf("%d",&a[i]);
      k=a[i];
      __asm {

                 CMP k , 0
                 JNE L3
                 MOV BX , bestr
                 CMP BX , r
                 JB L4
                 MOV r , BX
                 MOV BX , bindex1
                 MOV index1 , BX

                 DEC index2
                 MOV BX , index2
                 MOV bindex2 ,BX

                 L4:
                 MOV r , 1
                 MOV index2 , CX
                 INC index2
                 MOV BX , index2
                 MOV index1 , BX
                 L3:
                 CMP k ,0
                 JE L9

                 MUL k

                 INC index2
                 L9:
                 INC i
                 JMP L1
                 L2:
             MOV bestr , AX
                 /*
                 MOV BX , bindex1
                 L5:
                 CMP BX , bindex2
                 JAE L6
              }
      printf("%d * " , a[i]);
      __asm {
                 INC BX
                 JMP L1
                 L6:
              }
      printf("%d = %d",a[bindex2],bestr);*/

             }

     for(i = 0 ; i< n-1 ; i++ )
          printf( " %d * ", a[i] );
     printf( "  %d = %d ", a[n-1], bestr );
        return 0 ;
        getch();
  }

نحوه برنامه نویسی در اسمبلی

برنامه های اسمبلی و زبان ماشین تحت کامپایلر EMU8086 نوشته شده است .

برای دانلود برنامه EMU8086 به سایت emu8086.com مراجعه کنید و لینک دانلود برای ویندوز مورد نظرتون دانلود کنید .

نکته اینکه سایت ذکر شده برای ایرانی ها فیلتر میباشد . نه اینکه مخابرات فیلترش کرده باشه !!! نه !!! اونا مارو فیلتر کردن !!!!

باید با فیلتر شکن یا وی پی ان وارد سایت بشید .