تبلیغات :
آکوستیک ، فوم شانه تخم مرغی، صداگیر ماینر ، یونولیت
دستگاه جوجه کشی حرفه ای
فروش آنلاین لباس کودک
خرید فالوور ایرانی
خرید فالوور اینستاگرام
خرید ممبر تلگرام

[ + افزودن آگهی متنی جدید ]




صفحه 1 از 4 1234 آخرآخر
نمايش نتايج 1 به 10 از 38

نام تاپيک: آموزش کامل عالی اسمبلی

  1. #1
    آخر فروم باز Nesta's Avatar
    تاريخ عضويت
    Jan 2005
    محل سكونت
    tehran
    پست ها
    3,343

    پيش فرض آموزش کامل عالی اسمبلی

    اسمبلي ياد بگيريم - 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با 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. #2
    آخر فروم باز Nesta's Avatar
    تاريخ عضويت
    Jan 2005
    محل سكونت
    tehran
    پست ها
    3,343

    پيش فرض

    در اين قسمت ميخواهيم با استفاده از مطالبي كه در بخشهاي قبلي ياد گرفتيم
    برنامه اي بنويسيم كه كامل و قابل استفاده باشد . با اين برنامه ميتوانيم
    فلاپي ديسكهاي خودمان را با سرعت كپي كنيم ! امروز برنامه را به شكلي مينويسيم كه
    بتواند ديسكهاي 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
    آدرس بافر براي ذخيره كه DSX است 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. #3
    آخر فروم باز Nesta's Avatar
    تاريخ عضويت
    Jan 2005
    محل سكونت
    tehran
    پست ها
    3,343

    پيش فرض

    خوب ، رجيسترها را ديديم و آشنائي كلي با آنها پيدا كرديم .
    حالا ميخواهيم به رجيتسرها مقدار بدهيم و آنها را بخوانيم و ... . ما معمولا در
    ےزبانهاي ديگر از علامت =(يا =ا براي مقدار دهي استفاده ميكنيم ولي در زبان
    ےاسمبلي اين كار ممكن نيست . در عوض از دستورالعمل 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 را بزنيد .
    ےعددي بصورت xxxx: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. #4
    آخر فروم باز Nesta's Avatar
    تاريخ عضويت
    Jan 2005
    محل سكونت
    tehran
    پست ها
    3,343

    پيش فرض

    تا اينجا ياد گرفتيم كه چطور مقادير را بين ثباتها منتقل كنيم : با فرمان 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. #5
    آخر فروم باز Nesta's Avatar
    تاريخ عضويت
    Jan 2005
    محل سكونت
    tehran
    پست ها
    3,343

    پيش فرض

    وقتي كه ما به روش گفته شده مقداري را از حافظه ميخوانيم ، يك داده تك بايتي
    از حافظه گرفته ميشود . اما ممكن است بخواهيم كه يك كلمه يا كلمه
    مضاعف ( 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 قابل
    استفاده هستند .

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

  6. #6
    آخر فروم باز Nesta's Avatar
    تاريخ عضويت
    Jan 2005
    محل سكونت
    tehran
    پست ها
    3,343

    پيش فرض

    وقفه ها (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
    xxxx: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. این کاربر از Nesta بخاطر این مطلب مفید تشکر کرده است


  8. #7
    آخر فروم باز Nesta's Avatar
    تاريخ عضويت
    Jan 2005
    محل سكونت
    tehran
    پست ها
    3,343

    پيش فرض

    در اين قسمت طرز استفاده از ماكرواسمبلر را ياد ميگيريم و برنامه هايمان را بدون
    استفاده از 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 اسمبلي بنويسيم ، دستوراتمان هميشه
    از آدرس xxxx: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
    را اجرا كرده و نتيجه را مشاهده كنيد %

    امروز برنامه اي با اسمبلر نوشيتم . از اين پس نيز تمام برنامه هاي را با
    اسمبلر مينويسيم و از توانائيهاي آن استفاده ميكنيم .

  9. #8
    آخر فروم باز Nesta's Avatar
    تاريخ عضويت
    Jan 2005
    محل سكونت
    tehran
    پست ها
    3,343

    پيش فرض

    پرشهاي غير شرطي

    ے اگر با زبانهائي مثل 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
    ے بايد در يك سطر جاي داده شود و اگر از اين مقدار بيشتر بود ميتوانيم در سطر بعد
    هم يك كاراكتر ( درج كرده و ادامه توضيحات را بعد از آن بياوريم .


    پايان يخش هفتم

  10. #9
    آخر فروم باز Nesta's Avatar
    تاريخ عضويت
    Jan 2005
    محل سكونت
    tehran
    پست ها
    3,343

    پيش فرض

    برنامه اي
    براي تعريف رنگهاي جديد!
    اگر داراي كارت ويدئوي 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 را ياد گرفتيم و
    ديديم كه نوشتن يك برنامه اسمبلي برخلاف آنچه تا بحال تصور ميكرديم چقدر ساده
    و جالب است .

  11. #10
    آخر فروم باز Nesta's Avatar
    تاريخ عضويت
    Jan 2005
    محل سكونت
    tehran
    پست ها
    3,343

    پيش فرض

    دستورالعمل 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و
    استفاده ميكنيم .

  12. این کاربر از Nesta بخاطر این مطلب مفید تشکر کرده است


صفحه 1 از 4 1234 آخرآخر

Thread Information

Users Browsing this Thread

هم اکنون 1 کاربر در حال مشاهده این تاپیک میباشد. (0 کاربر عضو شده و 1 مهمان)

User Tag List

قوانين ايجاد تاپيک در انجمن

  • شما نمی توانید تاپیک ایحاد کنید
  • شما نمی توانید پاسخی ارسال کنید
  • شما نمی توانید فایل پیوست کنید
  • شما نمی توانید پاسخ خود را ویرایش کنید
  •