1. مهمان گرامی، جهت ارسال پست، دانلود و سایر امکانات ویژه کاربران عضو، ثبت نام کنید.
    بستن اطلاعیه

اسمبلی یاد بگیریم

شروع موضوع توسط minaaa ‏23/10/11 در انجمن Assembly

  1. کاربر پیشرفته

    تاریخ عضویت:
    ‏9/12/10
    ارسال ها:
    19,795
    تشکر شده:
    6,456
    امتیاز دستاورد:
    113
    برای یاد گرفتن اسمبلی باید با مبناهای عدد نویسی ، ساختمان داخلی کامپیوتر
    و برنامه نویسی آشنا باشیم .
    ما برنامه هایمان را مستقیما با اسمبلر Macro Assembler خواهیم نوشت و گاها از Debug
    استفاده خواهیم کرد . بعلاوه چون برنامه های حجیم نخواهیم نوشت قالب اکثر
    رنامه های ما COM. خواهد بود .
    برای شروع ابتدا نگاهی به حافظه میکنیم :

    حافظه و آدرس دهی

    هر کامپیوتر مبتنی بر ۸۰۸۶ دارای حداقل ۶۴۰ کیلوبایت حافظه است . این ۶۴۰
    کیلوبایت به قطعات ۶۴ کیلوبایتی تقسیم شده و ما این قطعات را “قطعه ” یا Segment
    مینامیم . هر سگمنت هم به خانه های تک بایتی دیگری تقسیم شده است .

    برای بدست آوردن مقدار یک بایت مشخص از حافظه ما باید عد مربوط به سگمنت و
    همچنین شماره آن بایت در سگمنت ( که آفست Offset نامیده میشود ) را بدانیم .
    مثلا اگر مقدار مورد نظر در قطعه ۰۰۳۰h(h( یعنی عدد در مبنای ۱۶ است ) و آفست ۱۳C4h
    باشد ما باید قطعه ای که شماره آن ۰۰۳۰h است را بیابیم و بعد در همان قطعه
    مقدار باین شماره ۱۳C4 را بخوانیم .
    برای نمایش این حالت بین عدد سگمنت و آفست علامت (:) قرار میدهیم . یعنی
    ابتدا عدد مربوط به قطعه را نوشته و سپس عدد آفست را می آوریم :
    Segment:Offset

    مثال : ۴D2F:َ۹۰۰۰ **
    همیشه در آدرس دهی ها از اعداد مبنای ۱۶ استفاده میکنیم .

    | | |

    | CConvertional | 1 Segment=64K | | | | | Memory

    | | | | | |
    | | | |
    | | | |

    ثباتها Registers

    رجیسترها مکان هائی از CPU هستند که برای نگهداری داده ها (DATA) و کنترل اجرای
    برنامه بکار میروند . ما میتوانیم آنها را مقدار دهی کرده و یا بخوانیم و یا
    باتغییر محتوای آنها CPU را مجبور به انجام یک پروسه (رویه یا Procedure) کنیم

    دسته ای از رجیسترها که ما انها را “ثباتهای همه کاره یا همه منظوره ” میخوانیم
    و شامل AX/BX/CX/DX هستند ، برای انتقال مقادیر بین رجیستر ها و CPU بکار میروند.
    این ثباتها را میتوانیم به هر نحوی تغییر دهیم و مقادیری را به آنهاارسال کنیم .

    ثباتهای دیگری هم که نام میبریم کاربردهای خاص خودشان را دارند و برای مقدار دهی
    آنها باید قواعد خاصی (که توضیح خواهیم داد) را بکار بریم .

    میکند عدد که در این ثبات وجود دارد شماره یک قطعه است و CPU برای یافتن DS : مخفف Data Segment . محل نگهداری متغییرها و ثابتهای برنامه را مشخص
    مقادیر لازم به آن قطعه مراجعه میکند . CS

    : مخفف Code Segment است و آدرس قطعه ای که برنامه در آن قرار گرفته را
    نشان میدهد . ES

    : این یک ثبات کمکی است و معمولا در آدرس دهی ها شماره قطعه را نگهداری
    میکند . DI

    DataIndex:Dبا DS/ESا مرتبط است و عدد آفست را نگهداری میکند . IP

    : این رجیستر معلوم میکند که برنامه در حال اجرائی که در CS قرار دارد از
    کدام بایت قطقه (یعنی کدام آفست ) شروع میشود . به همین دلیل همیشه این دو
    ثبات را با هم و بصورت CS:IP نشان میدهند.
    و …

    تمام رجیسترهای فوق ۱۶ بیتی (دوبایتی ) هستند و اعداد دوبایتی را نگهداری میکنند.
    ثباتهای همه منظوره به دو نیم ثبات تک بایتی تقسیم میشوند . بایت بالائی ب
    نماد H و بایت پائینی با نماد L نشان داده میشود . مثلا ثبات AX دارای دو نیم -
    ثبات AH/AL است :
    | AH – 8 Bit | AL -8 Bit |


    تمرین :
    برای دیدن رجیسترها در DOS، DEBUG، را اجرا کنید و فرمان R را صادر کنید :

    D:\MASM>DEBUG
    -R
    AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
    DS=17AA ES=17AA SS=17AA CS=17AA IP=0100 NV UP EI PL NZ NA PO NC
    17AA:0100 0F​
     
  2. کاربر پیشرفته

    تاریخ عضویت:
    ‏9/12/10
    ارسال ها:
    19,795
    تشکر شده:
    6,456
    امتیاز دستاورد:
    113
    پاسخ : اسمبلی یاد بگیریم

    خوب ، رجیسترها را دیدیم و آشنائی کلی با آنها پیدا کردیم .
    حالا میخواهیم به رجیتسرها مقدار بدهیم و آنها را بخوانیم و … . ما معمولا در
    ےزبانهای دیگر از علامت =(یا =ا:) برای مقدار دهی استفاده میکنیم ولی در زبان
    ےاسمبلی این کار ممکن نیست . در عوض از دستورالعمل MOV کمک میگیریم . با MOV
    میتوانیم داده ها را بین رجیسترها یا خانه های حافظه انتقال بدهیم . به این صورت
    MOV in_it/Value

    در اینجا In_it به معنای یک رجیستر، نام یک متغیر، یا آدرس یک مکان از حافظه
    است و Value هم یک مقدار عددی یا حرفی ، نام یک رجیستر و … میباشد .
    ےمانند MOV AX/200 که عدد ۲۰۰ دسیمال را به رجیستر AX منتقل میکند . (همیشه از
    سمت راست به چپ ) .

    در زبان اسمبلی ما میتوانیم با مبناهای ۲وَ۱۰وَ۱۶ کار کنیم . اعداد به طور پیش
    فرض مبنای ۱۰ هستند . برای نشان دادن عدد هگزا (مبنای ۱۶) در انتهای عدد یک
    حرف H ( یا h ) و در انتهای اعداد باینری علامت (b) قرار میدهیم . مثلا برای نشان
    دادن عدد AC1 مبنای ۱۶ باید حتما آن را بصورت AC1h بنویسیم . به همین ترتیب عدد۱۱۰b
    همان عدد ۶ خودمان است .

    با این تفاسیر برای دادن مقدار ۴Ch به رجیستر AX از دستور زیر استفاده میکنیم :
    mov ax/4Ch

    به همین شکل میتوانیم به نیم ثباتها هم مقدار بدهیم . مثلا میتوانیم برای مقدار
    دهی AH بنویسیم : mov ah/20h . در این حالت مقدار نیم ثبات AL ثابت بوده و
    محتوای AH برابر ۲۰h میشود . چون نیم ثباتها تک بایتی هستند ما نمیتوانیم عدد
    خارج از محدوده ۰ تا ۲۵۵ یا ۱۲۸- تا ۱۲۷ به آنها ارسال کنیم . در مورد اعداد منفی
    هم باید از طریق تبدیل به مکمل دو عمل کنیم که به زودی آن روش را توضیح خواهیم
    اد .
    مثلا ما نمیتوانیم mov ah/100h را انجام دهیم چون ۱۰۰h برابر ۲۵۶ بوده و از محدوده
    تعریف شده خارج است .
    به همین شکل میتوانیم محتوای ثباتها را هم منتقل کنیم . مثلا برای کپی کردن محتوای
    ثبات CXبه DX میتوانیم بنویسیم : mov dx/cx ، یعنی مقدار داخل Cx را به Dx کپی
    کن .
    ےباز هم باید به یک یا دوبایتی بودن ثباتها توجه کنیم . به عبارت دیگر ما
    ےنمیتوانیم مقدار یک ثبات تک بایتی را به یک ثبات کامل دوبایتی منتقل کنیم .
    مثلا عبارت mov DX/AL قابل قبول نیست چون AL یک بایتی بوده و DX دوبایتی است .
    به عبارت ساده و کامل تر دو طرف عملوند MOV باید از نظر اندازه برابر باشند.
    بنابر این :
    MOV DL/AL
    و MOV CL/BHوM درست ولی MOV DS/AH نادرست است .

    به علاوه ما فقط میتوانیم ثباتهای همه منظوره AXتا DX را به این صورت مقدار دهی
    ےکنیم . در صورتی که بخواهیم ثباتهائی مثل ..DS/ES/ را مقدار دهی کنیم باید از
    رجیستر AX به عنوان واسطه استفاده کرده و مقدار را از طریق آن انتقال دهیم .
    مثلا:
    نمیتوانیم بنویسیم mov ds/20h
    ولی میتوانیم داشته باشیم :
    mov ax/20h
    mov ds/ax

    ےبه این ترتیب مقدار ۲۰hبه DS انتقال پیدا میکند ( گرچه تغییر دادن DS ایده خوبی
    نیست !)

    ےحالا مطالب گفته شده را تمرین میکنیم . ما میتوانیم با DEBUG اسمبلی بنویسیم و
    حتی برنامه های COM. درست کنیم . بنا براین در DOS، DEBUG، را اجرا کنید .
    D:\LNG\ASM> DEBUG

    ےیک خط تیره به صورت – ظاهر میشود . این خط تیره اعلان DEUBG برای وارد کردن
    دستورات است .
    حرف A (به معنی شروع وارد کردن دستورات اسمبلی ) را وارد کرده و Enter را بزنید .
    ےعددی بصورت xxxx:0100 ظاهر میشود . این عدد برای شما (فعلا) مهم نیست ، پس به
    آن توجه نکنید .
    حالا میتوانید دستورات زیر را وارد کنید :

    MOV AX/100
    MOV BX/AX
    MOV ES/AX

    بعد از وارد کردن خط آخر یکبار دیگر کلید Enter را بزنید تا اعلان (-) دوباره ظاهر
    شود .
    در سطر اول ما عدد ۱۰۰h ( پیش فرض اعداد در Debug هگزا است ) را به AX منتقل
    کردیم . بعد مقدار AXبه BX و سپس مقدار AXبه ES منتقل شده . به این ترتیب همه
    ثباتهای AX/BX/ES باید در نهایت برابر ۱۰۰h باشند .
    برای دیدن صحت این مطلب دستور T ( به معنای Trace) را وارد کنید .
    با هر بار اجرای این دستور یکی از سطرهای برنامه اجرا میشود . بعلاوه شما میتوانید
    محتوای رجیسترها را هم ببینید .
    با اولین فرمان T ، سطر اول اجرا میشود . بازهم فرمان T را وارد کنید . الان مقدار۱۰۰h
    به BX داده شد و اگر به محتوای رجیستر AX توجه کنید خواهید دید که مقدار آن
    (همانطور که انتظار داشتیم ) برابر ۱۰۰h است . دوبار دیگر هم فرمان T را صادر
    کنید و در نهایت مقدار ثباتهای AX/BX/ES را ببینید . هر سه ثبات (حالا) برابر ۱۰۰h
    هستند .
    برای خارج شدن از Debug هم فرمان Q به معنی Quit را وارد کنید .
    ******

    پس امروز یاد گرفتیم گه چطور مقادیر و داده ها را بین ثباتها منتقل کنیم .
    خودتان همین تمرینات را با DEBUG انجام داده و در مورد MOV مطالعه کنید .
    در قسمت بعد چیزهای بیشتری رو خواهیم خواند و یاد خواهیم گرفت .
     
  3. کاربر پیشرفته

    تاریخ عضویت:
    ‏9/12/10
    ارسال ها:
    19,795
    تشکر شده:
    6,456
    امتیاز دستاورد:
    113
    پاسخ : اسمبلی یاد بگیریم

    تا اینجا یاد گرفتیم که چطور مقادیر را بین ثباتها منتقل کنیم : با فرمان MOV.
    با همین دستور میتوانیم مقادیر را از محلهای حافظه خوانده یا در آنجا بنویسیم .
    برای کار با حافظه دوحالت ممکن است وجود داشته باشد : ۱
    - آدرس مورد نظر در سگمنت جاری باشد . در برنامه های COM. کل برنامه (غالبا)
    از یک سگمنت تشکیل میشود . ۲
    - آدرس مورد نظر خارج از سگمنت جاری باشد .

    ثبات DS همیشه به قطعه ای اشاره میکند که داده های مورد نیاز برنامه در آن
    هستند . این قطعه در برنامه های EXE. یک قطعه مستقل است ولی در برنامه های COM
    . ، قطعه داده های و قطعه کد برنامه در یک سگمنت هستند . بنا براین مقدار
    ثبات DS در یک برنامه COM. ثابت است .
    در حالت کلی آدرس یک محل از حافظه بصورت DS:address مشخص میشود. DS حاوی
    آدرس سگمنت داده ها بوده و address آفست را مشخص میکند .
    چون همانطور که گفتیم DS در برنامه های COM. ثابت است ، پس در صورتی که آدرس
    مورد نظر در همین قطعه باشد از نوشتن DS صرفنظر میکنیم .
    به عنوان مثال اگر قطعه داده های برنامه ما ۹۰۰۰h باشد و ما بخواهیم آفست ۲۴h
    ام در همین قطعه را بدست بیاوریم ، میتوانیم از یکی از دو شکل زیر استفاده
    کنیم :
    DS:24h
    or
    24h

    البته چون اسمبلر منظور ما از نوشتن عدد ۲۴h را نخواهد فهمید شکل دوم یک خطای
    هنگام ترجمه تولید خواهد کرد ولی ما روش صحیح را هم خواهیم گفت .
    ما آدرس ها (یا اشاره گرها) را برای این میخواهیم که بتوانیم به یک خانه از
    حافظه دسترسی پیدا کنیم . برای اینکه نشان بدهیم منظور ما از عدد مشخص شده ،
    آدرس است نه خود عدد (مثل ۲۴h در مثال قبلی ) آن عدد را داخل [] قرار میدهیم .
    بنا براین :
    mov ah/24h عدد ۲۴h را به AX منتقل میکند ولی ….
    mov ah/[24h] محتوای آفست ۲۴h را به AX منتقل میکند .

    در شکل دوم هر مقداری که در آفست ۲۴h ام سگمنت جاری موجود باشد به ثبات Ah
    منتقل میگردد.
    به همین صورت میتوانیم یک مقدار را به یک خانه از حافظه منتقل کنیم : mov [24h]/ah
    : محتوای ثبات AH را به آفست ۲۴h ام منتقل میکند .
    ے اگر آدرس مورد نظر خارج از محدوده سگمنت جاری بوده و در قطعه ای جدا قرار داشته
    باشد ، میتوانیم از DSیا ESا (ترجیحا) برای دستیابی به حافظه استفاده کرد:
    مثال : mov ax/9000h
    mov ds/ax
    mov ah/ds:[89h]

    به این ترتیب ما به آفست ۸۹h از سگمنت ۹۰۰۰h دسترسی پیدا میکنیم .
    البته دستورات فوق مارا به مقصودمان میرسانند ولی ما نمیتوانیم به دلخواه خودمان DS
    را تغییر دهیم چون همانطور که گفتیم DS به قطعه داده های برنامه اشاره میکند و
    برنامه ، داده ها و مقادیر متغیر ها را از سگمنتی که با DS مشخص شده میخواند .
    بنا براین ما نباید مقدار DS را تغییر بدهیم مگر اینکه آن را دوباره به حالت اول
    برگردانیم . برای ذخیره و بازیابی محتوای رجیسترها، یک روش ساده و عمومی وجود
    دارد که به زودی خواهیم گفت ولی در این مثال ما میتوانستیم مقدار قبلی DS را در
    یک رجیستر دیگر مثل CX نگهداریم :

    انتقال محتوای dsبه AX mov ax/ds
    انتقال محتوای AXبه CX mov cx/ax
    دادن مقدار۹۰۰۰hبه AX mov ax/9000h
    انتقال محتوای AXبه DS mov ds/ax
    خواندن آدرس mov ah/ds:[89h]
    بازیابی مقدار DS mov ax/cx mov ds/ax

    اگر بخواهیم آفست آدرس را با یک رجیستر مشخص کنیم باید به نکات زیر توجه
    کنیم : ۱
    - اگر آدرس سگمنت با DS مشخص شده ، یا آدرس در سگمنت جاری باشد ، باید
    مقدار آفست را در ثبات BX قرار دهیم . مثلا mov cx/[BX]یا mov cx/ds:[bx]ا .
    ۲
    - اگر از ES به عنوان مقدار سگمنت استفاده میشود باید از DI به عنوان آفست
    استفاده کنیم مثل mov cx/es:[di] .

    چون ما با برنامه های COM. سرو کار داریم ، پس از شکل اول و BX استفاده خواهیم
    کرد .
    دستیابی به مکانهای حافظه نکته های جالب دیگری هم دارد که در قسمت بعدی یاد
    خواهیم گرفت .
     
  4. کاربر پیشرفته

    تاریخ عضویت:
    ‏9/12/10
    ارسال ها:
    19,795
    تشکر شده:
    6,456
    امتیاز دستاورد:
    113
    پاسخ : اسمبلی یاد بگیریم

    وقتی که ما به روش گفته شده مقداری را از حافظه میخوانیم ، یک داده تک بایتی
    از حافظه گرفته میشود . اما ممکن است بخواهیم که یک کلمه یا کلمه
    مضاعف ( ۴بایتی ) را بخوانیم یا بنویسیم . در این صورت میتوانیم از
    پیشوند های زیر استفاده کنیم :
    Byte Ptr
    : برای دست یابی به یک بایت Word Ptr
    : برای دستیابی به یک کلمه (۲بایت ) Dword Ptr
    : برای دست یابی به یک مقدار ۴ بایتی

    این پیشوند ها را باید قبل از آدرس مورد نظر قرار دهیم . به عنوان مثال برای
    خواندن یک بایت از آفست ۱۰h میتوانیم بنویسیم : mov al/byte ptr ds:[10h]

    و برای خواندن دو بایت بصورت : mov ax/byte ptr ds:[10h] .
    میتوانیم از همین روش استفاده کرده و مقداری را به حافظه انتقال دهیم . مثلا
    میخواهیم یک کلمه دوبایتی را به آفست ۳۴h (در سگمنت برنامه ) منتقل کنیم . کافی
    است بنویسیم :

    mov word ptr [34h]/1FCAh .
    مثال :
    mov bx/34h
    mov ax/ds
    mov cx/ax
    mov ax/00h
    mov ds/ax
    mov ax/word ptr ds:[bx]
    mov ax/cx
    mov ds/ax

    جمع و تفریق

    بحث ما در مورد روشهای دستیابی و انتقال داده ها (فعلا) به پایان میرسد . حالا
    میخواهیم ببینیم که چطور عمل جمع و تفریق ، و بعدا ضرب و … ، را روی مقادیر
    انجام دهیم .

    دستورالعمل ADD به میزان خواسته شده به محتوای یک رجیستر یا متغیر اضافه میکند .
    ےمثلا ADD AH/20 عدد ۲۰ را به AH اضافه کرده و مجددا در AH قرار میدهد . اگر مقدار
    فعلی AH برابر ۳۰ باشد بعد از اجرای آن دستور برابر ۵۰ میشود .
    باید توجه کنیم که حاصل بدست آمده از محدوده مجاز تجاوز نکند . در این مثال اگر
    حاصل جمع عدد ۲۰ با محتوای AH بزرگتر از ۲۵۵ باشد ، خطای سرریز (Over Flow) رخ
    میدهد .
    مثال : این دستورات را در دیباگ وارد کنید : mov ax/5
    add ax/4
    int 20

    (به معنی سطر آخر توجه نکنید) . حالا یکبار دیگر Enter را بزنید تا خط اعلان Debug
    ظاهر شود . حرف G را بزنید تا برنامه شما اجرا شود . حالا فرمان آشنای R را برای
    دیدن محتوای رجیسترها وارد کنید و مقدار AX را ببینید .

    دستورالعمل SUB برعکس ADD بوده و به مقدار خواسته شده از محتوای یک ثبات یا
    متغیر کم میکند . مثلا SUB AX/100h به اندازه ۲۵۶ (۱۰۰h) از AX کم کرده و نتیجه را
    دوباره در AX قرار میدهد .

    مثال : mov bbx/100h SUB bx/50

    در این مثال حاصل bx را از ۱۰۰ به ۵۰ کاهش داده ایم .
    فرمان INC یک حالت خاص از ADD بوده و تنها یکواحد به محتوای ثبات اضافه میکند
    مثلا inc cx یعنی یک واحد به cx اضافه کن .
    و برعکس این ، دستور dec یکواحد از محتوای ثبات کم میکند . مانند : dec cx .
    ے باید توجه کنیم که این دستورات تنها روی ثباتهای همه منظوره DX.AX.D قابل
    استفاده هستند .

    پس امروز مطالب مربوط به اینها رو یاد گرفتیم :
    byte ptr / word ptr / dword ptr
    add / sub / inc / dec
     
  5. کاربر پیشرفته

    تاریخ عضویت:
    ‏9/12/10
    ارسال ها:
    19,795
    تشکر شده:
    6,456
    امتیاز دستاورد:
    113
    پاسخ : اسمبلی یاد بگیریم

    وقفه ها (Interrupts) CPU
    برای اینکه بتواند کارهای مختلفی را انجام دهد،از وقفه ها استفاده میکند . یک
    ےوقفه درخواستی از CPU است که در طی آن زیر برنامه ای اجرا میشود. وقتی که وقفه
    فراخوانی میشود، CPU اعمال دیگر را متوقف کرده و آن اینتراپت را پردازش میکند
    به طور کلی وقفه ها به دودسته تقسیم میشوند:
    ےَ۱- وقفه های سخت افزاری (Hardware Interrupts) . وقفه هائی هستند که از سوی
    ے ادوات سخت افزاری کامپیوتر مانند کیبورد و … اجرا میشوند. مثلا با فشرده یارها
    شدن هر کلید ، یکبار وقفه شماره ۹ فراخوانی میشود. ۲
    - وقفه های سخت افزاری (SoftWare Interrupts). این وقفه ها در بایوس (BIOS)
    کامپیوتر قرار دارند. بایوس کامپیوتر یک تراشه (IC) قابل برنامه ریزی است که
    بنا بر نوع پردازنده بر روی برد اصلی کامپیوتر قرار میگیرد . بعلاوه خود DOS
    نیز وقفه ای (وقفه ۲۱h) را اداره میکند که به وقفه DOS معروف است . این توابع
    توسط MSDOS.SYS تعریف میشوند ولی در نهایت به بایوس مراجعه میکنند.
    هر وقفه دارای یک شماره خاص خود است و از صفر شروع میشود . وقفه ۲۱h (سرویس DOS
    ) نیز دارای ۲۵۵ سرویس دیگر است .
    برای اینکه بتوانیم یک برنامه خوب و مفید بنویسیم باید بتوانیم از اینتراپتها
    به نحو صحیح استفاده کنیم . پس هر برنامه نویس اسمبلی باید یک مرجع کامل
    اینتراپت در اختیار داشته باشد.
    وقتی میخواهیم یک وقفه را فراخوانی کنیم ، ابتدا (درصورت لزوم ) ثباتهای خاصی را
    مقدار دهی میکنیم . معمولا نیم ثبات AH ، از این جهت که اکثر اینتراپتها
    دارای چند سرویس مختلف هستند ، شماره تابع را مشخص میکند . بهمین صورت ، و
    اگر لازم باشد ، ثباتهای دیگر را هم مقدار دهی میکنیم . مثلا فرض کنید میخواهیم
    کلیدی را از صفحه کلید بخوانیم . تابع شماره ۰ از وقفه ۱۶h میتواند این کار را
    انجام دهد . وقتی میگوئیم تابع شماره ۰ ، یعنی باید به AH مقدار ۰ بدهیم و بعد
    اینتراپت ۱۶h را فراخوانی کنیم .
    فراخوانی اینتراپت به سادگی و با دستورالعمل INT انجام میشود. به صورت :
    INT int_no

    که int_no شماره اینتراپت میباشد . در مورد این مثال باید دستورات زیر را انجام
    دهیم : mov ah/0
    int 16h

    وقتی یک وقفه فراخوانی میشود ، ممکن است روی ثباتها تاثیر گذاشته و مقدار آنها
    را عوض کند. به این وسیله ما میتوانیم وضعیت اجرای وقفه را بدست بیاوریم . در
    مورد این مثال ، پس از خوانده شدن کلید ، کد اسکی (ASCII) کلید در ثبات AL قرار
    میگیرد . مثلا اگر حرف A تایپ شود ، مقدار AL برابر ۶۵ خواهد بود.
    حالا اگر عدد AH را قبل از فراخوانی وقفه بجای ۱ برابر Eh قرار دهیم و وقفه ۱۰hرا
    اجرا کنیم ، بجای خواندن کلید، یک کاراکتر را چاپ میکند . به این صورت که کد
    اسکی کاراکتر در ثبات AL و عدد Eh در ثبات AH قرار گرفته و وقفه ۱۰h فراخوانی
    میشود . mov AX/0E07h
    in 10h

    به سطر اول توجه کنید !. وقتی ما یک عدد دوبایتی (Hex) را به AX ارسال میکنیم ،
    دوبایت بالا در AH و دوبایت پائین در AL قرار میگیرد . پس در این مثال کاراکتر
    شماره ۷ باید چاپ شود و چون این کد مربوط به کاراکتر Bell است ، صدای بیپ
    شنیده خواهد شد.

    خاتمه دادن به برنامه :
    وقتی که یک برنامه به انتها رسید یا اگر خواستیم اجرای برنامه را متوقف
    کنیم ، میتوانیم از اینتراپت ۲۰h استفاده کنیم . DOS همیشه و بمحض اجرای این
    وقفه ، اجرای برنامه را متوقه میکند.
    اینراپت ۲۰h فقط با برنامه های COM. درست کار میکند و در مورد برنامه های EXE.
    درست جواب نمیدهد . در عوض سرویس ۴Ch از اینتراپت ۲۱h در هر دونوع برنامه
    بخوبی کار میکند .
    خوب ، حالا با مطالبی که یاد گرفتیم یک برنامه اسمبلی نوشته و فایل COM. آن را
    میسازیم .
    بنابر این در محیط DOS، DEBUG، را اجرا کنید .
    D:MASM>DEBUG

    سپس دستورد A را به معنی شروع دستورات اسمبلی وارد کنید : – A
    xxxx:0100

    به عدد آدرسی که دیده میشود توجه نکرده و دستورات زیر را تایپ کنید . mov ah/2
    mov al/7
    int 16
    int 20

    بعد از تایپ آخرین سطر، یکبار دیگر هم کلید Enter را بزنید تا اعلان debug مجددا
    ظاهر شود. حالا دستور N را برای نامگذاری برنامه بکار ببرید: – N BELL.COM

    بعد از آن باید طول برنامه را ، برحسب بایت ، مشخص کنیم . طول برنامه در ثبات CX
    نگهداری میشود پس از فرمان RCX برای مقدار دهی استفاده میکنیم . (طول برنامه ۸
    بایت است ) . – RCX
    8

    و در نهایت فرمان w برای نوشتن روی دیسک و Q برای خروج . حالا ما یک فایل COM.
    داریم که به محض اجرا یک صدای Beep تولید میکند .

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

    تاریخ عضویت:
    ‏9/12/10
    ارسال ها:
    19,795
    تشکر شده:
    6,456
    امتیاز دستاورد:
    113
    پاسخ : اسمبلی یاد بگیریم

    در این قسمت طرز استفاده از ماکرواسمبلر را یاد میگیریم و برنامه هایمان را بدون
    استفاده از Debug مینویسیم .
    برای استفاده از اسمبلر باید یک ادیتور اسکی مثل EDITیا PE2ا داشته باشید تا
    بتوانید برنامه هایتان را توسط آن تایپ کنید . هر برنامه اسمبلی دارای یک فایل
    منبع (Source) حاوی دستورالعملهای اسمبلی است . ما این فایل را با یک ویرایشگر
    تایپ کرده و به ماکرواسمبلر MASM.EXE میدهیم تا فایل مفعولی (OBJ.) آن را بسازد
    . این فایل هم باید با برنامه Link.exe به فرم EXE. تبدیل شود . چون ما میخواهیم
    برنامه های COM. بتویسیم باید فایل exe. تولید شده را با EXE2BIN.COMیا EXE2COMا
    به فرم com. تبدیل کنیم .
    فرض کنید در محیط ویرایشگر(مثلا EDIT ) هستیم و میخواهیم یک برنامه اسمبلی
    بنویسیم .
    هر برنامه از ۳ قطعه (سگمنت ) تشکیل میشود : ۱
    -قطعه داده ها یا DATA SEGMENT . متغیرهای برنامه و سایر داده های مورد نیاز در
    این سگمن قرار میگیرند . ۲
    - قطعه کد یا Code Segment . کدها و دستورات اسمبلی در این قسمت هستند . ۳
    - بخش انباره یا Stack Segment . این قطعه زیر برنامه ها و مقادیر موقتی را
    نگهداری میکند . ما حتی میتوانیم محتوای ثباتها را به پشته (Stack) منتقل کرده و
    بعد دوباره از آن خارج کنیم .
    در یک برنامه COM. قطعه داده ها و قطعه مد در یک سگمنت قرار دارند بنا براین
    ما قطعه داده ها را تعریف نمیکنیم . بعلاوه قطعه سگمنت هم برای یک فایل COM.
    وجود ندارد بلکه خود DOS این محیط را فراهم میکند . به همین دلایل است که نوشتن
    برنامه های COM. آسانتر است . با این حال ما با محدودیتی مواجه هستیم و آن
    اینست که سایز یک برنامه COM. نمیتواند بیش از ۶۴ کیلو بایت باشد .
    فرض کنید میخواهیم همان برنامه ای که صدای Beepتولید میکرد را با اسمبلر بنویسیم
    پس یک فایل (مثلا bell.asm) میسازیم : EDIT BELL.ASM
    حالا ما در محیط ویرایشگر هستیم . برنامه ما به این شکل خواهد بود :

    . MODEL SMALL
    . CODE
    MOV AH/0EH
    MOV AL/7
    INT 10H
    INT 20H
    END

    در سطر اول ، جمله model small. یک رهنمود مترجم است . رهنمودهای مترجم
    کداجرائی نیستند ولی اسمبلر را در ترجمه برنامه راهنمائی میکنند . MODEL SMALL.
    به اسمبلر میگوید که ما میخواهیم برنامه com. بنویسیم و قطعه داده ها و کدها
    مشترک است . این جمله باید همیشه وجود داشته باشد. CODE
    . میگوید که قسمت کدهای اجرائی شروع میشود . ما باید همیشه دستوراتمان را
    بعد از یک CODE. شروع کنیم و در انتها نیز جمله END را به معنی اتمام برنامه
    بنویسیم .
    بعد از اتمام این مراحل از ویرایشگر خارج شده و با MASM.EXE فایل برنامه را ترجمه
    میکنیم : MASM BELL.ASM
    در پرسشهای masm کلید enter را بزنید . اگر برنامه را صحیح تایپ کرده باشید باید
    این پیغامها را دریافت کنید :

    Microsoft( R )Macro Assembler Version 5.10
    Copyright( C )Microsoft Corp 1981/ 1988 .All rights reserved.
    50084 + 396073 Bytes symbol space free
    0 Warning Errors
    0 Severe Errors

    حالا فایل BELL.OBJ ساخته شده و باید آن را لینک کنیم : LINK BELL.OBJ

    و نتیجه این خواهد بود:

    Microsoft( R )Overlay Linker Version 3.69
    Copyright( C )Microsoft Corp 1983-1988 .All rights reserved.

    :Run File [ASM6.EXE]
    فقط Enter بزنید | :List File [NUL.MAP]
    :Libraries [.LIB] LINK : warning L4021 :no stack segment

    سطر آخر یک پیغام خطا است ولی دقیقا همان چیزی است که انتظار داریم . یعنی
    وجود نداشتن قطعه پشته (Stack) . به همین دلیل برنامه EXE. تولید شده توسط Link
    قابل اجرا نیست . پس با EXE2COM آن را به یک فایل COM. تبدیل میکنیم . EXE2COM BELL.EXE

    و داریم :

    EXE2COM Version 1.0( – c )Computer Magazine
    ASM6.EXE converted to ASM6.COM( 8 Bytes )
    Warning icon razz اسمبلي ياد بگيريم 6 rogram begins at Offset 0( Entry point .)
    ASM6.COM cannot be called directly!

    الان فایل COM. هم تولید شد ولی EXE2COM میگوید که ما نمیتوانیم برنامه را
    فراخوانی و اجرا کنیم . چرا!?
    اگر بیاد داشته باشید وقتی میخواستیم در DEBUG اسمبلی بنویسیم ، دستوراتمان همیشه
    از آدرس xxxx:0100h شروع میشد. دلیل آن اینست که DOS همیشه یک فضای ۲۵۶ بایتی
    بنام PSP در ابتدای برنامه ایجاد کرده و اطلاعات فوق العاده مهمی را در آن
    نگهداری میکند . بنا براین برنامه ما باید حتما از آدرس ۱۰۰h شروع شود . این
    قانون اسمبلر برای نوشتن برنامه های COM. است . پس کد برنامه را به شکل زیر
    اصلاح کنید :

    . MODEL SMALL
    . CODE

    دستورالعمل جدید ORG 100H MOV AH/0EH
    MOV AL/7
    INT 10H
    INT 20H
    END

    راهنمای Org 100hبه DOS میگوید که برنامه باید از آدرس ۱۰۰h شروع شود . ما این
    کد را اجبارا در همه برنامه ها قرار خواهیم داد . حالا برنامه را با تغییرات اعمل
    شده ذخیره کرده و با انجام مراحل قبلی دوباره ترجمه کنید . پس از ترجمه فایل BELL.COM
    را اجرا کرده و نتیجه را مشاهده کنید %

    امروز برنامه ای با اسمبلر نوشیتم . از این پس نیز تمام برنامه های را با
    اسمبلر مینویسیم و از توانائیهای آن استفاده میکنیم .
     
  7. کاربر پیشرفته

    تاریخ عضویت:
    ‏9/12/10
    ارسال ها:
    19,795
    تشکر شده:
    6,456
    امتیاز دستاورد:
    113
    پاسخ : اسمبلی یاد بگیریم

    پرشهای غیر شرطی

    ے اگر با زبانهائی مثل Basicیا Pascalا برنامه نویسی کرده باشید حتما از دستور Goto
    ے هم استفاده کرده اید . بوسیله این فرمان ، ما میتوانستیم روال اجرای برنامه را به
    یک نقطه مشخص انتقال بدهیم بدون اینکه نیاز به برقراری شرط خاصی باشد .
    در زبان اسمبلی هم چنین دستوری داریم : دستورالعمل JMP (مخفف JUMP) .
    دستور JMP به این شکل استفاده میشود:
    برچسب JMP
    ے منظور از برچسب مکانی از برنامه است . در اسمبلی برای اینکه یک نقطه از برنامه
    ے را علامت بزنیم ، نام برچسب مورد نظر را مینویسیم و برای اینکه اسمبلر آن را با
    ے یک دستورالعمل اجرائی اشتباه نکند، کاراکتر (:) را در مقابل آن قرار میدهیم
    مانند: :Start
    سپس میتوانیم با دستور JMP به آنحا پرش کنیم : Start :
    :
    :
    Jmp Start

    دقت کنید که بعد از Start ی که در مقابل JMP نوشتیم علامت : قرار نداده ایم .
    ے این JMP ها از نوع JUMP NERA هستند و نوعی دیگر بنام JUMP FAR هم داریم که بزودی
    آن را هم یاد میگیریم .
    ے مثال : برنامه ای که در مثالهای قبل نوشتیم را در نظر بگیرید . اگر ما از روی
    دستورالعملهای برنامه با JMP پرشی انجام دهیم هیچکدام از آن کدها اجرا نخواهندشد:

    ۱] JMP Quit _
    2] mov ax/0E07h
    3] int 10h
    4] Quit :_
    5] int 20h

    برنامه از روی سطرهای ۲وَ۳ پرش خواهد کرد .

    ثبات پرچم (Flags)

    ے ثبات پرچم یک ثبات ۱۶ بیتی است که ۱یا ۰ا بودن بیتهای آن نشانه درست یا
    ے نادرست بودن یک شرط است . مثلا اگر با دستورالعمل خاصی (میخوانیم ) تست کنیم
    که آیا ثبات BX مقدار ۰ را دارد ، در این صورت بیت ۶ برابر ۰ میشود و … .
    از این ۱۶ بیت فقط ۹ بیت استفاده میشود که به شرح زیر هستند :
    ۱۶ ۱۵ ۱۴ ۱۳ ۱۲ ۱۱ ۱۰ ۹ ۸ ۷ ۶ ۵ ۴ ۳ ۲ ۱ ۰
    * * * * * O D I T S Z * A * P * C

    علامت * به معنای بی استفاده بودن است .

    ے ۱- پرچم نقلی یا (CF) . بیت ۰ در نتیجه اجرای وقفه ها یا بعضی اعمال حسابی تغییر
    میکند .
    ے ۲-پرچم توازن (ZF) . بر اساس یک عمل مقایسه ای یا حسابی تغییر میکند . اگر
    نتیجه یک عبارت ۰ باشد مقدار ۱ و اگر نتیجه ۱ باشد مقدار ۰ میگیرد.
    ے ۳-پرچم وقفه (IF) . اگر ۰ باشد هیچ وقفه ای نمیتواند اجرا شود و اگر ۱ باشد
    میتوان وقفه ها را فراخوانی کرد .

    ے و … . ۶ پرچم دیگر را فعلا لازم نداریم بنا براین توضیحی برای آنها ارائه
    نمیکنیم .

    دستور مقایسه ای CMP

    ے برای مقایسه مقادیراز دستور CMP (مخفف CoMPare) استفاده میکینم . این دستور
    ے مقدار داخل یک ثبات یا متغیر را با مقداری دیگر مقایسه کره و روی ثبات های
    ے CFو ZFو تاثیر میگذارد . بعد از مقایسه میتوانیم بر حسب وضعیت پرچمها پرش
    لازم را انجام دهیم .

    ے مثلا CMP BX/0 تست میکند که آیا مقدار BX برابر ۰ است یا نه . در صورتی که برابر
    ۰
    باشد ،پرچم ZF برابر ۱ میشود .
    با همین دستور CMP میتوانیم کوچکتر،بزرگتر و …. را هم تست کنیم .

    پرشهای شرطی

    برای پرشهای شرطی از دستورهای زیر درست مثل JMP استفاده میکنیم .
    ے JE/JZ : اگر محتوای ZF صفر باشد جهش میکند . اگر دو مقداری که مقایسه کرده ایم
    برابر باشیند پرش انجام میشود.
    ے JNE/JNZ : برعکس JZو JEو هستند و اگر ZF یک باشد (بعبارتی دو مقداری که مقایسه
    کردیم برابر نباشند) جهش انجام میشود.
    ے JA/JNBE . اگر محتوای ثبات یا متغیری که مقایسه کرده ایم بزرگتر از عدد مورد نظر
    باشد پرش انجام میدهد . مثلا :

    mov bh/1
    cmp bh/10
    ja Dest

    ے مقدار BH برابر ۱ است و در سطر دوم تست آن را با ۱۰ مقایسه میکنیم . در سطر سوم
    چون BH بزرگتر از ۱ نیست ، پس پرش JA Dest انجام نمیشود .

    JAE/JNB . اگر بزرگتر یا مساوی باشد ، پرش انجام میشود.
    JB/JNAE: در صورتی که کوچکتر باشد پرش انجام میشود.
    JBE : در صورتی که کوچکتر یا مساوی باشد پرش انجام میشود .

    مثال :
    ے میخواهیم برنامه ای بنویسیم که تمام کاراکترهای بین ۱۲۸ تا ۲۵۵ را
    چاپ کند.

    . MODEL SMALL
    . CODE
    ORG 100H
    START :

    کاراکتر ۱۲۸ برای شروع ; MOV CH/128 CHARS :

    کداسکی را درAL قرار میدهیم تا چاپ شود ; MOV AL/CH
    سرویس ۰Eh برای چاپ کاراکتر ; MOV AH/0EH
    اینتراپت ۱۰h ; INT 10H
    یکواحد به CH اضافه کن ; INC CH
    مقایسه CH با ۲۵۵ ; CMP CH/255
    اگر مساوی نباشد به CHARS پرش میکند ; JNZ CHARS
    پایان ; INT 20H END START

    ے تمام برنامه ساده و روشن است ولی در سطر آخر نکته جدیدی وجود دارد . بعد از
    ے END نام برچسب Start را آورده ایم . نکته ای که در نوشتن برنامه های اسمبلی باید
    ے مراعات کنیم اینست که : اگر از برچسبی در برنامه استفاده میکنیم ، اسمبلر باید
    ے یک برچسب را به عنوان نقطه آغاز کدبرنامه ببیند . به همین خاطر علاوه بر برچسب
    ے CHARS یک برچسب بنام Start هم در ابتدای برنامه تعریف کرده و برای اینکه
    ے اسمبلر بداند مما کدام برچسب را برای انیکار اینخاب کرده ایم ، نام آن را در
    مقابل END می آوریم ، یعنی END START .

    ے نکته دیگر اینکه ، در اسمبلر هر چیزی که بعد از کاراکتر (;) باشد ، توضیح
    ے (Comment) فرض شده و اصلا ترجمه نمیشود . (مثل REM در بیسیک و .. ) . هر comment
    ے باید در یک سطر جای داده شود و اگر از این مقدار بیشتر بود میتوانیم در سطر بعد
    هم یک کاراکتر (;) درج کرده و ادامه توضیحات را بعد از آن بیاوریم .
     
  8. کاربر پیشرفته

    تاریخ عضویت:
    ‏9/12/10
    ارسال ها:
    19,795
    تشکر شده:
    6,456
    امتیاز دستاورد:
    113
    پاسخ : اسمبلی یاد بگیریم

    امروز میخواهیم با مطالبی که خوانده ایم یک برنامه تمرینی بنویسیم . برنامه ای
    برای تعریف رنگهای جدید!
    اگر دارای کارت ویدئوی VGA و طبعا VGA BIOS باشید ، میتوانید از تابع ۱۰H مربوط
    به اینتراپت ۱۰h (که مربوط به سرویسهای تصویری است ) برای تعریف پالت های جدید
    استفاده کنید . تعداد رنگهائی که میتوان آنها را تغییر داد به نوع کارت گرافیک
    و VGA BIOS مربوط است و در حالت عادی رنگهای شماره ۰تا ۷ا قابل تعریف هستند .
    پالت رنگ را به این صورت باید تعریف کنیم : AH=10H
    AL=10H

    شماره رنگ از ۰تا BX= 7ا
    عدد رنگ سبز CH=
    عدد رنگ آبی CL=
    عددرنگ قرمز DH=

    رنگهائی که دیده میشود ، ترکیبی از سه رنگ اصلی قرمز،سبز و آبی (RGB) هستند .
    برای تعریف یک رنگ جدید نیز باید مقدار هر رنگ اصلی در پالت مورد نظر را در
    نیم ثباتهای CH/CLو DHوC قرار دهیم . این مقادیر ۶ بیتی و در محدوده ۱ تا ۶۳
    هستند .
    پس از مقداردهی ثباتها اینتراپت ۱۰h را فراخوانی میکنیم . در مثال زیر ما رنگ
    شماره ۱ که آبی میباشد را تغییر داده ایم .

    تمرین : برنامه را برای رنگهای شماره ۲تا ۷ا نیز با مقادیر دلخواه تکمیل کنید

    . MODEL SMALL
    . CODE
    ORG 100H
    START :
    MOV AH/010H
    MOV AL/010H
    MOV BX/1 ; COLOR NUMBER
    MOV CH/12 ; GREEN VALUE
    MOV CL/24 ; BLUE VALUE – THE 16-BIT NUMBER
    MOV DH/14 ; RED VALUE
    INT 10H ; VIDEO BIOS INT .
    INT 20H ; TERMINATE PROGRAM
    END START

    ما در اینجا وجود VGA BIOS را تست نکرده ایم و اگر این برنامه روی کامپیوتری
    با کارت گرافیک EGA و … اجراشود نتایج غیرقابل پیش بینی بدست خواهد آمد.
    یک راه ساده برای تست وجود کارت VGA وجود دارد . به اینصورت که مقدار ثباتهای AH
    و ALو را به ترتیب برابر ۱Ah و ۰۰h قرار داده و اینتراپت ۱۰h را اجرا میکنیم
    اگر بعد از فراخوانی وقفه ، AL برابر ۱Ah بود یعنی کارت VGA فعال است .
    پس در برنامه ای که نوشتیم میتوانیم با یک دستور CMP ساده از بوجود آمدن خطای
    نبود VGA BIOS جلوگیری کنیم .
    بنا براین برنامه را به این صورت تکمیل میکنیم :

    . MODEL SMALL
    . CODE
    ORG 100H ; BEGINING OFFSET : 100H
    START :

    MOV AH/1AH
    این قسمت را | MOV AL/00
    اضافه کرده ایم | INT 10H CMP AL/1AH ; VGA BIOS EXIST? |

    ; NO UJUMP TO THE END JNZ NOVGA
    MOV AH/010H
    MOV AL/010H
    MOV BX/1 ; COLOR NUMBER
    MOV CH/12 ; GREEN VALUE
    MOV CL/24 ; BLUE VALUE – THE 16-BIT NUMBER
    MOV DH/14 ; RED VALUE
    INT 10H ; VIDEO BIOS INT.
    NOVGA:
    INT 20H ; TERMINATE PROGRAM
    END START

    در برنامه بالا اگر بعد از اجرای وقفه ۱۰h مقدار AL برابر ۱Ah نباشد، نمیتوانیم
    از سرویس تعریف رنگ استفاده کرده و مجبوریم برنامه را با پرش به NOVGA خاتمه
    دهیم .

    در این قسمت با نوشتن یک برنامه ، دو تابع مفید از وقفه ۱۰h را یاد گرفتیم و
    دیدیم که نوشتن یک برنامه اسمبلی برخلاف آنچه تا بحال تصور میکردیم چقدر ساده
    و جالب است .
     
  9. کاربر پیشرفته

    تاریخ عضویت:
    ‏9/12/10
    ارسال ها:
    19,795
    تشکر شده:
    6,456
    امتیاز دستاورد:
    113
    پاسخ : اسمبلی یاد بگیریم

    دستورالعمل LOOP

    تا اینجا هروقت که میخواستیم یک حلقه ایجاد کنیم از دستورالعمل CMP و پرشهای
    شرطی استفاده میکردیم . راه ساده تری برای اجرای مکرر دستورالملها وجود دارد و آن
    استفاده از LOOP است . دستور LOOP به تعداد دفعاتی که با ثبات CX مشخص میکنیم
    حلقه ای را ایجاد میکند .
    برای ایجاد چنین حالتی ابتدا مقدار لازم را در ثبات CX قرار میدهیم . دستور Loop
    همیشه از مقدار CX یک واحد،یک واحد کم میکند تا به ۰ برسد . وقتی که مقدار CX
    برابر ۰ شد ، از حلقه خارج میشود . بنا براین برای ایجاد حلقه ای که ۱۰۰ بار
    تکرار شود، CX را برابر ۱۰۰ قرار میدهیم . MOV CX/100

    حلقه مورد نظر بین دستور loop و یک برچسب انجام میشود . برچسب ، در ابتدای حلقه
    و دستور loop در انتهای آن قرار میگیرد.

    MOV CX/100
    LPCNT :
    :
    :
    :
    LOOP LPCNT

    در بالا با mov cx/100 میخواهیم حلقه ای داشته باشیم که ۱۰۰ بار انجام بشود. وقتی
    به loop lpcnt میرسیم ، یکواحد از مقدار CX کاسته شده و مجددا به lpcnt پرش
    میکنیم .
    به همین سادگی ! .
    تمرین : برنامه ای بنویسید که کاراکتر های با کد اسکی ۳۲ تا ۲۵۵ را نمایش دهد.

    راهنمائی :
    چون همیشه CX در حال کاهش است ، برای اینکه بتوانیم از ۳۲ تا ۲۵۵ برویم ، باید
    عدد داخل CX را برابر ۳۱=۲۲۴-َ۲۵۵ قرار بدهیم تا تعداد ۲۵۴ حرف چاپ بشود. سپس
    مقدار CX را از ۲۵۵ کم کرده و داخل AL قرار میدهیم تا با تابع ۰Eh ار وقفه ۱۰h
    چاپ شود . این وقفه را در برنامه ALLCHR.ASM توضیح دادیم .

    پشته (Stack) ، ذخیره و بازیابی ثباتها

    ما تعدادی ثبات برای نگهداری و انتقال اعداد و مقادیر داریم ولی کافی نیستند .
    بخصوص در DEBUG که نمیتوانیم متغیرتعریف کنیم . یا در برنامه پیش می آید
    بخواهیم برای یک کار خاص و بطور موقت مقدار ثبات را تغییر دهیم ،در این مواقع
    مقدار ثبات را در پشته ذخیره کرده و بعدا مجددا بازیابی میکنیم .
    در برنامه های EXE. پشته یا Stack یک سگمنت مستقل است و آدرس آن در ثبات SS
    (Stack Segment) قرار دارد . در برنامه های COM. پشته به آنصورت وجود ندارد و
    خود DOS فضای لازم را برای برنامه فراهم میکند . در هر صورت ما به اینکه پشته در
    کجاست کاری نداریم و به یک شکل مقادیر را به پشته فرستاده (PUSH) یا از آن
    خارج میکنیم (POP) .
    خاصیت مهمی که در PUSHو POPو کردن مقادیر به پشته وجود دارد اینست که همیشه
    اولین مقداری که به پشته فرستاده میشود، آخرین مقداری است که از پشته خوانده
    میشود . مثلا فرض کنید که ابتدا AX و بعد CX را به پشته میفرستیم . حال برای خارج
    کردن درست این مقادیر، ابتدا CX و بعد AX را خارج میکنیم .
    این قانون اسمبلی است و به (FILO=First In Last Out) معروف است .

    برای فرستادن مقدار یک ثبات به پشته از دستور PUSH استفاده میکنیم .
    مثلا برای قرار دادن AX مینویسیم : PUSH AX . PUSH
    نمیتواند مقدار یک نیم ثبات را در پشته قرار دهد و حتما باید یک ثبات
    کامل دوبایتی باشد .
    وقتی ثباتی را PUSH کردیم ، مقدار آن در Stack نگهداری میشود و میتوانیم مقدار آن
    را تغییر دهیم .
    پس از آن ، از دستور POP برای خارج کردن ثبات از پشته استفاده میکنیم مانند . POP AX

    مثال :

    ۱] MOV AX/0AH ; ax = 0Ah
    2] MOV BX/0BH ; bx = 0Bh
    3] PUSH AX ; push ax to stack
    4] PUSH BX ; hold bx in stack
    5] MOV AX/5 ; now ax=5
    6] MOV BX/2AH ; and bx=2Ah
    7] : ; other commands and
    8] : ; statements …
    9] POP BX ; POP bx from stack
    10] POP AX ; read ax from stack

    در سطر ۳وَ۴ مقادیر axو bxو را به پشته میفرستیم . در سطر ۵و ۶و مقادیر جدید به ax
    bx/ میدهیم و بعد از آن یکسری دستورات دیگر هستند … . در سطر ۱۰ مقدار BX
    از داخل Stack بیرون کشیده میشود . توجه داشته باشید که bx را بعد از ax در پشته
    قرار داده ایم ولی در هنگام خارج کردن به ترتیب عکس عمل میکنیم .

    بعلاوه دستور PUSHF به معنی PUSH FLAGS ، ثبات پرچم را در پشته قرار میدهد . نحوه
    کار با آن هم مثل PUSH معمولی است ولی آرگومان ندارد .
    مثال : PUSHF

    در قسمت بعد، این مطالب را تمرین میکنیم و چند برنامه نمونه میبینیم ، حتی
    یک برنامه گرافیکی با ۲۵۶ رنگ مینویسیم و در آن از دستورات LOOPو PUSHو
    استفاده میکنیم .
     
  10. کاربر پیشرفته

    تاریخ عضویت:
    ‏9/12/10
    ارسال ها:
    19,795
    تشکر شده:
    6,456
    امتیاز دستاورد:
    113
    پاسخ : اسمبلی یاد بگیریم

    در این قسمت یک تمرین دیگر با هم انجام میدهیم و برنامه ای مینویسیم که تعداد ۲۰۰
    رنگ از ۲۵۶ رنگ موجود در حالت ۳۲۰×۲۰۰ گرافیکی را نمایش دهد .
    تابع شماره ۰۰h از وقفه ۱۰h مربوط به تعیین حالت نمایش است . کد مربوط به
    حالت صفحه نمایش در ثبات AL قرار گرفته و وقفه فراخوانی میشود:

    شماره تابع برابر AH=00h
    حالت صفحه نمایش با استفاده از جدول AL= INT 10h

    حالت صفحه نمایش در AL از جدول مخصوص موجود در کتابهای اسمبلی بدست می آید.
    کد مربوط به حالت ۲۵۶C َ۳۲۰×۲۰۰ برابر ۱۳h است بنا براین AL را برابر ۱۳h قرار
    میدهیم .
    برای نمایش و روشن کردن یک نقطه (Pixel) در حالت گرافیکی از تابع ۰Ch همین
    وقفه استفاده میکنیم . یعنی شماره ستون را در CX ، سماره سطر را در DX و شماره
    رنگ را در AL قرار داده و وقفه را اجرا میکنیم . برای اینکه از ستون ۱۹۹ تا ستون
    شماره صفر نقطه روشن کنیم ، CX را برابر ۳۱۹ قرار داده و با دستور LOOP نقاط را
    در یک سطر روشن میکنیم .

    MOV CX/319 ; COLUMN 319 = START COLUMN
    COL:
    INT 10H ; CALL INTERRUPT 10H
    LOOP COL

    سپس DX یا همان شماره سطر را یکواحد افزایش میدهیم و مقدار آن را با ۱۹۹ مقایسه
    میکنیم (چون از ۰ تا ۱۹۹ سطر داریم ) و اگر برابر نبود دوباره عملیات بالا را
    انجام میدهیم .

    بعد از اینکه این عملیات انجام شد، تابع ۰۰h از INT 16h را فراخوانی میکنیم تا
    منتظر دریافت یک کلید از صفحه کلید شود . به این ترتیب میتوانیم نتیجه برنامه
    را مشاهده کنیم و کلیدی را برای اتمام برنامه بزنیم .
    در نهایت باید حالت صفحه نمایش را به مود متنی برگردانیم .
    برای اینکار از همان تابع تعیین مود نمایشی استفاده میکنیم و حالت صفحه نمایش
    که با AL مشخص میشود را برابر ۳ قرار میدهیم .
    برنامه را با روش گفته شده تایپ و کامپایل کنید . اگر از توربو اسمبلر استفاده
    میکنید با فرمان ASM/T.َTASM VGA256 آن را به فرم COM. ترجمه کنید.

    . MODEL SMALL
    . CODE
    ORG 100H
    START :
    MOV AH/00H
    MOV AL/13H
    MOV BX/00H ; PAGE NUMBER
    INT 10H ; SET TO 320×200 256 COLORS

    MOV AH/0CH ; PUTPIXEL FUNCTION
    MOV AL/25 ; COLOR #25
    MOV DX/0 ; ROW 0
    ROW :
    MOV CX/319 ; COLUMN 319 = START COLUMN
    COL :
    INT 10H ; CALL INTERRUPT 10H
    LOOP COL ; DOWN TO CX=0
    INC DX ; DX=DX+1
    INC AL ; AL=AL+1( COLOR NUMBER )
    CMP DX/199 ; IF DX=199
    JNZ ROW ; ELSE JUMP TO ROW

    MOV AH/00H
    INT 16H

    MOV AH/00H ; VIDEO MODE SET
    MOV AL/03H ; 80×25 16 COLORS
    INT 10H ; CALL INT .10H
    INT 20H ; TERMINATE PROGRAM
    END START

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