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

آموزش اسمبلی از پایه تا پیشرفته

شروع موضوع توسط MiTra ‏14/12/10 در انجمن Assembly

  1. پیشکسوت انجمن کاربر ارزشمند❤

    تاریخ عضویت:
    ‏11/11/10
    ارسال ها:
    11,746
    تشکر شده:
    3,538
    امتیاز دستاورد:
    113
    بخش اول آموزش : 1- چگونه اسمبلی رو شروع کنیم


    برای یاد گرفتن اسمبلی باید با مبناهای عدد نویسی ، ساختمان داخلی كامپیوتر
    و برنامه نویسی آشنا باشیم .
    ما برنامه هایمان را مستقیما با اسمبلر Macro Assembler خواهیم نوشت و گاها از Debug
    استفاده خواهیم كرد . بعلاوه چون برنامه های حجیم نخواهیم نوشت قالب اكثر
    رنامه های ما COM. خواهد بود .
    برای شروع ابتدا نگاهی به حافظه میكنیم :

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

    هر كامپیوتر مبتنی بر 8086 دارای حداقل 640 كیلوبایت حافظه است . این 640
    كیلوبایت به قطعات 64 كیلوبایتی تقسیم شده و ما این قطعات را "قطعه " یا Segment
    مینامیم . هر سگمنت هم به خانه های تك بایتی دیگری تقسیم شده است .

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

    مثال : 4D2F:َ9000 **
    همیشه در آدرس دهی ها از اعداد مبنای 16 استفاده میكنیم .

    | | |

    | 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 نشان میدهند.
    و ...

    تمام رجیسترهای فوق 16 بیتی (دوبایتی ) هستند و اعداد دوبایتی را نگهداری میكنند.
    ثباتهای همه منظوره به دو نیم ثبات تك بایتی تقسیم میشوند . بایت بالائی ب
    نماد 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. پیشکسوت انجمن کاربر ارزشمند❤

    تاریخ عضویت:
    ‏11/11/10
    ارسال ها:
    11,746
    تشکر شده:
    3,538
    امتیاز دستاورد:
    113
    شماره ارسال: #2
    RE:آموزش اسمبلی از پایه تا پیشرفته
    قدم دوم : با اطلاعاتی که تا کنون شروع کردیم به یاد گرفتن . از این به بعد باید این اطلاعا رو جلوه بدیم

    در این قسمت میخواهیم با استفاده از مطالبی كه در بخشهای قبلی یاد گرفتیم
    برنامه ای بنویسیم كه كامل و قابل استفاده باشد . با این برنامه میتوانیم
    فلاپی دیسكهای خودمان را با سرعت كپی كنیم ! امروز برنامه را به شكلی مینویسیم كه
    بتواند دیسكهای 1.44 را بوسیله درایو A كپی كند . بیشتر نیاز ما در كپی (تكثیر)
    دیسكها هم به همین شكل هست . با اینحال در قسمت بعدی نگارش (Version) جدیدتری از
    برنامه را مینویسیم و قابلیت تشخیص نوع دیسك و قابلیت مشخص كردن درایو را به آن
    اضافه میكنیم .
    بهترین كاری كه میتوانیم بكنیم اینست كه بتوانیم داده های خوانده شده از
    دیسك را در حافظه EMS بنویسیم (در این نسخه روی هارددیسك مینویسیم ) . وقتی كه
    نحوه كار را حافظه گسترش یافته (Extended Memory) را هم یاد گرفتیم ، برنامه
    خود را كامل كرده و از آن بعنوان اولین دستختمان در برنامه نویسی اسمبلی لذت
    میبریم .
    لیست برنامه در زیر قرار دارد و توضیحات برنامه را روی آن میبینیم
    قبل از آن یاد آوری میكنم كه هر دیسك HD َ1.44 دارای دو طرف و در هر طرف 80 شیار
    (Track) بوده و هر شیار هم به 18 بخش بنام قطاع (Sector) تقسیم میشود . برنامه
    ما باید محتوای تمام این قطاعها را خوانده و در فایلی روی دیسك سخت ذخیره كند.
    سپس همین داده ها را از فایل خوانده و مجددا روی دیسك جدید بنویسد.



    طول هر قطاع 512 بایت است EQU 512 SECTORSIZE
    تعداد شیار ها 80 شیار (79- 0-) است EQU 79 MAXTRACK
    هر دیسك دو طرف دارد EQU 2 NUMSIDES
    تعداد سكتور در هر شیار 18 تا است EQU 118 SECTOR_PER_TRACK E
    .MODEL SMALL
    .CODE
    ORG 100H
    START:
    JMP MAIN

    بافر برای ذخیره (0)BUF DB SECTORSIZE*SECTOR_PER_TRACK DUP
    داده ها . اندازه آن به اندازه بایتهای یك شیار است
    معرف رویه فعلی دیسك SIDE D DB 0
    معرف تراك جاری TRACK DDB 0
    هندل (مشخصه ) فایل HANDLE DW 0

    اسم فایل برای دخیره موقت داده ها FILENAME DB 'C:TTEMP.$$$'/0

    MSG1 DB 'ENTER A DISK INTO DRIVE A :THEN PRESS A KEY'/13/10/'$'
    MSG2 DB 'ENTER A NEW DISK INTO DRIVE A :THEN PRESS A KEY'/13/10/'$'



    رویه ReadTrack داده های یك شیار را بطور كامل میخواند . برای خواندن یك شیار
    كامل از Int 13h/Ah=02h استفاده كرده ایم . داده ها بعد از خوانده شدن در محلی
    كه با ES:BX مشخص میشود ذخیره میشوند . (به مرجع اینتراپیتها مراجعه كنید) قبلا
    كار با این وقفه را توضیح داده ایم (برنامه Boots.asm را ببینید)


    READTRACK PROC ;READ A TRACK
    PUSH ES
    MOV AX/DS
    MOV ES/AX
    LEA BX/BUF
    MOV AH/2
    MOV DL/0 ;DRIVE A:
    MOV DH/SIDE
    MOV CH/TRACK
    MOV CL/1 ;THE 1st SECTOR
    MOV AL/SECTOR_PER_TRACK
    INT 13H
    POP ES
    RET
    READTRACK ENDP



    این رویه داده های موجود در BUF را خوانده و در یك شیار كامل كه با متغیر Track
    مشخص میشود مینویسد . برای اینكار از INT 13h/AH=03h استفاده شده است . آدرس
    متغیر BUF را باید در ES:BX قرار بدهیم .


    WRITETRACK PROC
    LEA BX/BUF
    PUSH ES
    MOV AX/DS
    MOV ES/AX


    شماره تابع برای نوشتن MOV AH/03
    تعداد سكتورها برای نوشتن MOV AL/SECTOR_PER_TRACK
    شماره تراك MOV CH/TRACK
    شماره سكتور شروع MOV CL/1
    رویه دیسك (طرف دیسك ) MOV DH/SIDE
    شماره درایو كه اینجا A است MOV DL/0 INT 13H
    POP ES
    RET
    WRITETRACK ENDP


    این پروسیجر به اندازه یك تراك كامل از فایل خوانده و در متغیر BUF قرار میدهد
    READFILE PROC
    MOV BX/HANDLE

    اندازه یك تراك MOV CX/SECTORSIZE*SECTOR_PER_TRACK
    آدرس بافر برای ذخیره كه DS:DX است LEA DX/BUF MOV AH/3FH
    INT 21H
    RET
    READFILE ENDP


    این پروسیجر كلیه داده های داخل BUF كه به اندازه یك تراك كامل (18*512 بایت )
    است را خوانده و در فایل مینویسد تا بعدا مجددا خوانده و روی دیسك جدید بنویسد


    WRITEFILE PROC
    MOV BX/HANDLE
    MOV CX/SECTORSIZE*SECTOR_PER_TRACK
    LEA DX/BUF
    MOV AH/40H
    INT 21H
    RET
    WRITEFILE ENDP



    منتظر میماند تا كلیدی فشرده شود WAIT PPROC
    تابع خواندن كلید MOV AH/0 INT 16H
    RET
    WAIT _ENDP



    این رویه فایل با هندل مشخص شده را میبندد CLOSEFILE PROC MOV AH/3EH
    MOV BX/HANDLE
    INT 21H
    RET
    CLOSEFILE ENDP


    شروع برنامه اصلی . MAIN:

    در این قسمت اعذم میكنیم كه دیسكی را در درایو A قرار دهده و كلیدی را
    برنند . MOV AH/9
    LEA DX/MSG1
    INT 21H

    مكث برای دریافت كلید _CALL WAIT

    ساختن فایل برای ذخیره داده ها MOV AH/3CH
    LEA DX/FILENAME
    MOV CX/0
    INT 21H

    MOV SIDE/0
    MOV HANDLE/AX
    MOV TRACK/1

    موتور دیسك خوان مدت زمانی لازم دارد تا به سرعت كافی برسد . بنا براین باید
    یك یا دو بار قبل از خواندن دیسك ، تابع خواندن را اجرا كنیم تا موتور دیسك در
    حالت مناسب قرار بگیرد.
    CALL READTRACK ; START UP THE CASSETTE-MOTOR
    COPY:
    MOV TRACK/0
    COPYTRACK:

    خواندن شیار CALL READTRACK
    نوشتن داده های خوانده شده در دیسك CALL WRITEFILE
    شیار بعدی INC TRACK
    آیا شیار80 هستیم / CMP TRACK/80
    نه ، شیار بعدی TRACKS َ; COPY 80 JNZ COPYTRACK

    طرف بعدی دیسك INC SIDE
    آیا طرف دوم دیسك هستیم ? CMP SIDE/1
    نه ، پس ادامه بده JZ COPY
    وگر نه فایل را ببند CALL CLOSEFILE

    حالا اعلام میكنیم كه دیسك جدید را در درایو A قرار دهد و كلیدی را بزند MOV AH/09H
    LEA DX/MSG2
    INT 21H
    CALL WAIT_

    MOV SIDE/0


    همان فایل را برای خواندن باز میكنیم . وقتی كه فایلی را میسازیم تنها میتوانیم
    در آن فایل بنویسیم . بنا براین برای خواندن از فایل ، باید آن را بسته و مجددا
    برای خواندن باز كنیم . LEA DX/FILENAME
    MOV AH/3DH
    MOV AL/0
    INT 21H

    مشخصه فایل در Handle قرار میگیرد MOV HANDLE/AX

    MOV TRACK/1
    MOV SIDE/0

    اجرای تابع نوشتن برای راه اندازی موتور دیسك CALL WRITETRACK
    WRITE:
    MOV TRACK/0
    WRITE_ON_TRACK:

    داده هارا از فایل بخوان CALL READFILE
    داده ها را روی شیار بنویس CALL WRITETRACK
    شیار بعدی INC TRACK
    آیا شیار 80 هستیم ? CMP TRACK/80
    نه ، پس ادامه بده JNZ WRITE_ON_TRACK
    بله ، طرف بعدی دیسك INC SIDE
    آیا الان طرف دوم را هم خوانده ایم ? CMP SIDE/1
    نه ، پس شیار بعدی را بنویس JZ WRITE
    بله ، فایل را ببند CALL CLOSEFILE

    فایلی كه ساخته بودیم فضائی از دیسك سخت را اشغال كرده ، بنا براین بهتر است
    آن را با استفاده از وقفه 21h و تابع 3Ah حذف كنیم . LEA DX/FILENAME
    MOV AH/3AH
    INT 21H ;ERASE THE TEMPORARY FILE
    INT 20H
    END START
     
  3. پیشکسوت انجمن کاربر ارزشمند❤

    تاریخ عضویت:
    ‏11/11/10
    ارسال ها:
    11,746
    تشکر شده:
    3,538
    امتیاز دستاورد:
    113
    شماره ارسال: #3
    RE:آموزش اسمبلی از پایه تا پیشرفته
    خوب ، رجیسترها را دیدیم و آشنائی كلی با آنها پیدا كردیم .
    حالا میخواهیم به رجیتسرها مقدار بدهیم و آنها را بخوانیم و ... . ما معمولا در
    ےزبانهای دیگر از علامت =(یا =ابرای مقدار دهی استفاده میكنیم ولی در زبان
    ےاسمبلی این كار ممكن نیست . در عوض از دستورالعمل MOV كمك میگیریم . با MOV
    میتوانیم داده ها را بین رجیسترها یا خانه های حافظه انتقال بدهیم . به این صورت
    MOV in_it/Value


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

    در زبان اسمبلی ما میتوانیم با مبناهای 2وَ10وَ16 كار كنیم . اعداد به طور پیش
    فرض مبنای 10 هستند . برای نشان دادن عدد هگزا (مبنای 16) در انتهای عدد یك
    حرف H ( یا h ) و در انتهای اعداد باینری علامت (b) قرار میدهیم . مثلا برای نشان
    دادن عدد AC1 مبنای 16 باید حتما آن را بصورت AC1h بنویسیم . به همین ترتیب عدد110b
    همان عدد 6 خودمان است .

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


    به همین شكل میتوانیم به نیم ثباتها هم مقدار بدهیم . مثلا میتوانیم برای مقدار
    دهی AH بنویسیم : mov ah/20h . در این حالت مقدار نیم ثبات AL ثابت بوده و
    محتوای AH برابر 20h میشود . چون نیم ثباتها تك بایتی هستند ما نمیتوانیم عدد
    خارج از محدوده 0 تا 255 یا 128- تا 127 به آنها ارسال كنیم . در مورد اعداد منفی
    هم باید از طریق تبدیل به مكمل دو عمل كنیم كه به زودی آن روش را توضیح خواهیم
    اد .
    مثلا ما نمیتوانیم mov ah/100h را انجام دهیم چون 100h برابر 256 بوده و از محدوده
    تعریف شده خارج است .
    به همین شكل میتوانیم محتوای ثباتها را هم منتقل كنیم . مثلا برای كپی كردن محتوای
    ثبات 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


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

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


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


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



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


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

    تاریخ عضویت:
    ‏11/11/10
    ارسال ها:
    11,746
    تشکر شده:
    3,538
    امتیاز دستاورد:
    113
    شماره ارسال: #4
    RE:آموزش اسمبلی از پایه تا پیشرفته
    تا اینجا یاد گرفتیم كه چطور مقادیر را بین ثباتها منتقل كنیم : با فرمان MOV.
    با همین دستور میتوانیم مقادیر را از محلهای حافظه خوانده یا در آنجا بنویسیم .
    برای كار با حافظه دوحالت ممكن است وجود داشته باشد : 1
    - آدرس مورد نظر در سگمنت جاری باشد . در برنامه های COM. كل برنامه (غالبا)
    از یك سگمنت تشكیل میشود . 2
    - آدرس مورد نظر خارج از سگمنت جاری باشد .

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


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

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


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

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


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

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

    تاریخ عضویت:
    ‏11/11/10
    ارسال ها:
    11,746
    تشکر شده:
    3,538
    امتیاز دستاورد:
    113
    شماره ارسال: #5
    RE:آموزش اسمبلی از پایه تا پیشرفته
    وقتی كه ما به روش گفته شده مقداری را از حافظه میخوانیم ، یك داده تك بایتی
    از حافظه گرفته میشود . اما ممكن است بخواهیم كه یك كلمه یا كلمه
    مضاعف ( 4بایتی ) را بخوانیم یا بنویسیم . در این صورت میتوانیم از
    پیشوند های زیر استفاده كنیم :
    Byte Ptr
    : برای دست یابی به یك بایت Word Ptr
    : برای دستیابی به یك كلمه (2بایت ) Dword Ptr
    : برای دست یابی به یك مقدار 4 بایتی

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

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

    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 عدد 20 را به AH اضافه كرده و مجددا در AH قرار میدهد . اگر مقدار
    فعلی AH برابر 30 باشد بعد از اجرای آن دستور برابر 50 میشود .
    باید توجه كنیم كه حاصل بدست آمده از محدوده مجاز تجاوز نكند . در این مثال اگر
    حاصل جمع عدد 20 با محتوای AH بزرگتر از 255 باشد ، خطای سرریز (Over Flow) رخ
    میدهد .
    مثال : این دستورات را در دیباگ وارد كنید : mov ax/5
    add ax/4
    int 20


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

    دستورالعمل SUB برعكس ADD بوده و به مقدار خواسته شده از محتوای یك ثبات یا
    متغیر كم میكند . مثلا SUB AX/100h به اندازه 256 (100h) از AX كم كرده و نتیجه را
    دوباره در AX قرار میدهد .

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


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

    تاریخ عضویت:
    ‏11/11/10
    ارسال ها:
    11,746
    تشکر شده:
    3,538
    امتیاز دستاورد:
    113
    شماره ارسال: #6
    RE:آموزش اسمبلی از پایه تا پیشرفته
    وقفه ها (Interrupts) CPU

    برای اینكه بتواند كارهای مختلفی را انجام دهد،از وقفه ها استفاده میكند . یك
    ےوقفه درخواستی از CPU است كه در طی آن زیر برنامه ای اجرا میشود. وقتی كه وقفه
    فراخوانی میشود، CPU اعمال دیگر را متوقف كرده و آن اینتراپت را پردازش میكند
    به طور كلی وقفه ها به دودسته تقسیم میشوند:
    ےَ1- وقفه های سخت افزاری (Hardware Interrupts) . وقفه هائی هستند كه از سوی
    ے ادوات سخت افزاری كامپیوتر مانند كیبورد و ... اجرا میشوند. مثلا با فشرده یارها
    شدن هر كلید ، یكبار وقفه شماره 9 فراخوانی میشود. 2
    - وقفه های سخت افزاری (SoftWare Interrupts). این وقفه ها در بایوس (BIOS)
    كامپیوتر قرار دارند. بایوس كامپیوتر یك تراشه (IC) قابل برنامه ریزی است كه
    بنا بر نوع پردازنده بر روی برد اصلی كامپیوتر قرار میگیرد . بعلاوه خود DOS
    نیز وقفه ای (وقفه 21h) را اداره میكند كه به وقفه DOS معروف است . این توابع
    توسط MSDOS.SYS تعریف میشوند ولی در نهایت به بایوس مراجعه میكنند.
    هر وقفه دارای یك شماره خاص خود است و از صفر شروع میشود . وقفه 21h (سرویس DOS
    ) نیز دارای 255 سرویس دیگر است .
    برای اینكه بتوانیم یك برنامه خوب و مفید بنویسیم باید بتوانیم از اینتراپتها
    به نحو صحیح استفاده كنیم . پس هر برنامه نویس اسمبلی باید یك مرجع كامل
    اینتراپت در اختیار داشته باشد.
    وقتی میخواهیم یك وقفه را فراخوانی كنیم ، ابتدا (درصورت لزوم ) ثباتهای خاصی را
    مقدار دهی میكنیم . معمولا نیم ثبات AH ، از این جهت كه اكثر اینتراپتها
    دارای چند سرویس مختلف هستند ، شماره تابع را مشخص میكند . بهمین صورت ، و
    اگر لازم باشد ، ثباتهای دیگر را هم مقدار دهی میكنیم . مثلا فرض كنید میخواهیم
    كلیدی را از صفحه كلید بخوانیم . تابع شماره 0 از وقفه 16h میتواند این كار را
    انجام دهد . وقتی میگوئیم تابع شماره 0 ، یعنی باید به AH مقدار 0 بدهیم و بعد
    اینتراپت 16h را فراخوانی كنیم .
    فراخوانی اینتراپت به سادگی و با دستورالعمل INT انجام میشود. به صورت :
    INT int_no

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

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

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

    خاتمه دادن به برنامه :
    وقتی كه یك برنامه به انتها رسید یا اگر خواستیم اجرای برنامه را متوقف
    كنیم ، میتوانیم از اینتراپت 20h استفاده كنیم . DOS همیشه و بمحض اجرای این
    وقفه ، اجرای برنامه را متوقه میكند.
    اینراپت 20h فقط با برنامه های COM. درست كار میكند و در مورد برنامه های EXE.
    درست جواب نمیدهد . در عوض سرویس 4Ch از اینتراپت 21h در هر دونوع برنامه
    بخوبی كار میكند .
    خوب ، حالا با مطالبی كه یاد گرفتیم یك برنامه اسمبلی نوشته و فایل COM. آن را
    میسازیم .
    بنابر این در محیط DOS، DEBUG، را اجرا كنید .
    D:\MASM>DEBUG

    سپس دستورد A را به معنی شروع دستورات اسمبلی وارد كنید : - A
    ****:0100

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

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

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

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

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

    تاریخ عضویت:
    ‏11/11/10
    ارسال ها:
    11,746
    تشکر شده:
    3,538
    امتیاز دستاورد:
    113
    شماره ارسال: #7
    RE:آموزش اسمبلی از پایه تا پیشرفته
    در این قسمت طرز استفاده از ماكرواسمبلر را یاد میگیریم و برنامه هایمان را بدون
    استفاده از Debug مینویسیم .


    برای استفاده از اسمبلر باید یك ادیتور اسكی مثل EDITیا PE2ا داشته باشید تا
    بتوانید برنامه هایتان را توسط آن تایپ كنید . هر برنامه اسمبلی دارای یك فایل
    منبع (Source) حاوی دستورالعملهای اسمبلی است . ما این فایل را با یك ویرایشگر
    تایپ كرده و به ماكرواسمبلر MASM.EXE میدهیم تا فایل مفعولی (OBJ.) آن را بسازد
    . این فایل هم باید با برنامه Link.exe به فرم EXE. تبدیل شود . چون ما میخواهیم
    برنامه های COM. بتویسیم باید فایل exe. تولید شده را با EXE2BIN.COMیا EXE2COMا
    به فرم com. تبدیل كنیم .
    فرض كنید در محیط ویرایشگر(مثلا EDIT ) هستیم و میخواهیم یك برنامه اسمبلی
    بنویسیم .
    هر برنامه از 3 قطعه (سگمنت ) تشكیل میشود : 1
    -قطعه داده ها یا DATA SEGMENT . متغیرهای برنامه و سایر داده های مورد نیاز در
    این سگمن قرار میگیرند . 2
    - قطعه كد یا Code Segment . كدها و دستورات اسمبلی در این قسمت هستند . 3
    - بخش انباره یا Stack Segment . این قطعه زیر برنامه ها و مقادیر موقتی را
    نگهداری میكند . ما حتی میتوانیم محتوای ثباتها را به پشته (Stack) منتقل كرده و
    بعد دوباره از آن خارج كنیم .
    در یك برنامه COM. قطعه داده ها و قطعه مد در یك سگمنت قرار دارند بنا براین
    ما قطعه داده ها را تعریف نمیكنیم . بعلاوه قطعه سگمنت هم برای یك فایل COM.
    وجود ندارد بلكه خود DOS این محیط را فراهم میكند . به همین دلایل است كه نوشتن
    برنامه های COM. آسانتر است . با این حال ما با محدودیتی مواجه هستیم و آن
    اینست كه سایز یك برنامه COM. نمیتواند بیش از 64 كیلو بایت باشد .
    فرض كنید میخواهیم همان برنامه ای كه صدای 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 :Program begins at Offset 0( Entry point .)
    ASM6.COM cannot be called directly!


    الان فایل COM. هم تولید شد ولی EXE2COM میگوید كه ما نمیتوانیم برنامه را
    فراخوانی و اجرا كنیم . چرا!?
    اگر بیاد داشته باشید وقتی میخواستیم در DEBUG اسمبلی بنویسیم ، دستوراتمان همیشه
    از آدرس ****:0100h شروع میشد. دلیل آن اینست كه DOS همیشه یك فضای 256 بایتی
    بنام PSP در ابتدای برنامه ایجاد كرده و اطلاعات فوق العاده مهمی را در آن
    نگهداری میكند . بنا براین برنامه ما باید حتما از آدرس 100h شروع شود . این
    قانون اسمبلر برای نوشتن برنامه های COM. است . پس كد برنامه را به شكل زیر
    اصلاح كنید :

    . MODEL SMALL
    . CODE

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


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

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

    تاریخ عضویت:
    ‏11/11/10
    ارسال ها:
    11,746
    تشکر شده:
    3,538
    امتیاز دستاورد:
    113
    شماره ارسال: #8
    RE:آموزش اسمبلی از پایه تا پیشرفته
    پرشهای غیر شرطی

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


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


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


    برنامه از روی سطرهای 2وَ3 پرش خواهد كرد .

    ثبات پرچم (Flags)


    ے ثبات پرچم یك ثبات 16 بیتی است كه 1یا 0ا بودن بیتهای آن نشانه درست یا
    ے نادرست بودن یك شرط است . مثلا اگر با دستورالعمل خاصی (میخوانیم ) تست كنیم
    كه آیا ثبات BX مقدار 0 را دارد ، در این صورت بیت 6 برابر 0 میشود و ... .
    از این 16 بیت فقط 9 بیت استفاده میشود كه به شرح زیر هستند :
    16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
    * * * * * O D I T S Z * A * P * C

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

    ے 1- پرچم نقلی یا (CF) . بیت 0 در نتیجه اجرای وقفه ها یا بعضی اعمال حسابی تغییر
    میكند .
    ے 2-پرچم توازن (ZF) . بر اساس یك عمل مقایسه ای یا حسابی تغییر میكند . اگر
    نتیجه یك عبارت 0 باشد مقدار 1 و اگر نتیجه 1 باشد مقدار 0 میگیرد.
    ے 3-پرچم وقفه (IF) . اگر 0 باشد هیچ وقفه ای نمیتواند اجرا شود و اگر 1 باشد
    میتوان وقفه ها را فراخوانی كرد .

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

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


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

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

    پرشهای شرطی


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


    mov bh/1
    cmp bh/10
    ja Dest



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

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

    مثال :
    ے میخواهیم برنامه ای بنویسیم كه تمام كاراكترهای بین 128 تا 255 را
    چاپ كند.


    . MODEL SMALL
    . CODE
    ORG 100H
    START :

    كاراكتر 128 برای شروع ; MOV CH/128 CHARS :

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



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

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

    تاریخ عضویت:
    ‏11/11/10
    ارسال ها:
    11,746
    تشکر شده:
    3,538
    امتیاز دستاورد:
    113
    شماره ارسال: #9
    RE:آموزش اسمبلی از پایه تا پیشرفته
    برنامه ای برای تعریف رنگهای جدید!
    اگر دارای كارت ویدئوی VGA و طبعا VGA BIOS باشید ، میتوانید از تابع 10H مربوط
    به اینتراپت 10h (كه مربوط به سرویسهای تصویری است ) برای تعریف پالت های جدید
    استفاده كنید . تعداد رنگهائی كه میتوان آنها را تغییر داد به نوع كارت گرافیك
    و VGA BIOS مربوط است و در حالت عادی رنگهای شماره 0تا 7ا قابل تعریف هستند .
    پالت رنگ را به این صورت باید تعریف كنیم : AH=10H
    AL=10H

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

    رنگهائی كه دیده میشود ، تركیبی از سه رنگ اصلی قرمز،سبز و آبی (RGB) هستند .
    برای تعریف یك رنگ جدید نیز باید مقدار هر رنگ اصلی در پالت مورد نظر را در
    نیم ثباتهای CH/CLو DHوC قرار دهیم . این مقادیر 6 بیتی و در محدوده 1 تا 63
    هستند .
    پس از مقداردهی ثباتها اینتراپت 10h را فراخوانی میكنیم . در مثال زیر ما رنگ
    شماره 1 كه آبی میباشد را تغییر داده ایم .

    تمرین : برنامه را برای رنگهای شماره 2تا 7ا نیز با مقادیر دلخواه تكمیل كنید


    . 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و را به ترتیب برابر 1Ah و 00h قرار داده و اینتراپت 10h را اجرا میكنیم
    اگر بعد از فراخوانی وقفه ، AL برابر 1Ah بود یعنی كارت 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



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


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

    تاریخ عضویت:
    ‏11/11/10
    ارسال ها:
    11,746
    تشکر شده:
    3,538
    امتیاز دستاورد:
    113
    شماره ارسال: #10
    RE:آموزش اسمبلی از پایه تا پیشرفته
    دستورالعمل LOOP

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


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


    MOV CX/100
    LPCNT :
    :
    :
    :
    LOOP LPCNT



    در بالا با mov cx/100 میخواهیم حلقه ای داشته باشیم كه 100 بار انجام بشود. وقتی
    به loop lpcnt میرسیم ، یكواحد از مقدار CX كاسته شده و مجددا به lpcnt پرش
    میكنیم .
    به همین سادگی ! .
    تمرین : برنامه ای بنویسید كه كاراكتر های با كد اسكی 32 تا 255 را نمایش دهد.

    راهنمائی :
    چون همیشه CX در حال كاهش است ، برای اینكه بتوانیم از 32 تا 255 برویم ، باید
    عدد داخل CX را برابر 31=224-َ255 قرار بدهیم تا تعداد 254 حرف چاپ بشود. سپس
    مقدار CX را از 255 كم كرده و داخل AL قرار میدهیم تا با تابع 0Eh ار وقفه 10h
    چاپ شود . این وقفه را در برنامه 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


    مثال :


    1] 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



    در سطر 3وَ4 مقادیر axو bxو را به پشته میفرستیم . در سطر 5و 6و مقادیر جدید به ax
    bx/ میدهیم و بعد از آن یكسری دستورات دیگر هستند ... . در سطر 10 مقدار BX
    از داخل Stack بیرون كشیده میشود . توجه داشته باشید كه bx را بعد از ax در پشته
    قرار داده ایم ولی در هنگام خارج كردن به ترتیب عكس عمل میكنیم .

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


    در قسمت بعد، این مطالب را تمرین میكنیم و چند برنامه نمونه میبینیم ، حتی
    یك برنامه گرافیكی با 256 رنگ مینویسیم و در آن از دستورات LOOPو PUSHو
    استفاده میكنیم .