آژانس هواپیماییexchanging

اسمبلي براي سي

شروع موضوع توسط saalek ‏7 فوریه 2006 در انجمن خانواده C++ , C

  1. saalek

    saalek مدیر بازنشسته

    تاریخ عضویت:
    ‏24 می 2005
    نوشته ها:
    654
    تشکر شده:
    53
    محل سکونت:
    در پاي كوهپايه ها
  2. saalek

    saalek مدیر بازنشسته

    تاریخ عضویت:
    ‏24 می 2005
    نوشته ها:
    654
    تشکر شده:
    53
    محل سکونت:
    در پاي كوهپايه ها
    من وقتي به سراغ سي آمدم قصد نداشتم از اسمبلي دور بشوم. و چون محيط سي راحت تر از اسمبلي است ولي نزديك به اسمبلي ، ترجيح دادم در محيط سي كار كنم.

    و چون بعد اين در تاپيك آموزش سي ++ از پايه بيشتر مي خواهم به سمت اسمبلي متمايل بشوم ، اين تاپيك را باز كردم تا اول يك پيش زمينه آشنايي با اسمبلي براي كساني كه با اسمبلي آشنا نيستند داده بشه و بعد من بتوانم راحت تر آنجا صحبت كنم.

    ارتباط با ويندوز را هم مي گذاريم در ويژوال سي. و فعلا با ويندوز كاري نداريم.
    به نقل از تاپيك : ((آي هوار ، برنامه نويسان دلفي هم دل دارند.))

    البته ممكنه برداشت من از فرموده مديريت محترم اشتباه باشه ولي در ويژوال سي با پيغامها با ويندوز ارتباط برقرار مي كنيم. پيغام(مسيج) دريافت مي كنيم و مسيج مي فرستيم..

    در كل من مي خواهم وقتي مي روم سراغ ويژوال سي ، ((اسمبلي و سي و ويژوال سي)) را با هم داشته باشيم.

    در اين تاپيك نمي خواهيم اسمبلي كار بشويم . بلكه آن قسمتي كه در سي لازم است را پي مي گيريم.
     
  3. saalek

    saalek مدیر بازنشسته

    تاریخ عضویت:
    ‏24 می 2005
    نوشته ها:
    654
    تشکر شده:
    53
    محل سکونت:
    در پاي كوهپايه ها
    ما مي توانيم 3 جور اسمبلي كار كنيم:

    روش اول : با همان برنامه debug ويندوز كار كنيم. كه بيشتر براي تمرين و آموزش خوبه.

    روش دوم : فايل سورس اسمبلي توليد كنيم و با اسمبلر(كامپايلر) ها ، مثل masm و tasm و لينك كننده ها (linker ) به exe تبديل كنيم.

    روش سوم : كدهاي اسمبلي خود را در دل كدهاي سي بنويسيم.

    = == = == = == = == = == = == = == = == = == == == = == =

    اما من بيشتر نظرم روي روش سوم است . چون ((اسمبلي در خدمت سي)) هدفم است.

    = == = == = == = == = == == == == == == == == == = == = =

    توضيح روش دوم: همان طور كه ما سورس c يا c++ را با پسوندهاي c و cpp ذخيره مي كنيم و از tc و link براي تبديل به exe استفاده مي كنيم ...

    در اسمبلي هم سورس داريم كه پسوندش asm است و با masm و link تبديل به exe مي كنيم.

    البته من از tasm استفاده مي كنم. چون كمتر error ميده.
    و اصلا شما نياز نداريد كه tasm و masm را داشته باشيد ، چون ما قصد نداريم كه اسمبلي كار كنيم. ما با همان روش سوم كار مي كنيم. ولي من لينك دانلود اين ها را مي گذارم. خيلي هم كوچك هستند، تا كليك كنيد دانلود مي شوند. ولي چون من از كامپيوتر خودم آپلود كرده ام ، ممكنه ويروسي باشه و من پاسخ گوي ويروسي شدن كامپيوتر شما نيستم. با يك سرچ كوچك هم مي توانيد سريع پيدا كنيد آنها را در اينترنت. اگر كسي هم لينك داشت بگذارد.


    من اين دو را داشتم. آپلود كردم.
    tasm & link

    ولي اگر ويروس ياب نداريد ، دانلود نكنيد.

    http://assembly.250free.com/Link.exe
    http://assembly.250free.com/Tasm.exe

    روي هم 150 كيلو.
    .

    ولي با وجودي كه روش دوم لازم نيست ، من طرز سورس سازي و كامپايل در روش دوم را خواهم گفت.
     
  4. saalek

    saalek مدیر بازنشسته

    تاریخ عضویت:
    ‏24 می 2005
    نوشته ها:
    654
    تشکر شده:
    53
    محل سکونت:
    در پاي كوهپايه ها
    روش سوم:
    كه با دو روش داره:

    اول با استفاده از كلمه asm كه از كلمات كليدي سي نيست و از كلمات ((مايكروسافت سي)) است . و در توربو سي معتبر است.
    دوم با همان union و استيل سي.


    با استيل اسمبلي:​
    کد:
    [left]
    #include <iostream.h>
    #include <conio.h>
    
    
    void main()
    {
     clrscr();
    cout<<"hello  "<<2+2<<endl;
    
    asm{
    mov ax,1
    int 33h
    
       }
    
    getch();
    }
    
    
    [/left]

    با استيل سي :
    کد:
    [left]
    #include <iostream.h>
    #include <conio.h>
    #include<dos.h>
    
    void main()
    {
     clrscr();
    cout<<"hello  "<<2+2<<endl;
    
    union REGS i,o;
      i.x.ax=1;
      int86(0x33,&i,&o);
    getch();
    
    }
    [/left]
    در برنامه بالا كه هر دو يكي است در حقيقت در قسمت سي فقط يك hello نوشته ايم و 2+2 و با اسمبلي ماوس را روشن كرده ايم.
    .
     
  5. saalek

    saalek مدیر بازنشسته

    تاریخ عضویت:
    ‏24 می 2005
    نوشته ها:
    654
    تشکر شده:
    53
    محل سکونت:
    در پاي كوهپايه ها
    توضيح روش اول: يعني همون استفاده از debug .

    اول يافتن debug در ويندوز كه با دو روش مي توانيد اين كار را بكنيد.


    روش اول:

    [​IMG]

    [​IMG]



    روش دوم:

    [​IMG]

    [​IMG]


    در روش دوم با نوشتن كلمه debug وارد محيط آن مي شويم و و با زدن q خارج مي شويم.
    وقتي خطي افقي مثل منها ميايد ، يعني در محيط ديباگ هستيم. من كلمه r را هم زدم تا ليست رجيسترهاي cpu به نمايش دربياد. كه بعدا رجيسترها را بحث مي كنيم. و يكي از اهداف اين تاپيك معرفي خوب آنهاست و طرز استفاده از آنها.
    .
    در شكل بالا تمامي رجيسترها در معرض ديد نيست چون من پنجره را كوچك كرده ام. 3 تا از سمت راست رفته بيرون.
     
  6. saalek

    saalek مدیر بازنشسته

    تاریخ عضویت:
    ‏24 می 2005
    نوشته ها:
    654
    تشکر شده:
    53
    محل سکونت:
    در پاي كوهپايه ها

    [​IMG]

    به شكل بالا نگاه كنيد. ابتدا با r من ليست رجيسترها را گرفته ام. و مي بينيد كه ax و bx هر دو صفرند.
    بعد با دستور a و اينتر ، آماده نوشتن كدها شده ام. 3 خط دستور نوشته ام و بعد در خط چهارم ، اينتر كرده ام تا از ((حالت دستور نويسي)) خارج شوم. بعد 3 بار حرف t را زده ام. T كارش اين است كه هر بار يك خط از دستورات ما را اجرا مي كند.
    بعد t اول مي بينيد كه ax تغيير كرده. چون دستور mov باعث شده كه عدد 1111 به ax نسبت داده شود.
    بعد t دوم مي بينيد كه bx تغيير كرده. به دليل قبلي.
    بعد t سوم مي بينيد كه ax تغيير كرده . چون كه add باعث شده كه ax و bx جمع بشوند و در ax قرار بگيرند.
    == == == == === == == === === === === === ===
    البته من برنامه بالا را براي سرگرمي نوشتم. چون قراره كه ما محاسبات را در سي انجام بدهيم و براي چنين كارهايي به سراغ اسمبلي نرويم.
    == == == == == == == == == == == === === ====
    در شكل بالا ، چهار بار ليست رجيسترها به نمايش درآمده. اولي قبل اجراي هيچ دستوري و 3 بار بعد بعد اجراي دستورات ما.
    به ip دقت كنيد. از 100 تا 108 تغيير كرده. و موقعي كه داشتيم دستور مي نوشتيم به آن اعداد قبل دستور نگاه كنيد. آنها هم مشابه ip تغيير كرده اند.
    نتيجه گيري: ip آدرس جايي است كه دستور آماده اجرا آنجا در Ram نشسته. كنترل cpu به ip منتقل مي شود و آن دستور را اجرا مي كند.
    پس در استفاده از حرف t دقت كنيد. چون اين دستور باعث ميشود كه كنترل برود دستور نشسته در آدرس ip را اجرا كند. پس بايد قبل استفاده از اين دستور ip را خودتان به شكل دستي تنظيم كنيد.
    در تمرينات بالا من تنظيم نكرده ام . چون مي دانستم كه بعد هر t خودبه خود ip به دستور بعدي نشانه مي رود و من هم همين را مي خواستم . يعني مي خواستم بعد اجراي هر دستور ، دستور بعدي اجرا شود.
    و حالا اگر بخواهم كار را تكرار كنم اول بايد دوباره ip را 100 كنم. براي اينكار

    کد:
    -r ip  ((inter))
    ip 0100
    : [color=red]_[/color]
    
    طبق كد بالا اول ip را نوشته و اينتر مي كنيم. كه ابتدا ip فعلي را گزارش مي كند و جايي كه با خط قرمز نشان دادم ، كرزر چشمك مي زند و آماده وارد كردن مقدار جديد است كه مقدار جديد را وارد مي كنيم و اينتر مي كنيم.
    بگذريم.


    [​IMG]

    در شكل بالا اول با r يك ليست گرفتيم. بعد با a باز وارد دستور نويسي شده ايم.
    ولي اين بار بجاي t از حرف g استفاده كرديم.
    تفاوتهاي t و g :

    اولي يعني t فقط يك دستور را اجرا مي كند و متوقف مي شود و نتيجه را با نشان دادن رجيسترها در معرض ديد قرار مي دهد ولي دومي يعني g ، تا دستور خاتمه (كه در اينجا int 20 است) به اجراي متوالي دستورات ادامه مي دهد.

    اولي هر تغييري در رجيسترها ايجاد شود را ره حالت اول برنمي گرداند ولي دومي بعد اينكه كارش تمام شد ، تمام رجيسترها (من جمله ip ) را به حالت قبلي در مي آورد.

    حالا خودتان قضاوت كنيد كه هر يك به چه كاري ميايد.
    = == = == == = == = == = == == == == ==
    حالا شرح دستورات :
    ما آمده ايم ah را 2 كرده ايم . و dl را 41 .
    و بعد وقفه 21 را صدا زده ايم. وقفه 21 كه كار نداريم وقفه dos است يا bios ، هزار تا كار انجام مي دهد و چه طوري بهش بگيم الان كدام كارت را انجام بده؟؟
    جواب : با تنظيم ah . با تغيير ah مي گوييم داريم از سرويسهاي فلان وقفه استفاده مي كنم. و اينجا داريم از سرويس 2 وقفه 21 استفاده مي كنيم. و اينجا كارش اينه كه dl را كد يك حرف در نظر مي گيره و آن را چاپ مي كند كه 41 همان كد حرف A است.

    براي خروج هم از وقفه int 20 استفاده كرده ايم. كه اين وقفه كارش اينه كه كنترل را از برنامه ما مي گيره و به dos برمي گرداند و يكسري كارها هم انجام مي دهد كه حالا در سطح من نيست بگم.

    خوب يك برنامه 4 خطي نوشتيم. حالا همين برنامه مي تواند تبديل به يك فايل com بشه و يادگاري براي خود نگه داريم تا هر وقت روي اين فايل كليك كنيم ، يك حرف a براي ما چاپ كنه. البته ضرورتي نداره كه اين كار را ياد بگيريم ولي براي سرگرمي من روش اين كار را مي گويم.
    اول بايد اسمي به فايل بدهيد. كه بايد با پسوند بگيد. كه com است

    کد:
    -n saalek.com
    
    بعد بايد حساب كنيد كه حجم فايل شما چقدر است و در cx قرار بدهيد(اگر بزرگ بود ، ادامه اش در bx ) . در شكل بالا جايي كه اينتر كرديم و از دستورنويسي خارج شديم ، نگاه كنيد ، ip مساوي 108 است . يعني ما از 100 شروع كرده ايم و تا 108 كد نوشته ايم . پس 8 ميشه حجم فايل ما . پس cx را مساوي 8 كنيد. با همان دستور قr . همون جور كه ip را تنظيم كرديد.

    مي رسيم به مرحله آخر كار كه save است. البته بايد قبلش اسم داده باشيد و حجم فايل را تعيين كرده باشيد. براي save ، كافيه حرف w را بزنيد و اينتر كنيد.
    در شكل زير همين مراحل را من انجام داده ام.


    [​IMG]

    فايل شما روي desktop منتظر شماست. و حجمش 8 بايته.
    شايد اين كوچكترين برنامه اي باشه كه مي شد ساخت كه كاري انجام مي دهد!!


    [​IMG]

    اين تمرينات ساده را كرديم تا فقط با رجيسترها و مقدار دهي به آنها مانوس بشه ذهنمان و كار با وقفه.
    دليلي به انجام اين تمرينات از سوي شما نيست. فقط مي خواستم بدانيد كه رجيسترها چه هستند و وقفه چه طوري احضار ميشه.
    در تاپيك اصلي هم از وقفه 33 براي روشن كردن ماوس استفاده كرديم ولي با استيل سي نه با استيل اسمبلي.
    يك هدف من از نوشتن اينها اينه كه يكسري رجيستر خاص را بعدا مي خواهم بكار ببرم كه كارهاي خاصي انجام مي دهند.

    به اسامي رجيسترها نگاه كنيد. يكسري با حرف s ختم مي شوند شامل cs و ds و ss و es . اينها مربوط به آدرس قطعه ها هستند. مثلا cs:ip ميشه آدرسي كه كنترل به آنجا ميره براي اجراي دستور آنجا. مثل آدرس خيابان و كوچه است. Ip ميشه كوچه و cs خيابان.

    قطعه پشته هم با ss و sp معين ميشه. دقيقا مثل cs:ip . داريم ss:sp . كه sp ميشه stack pointer كه فكر كنم نشان مي دهد تا كجا پشته پر شده. كه فعلا چون كتاب جلوم نيست مطمئن نيستم. bp هم شايد با پشته مربوط باشه. فعلا قسمتهايي كه راجع به ادرسهاي پشته گفته شد را با شك بخوانيد تا بعدا بيام اصلاح كنم. ولي بعدا اگر لازم شد درست تر و كامل تر بحث مي كنيم.
    اين كه مي گم اگر لازم شد به اين خاطره كه تا آنجايي كه ميشه ، من مي خواهم از دستورات اسمبلي در تاپيك اصلي استفاده نكنم و اگر كار در محيط اسمبلي كمك كننده بود ، آنگاه به اين سمت(اسمبلي) متمايل مي شوم. پس اگر با دستورات سي ، بشه پشته را آموزش داد و استفاده كرد ، سراغ اسمبلي نمي آيم و آنوقت اين تاپيك فقط جهت يك آشنايي و سرگرمي مطرح است.

    راجع به si و di هم كاربرد خوبي داره براي نوشتن و خواندن در Ram . كه با ds و es تركيبي كار مي كنند. يعني قضيه كوچه و خيابان ، هميشه براي Ram مطرحه.

    اين پست شد اولين آشنايي با اسامي و كار رجيسترها.
    بقيه بماند براي پستهاي بعدي.
    .

    ====================================

    ديباگ قادر است كه فايل com شما را دوباره بخواند. به محيط ms dos prompt برويد و به جايي كه فايل com شما قرار دارد برويد و مثل شكل زير اقدام كنيد. حرف u ليست دستورات را مي دهد كه مي بينيد همان دستوراتي است كه شما نوشته ايد.


    [​IMG]
    .
     
  7. appbannerkhuniresbanner
  8. saeedsmk

    saeedsmk مدیر بازنشسته

    تاریخ عضویت:
    ‏6 سپتامبر 2003
    نوشته ها:
    1,519
    تشکر شده:
    4
    اقا خيلي با حال بودن استفاده كردم .
    مرسي
     
  9. saalek

    saalek مدیر بازنشسته

    تاریخ عضویت:
    ‏24 می 2005
    نوشته ها:
    654
    تشکر شده:
    53
    محل سکونت:
    در پاي كوهپايه ها
    هدف من از پست قبل فقط اين بود كه بدانيد كه دستورات اسمبلي براي خود شخصيت دارند و اگر در قلب يك برنامه سي مي نويسيم به اين معني نيست كه جزيي از سينتكس سي هستند. با همين دستورات اسمبلي است كه خود توربو سي نوشته شده.

    و كار با debug به نوعي يك تفريح بود در اينجا. و همان برنامه پست قبل را حالا من ((درون دل برنامه سي )) مي نويسم و زياد ديگه سمت برنامه debug نمي رويم و با استيل زير كار را ادامه مي دهيم.

    کد:
    #include <iostream.h>
    #include <conio.h>
    
    
    void main()
    {
     clrscr();
    
    [color=red]asm{
    mov ah,2
    mov dl,41h
    int 21h
    
       }[/color]
    
    getch();
    }
    
    برنامه بالا ، دقيقا همان كار را انجام مي دهد . يعني حرف A را چاپ مي كند. فقط بعد 41 يك حرف h من اضافه كردم. يعني هگزايي. بعد 2 هم ميشه اضافه كرد ولي 2 در دهدهي و هگزايي ، هر دو يكي است.

    با يونيون و استيل سي هم ميشه نوشت و فرقي نمي كند. فقط در مبنا ها بايد دقت كرد.
    حالا من كمي تعداد بايتها را شرح مي دهم.
    اول به ax فكر كنيد. Ah و al داره. H و L مخفف كلمات high و low است. و بايت پايين و بايت بالاي ax مي گويند اينها را .
    بايت كه مي دانيد 8 بيت است. هر 4 بيت 16 حالت مي تواند داشته باشد. كه از صفر تا f نام مي برند اين 16 حالت را . كه f همان 15 ميشه. پس a و b و c و d و e هم ميشه 10 تا 14 .
    حالا اگر بخواهيم به قولي ax و ah و al را پر كنيم ميشه :

    کد:
    Ax=ffff
    Ah=ff
    Al=ff
    
    البته ah و al اجزاي ax هستند و با پر كردن ax معلومه كه ah و al هم مقدار دهي شده اند.
    حالا در ديباگ لازم نيست حرف h را بعد عدد بنويسيم . چون خودش به حالت پايه مبناي 16 در نظر ميگيره. ولي وقتي مثل برنامه بالا ،داريم در دل سي كدهاي اسمبلي را مي نويسيم ، به طور پايه مبناي 10 در نظر ميگيره و اگر مبناي 16 مورد نظر ماسا بايد حرف h را بعد عدد بگذاريم تا كامپايلر توربوسي بفهمه كه اينها اعدادي در مبناي 16 هستند.

    وقتي هم كه داريد سورس اسمبلي مي سازيد تا با masm , tasm كامپايل كنيد، بايد به اين قضيه توجه كنيد . بايد ببينيد كه آنجا به طور پايه مبناي 10 در نظر ميگيره يا مبناي 16 . ولي فكر كنم برعكس محيط debug بود. يعني ميشد شبيه محيط توربو سي. يعني مبناي 10 . هر وقت طرز ساختن سورس و كامپايل با اسمبلر را گفتيم اين قضيه را بررسي مي كنيم. ولي فكر كنم اصلا نياز نباشه كه ما سورس اسمبلي بسازيم و با اسمبلر كامپايل كنيم. و همين جوري ((وسط برنامه سي)) بنويسيم خيلي راحت تره. چون محيط كارمون يكپارچه است و نيازي نيست يك اسمبلر هم با خود يدك بكشيم. .
    ولي اگر كسي كارش اسمبلي خالص باشه ، مسلما با tasm و masm كار خواهد كرد نه با توربو سي.

    = === ==== === === === == === ==
    اما وقتي درون برنامه سي ، اين طوري دستورات اسمبلي را (بعد دستورasm) مي نويسيم ، محدوديتهايي داره و حدود 10 محدوديت ميشه كه من مقاله اي در اين مورد دارم. و فعلا لازم نيست بگم. مثلا يكي از آنها مثلا اين ميشه كه نمي توانيد زيربرنامه داشته باشيد و يا مثلا lable داشته باشيد . البته باز اين 2 مورد را با شك گفتم. بايد بعدا اين 10 مورد را نگاه كنم و شايد يك پست كردم و زدم.يا ادرس سايتش را اگر پيدا كردم مي دهم خودتان برويد بخوانيد. ما هم نمي خواهيم كه يك برنامه كامل اسمبلي بكاريم وسط سي كه! تا نياز به اينها داشته باشيم. پس ديگه با اين محدوديتها بحثي نداريم.

    راجع كار با يونيون براي تغيير رجيسترها و احضار وقفه ها هم ، من خودم هنوز مسلط نيستم. وقتي مسلط شدم توضيح كامل مي دهم. و در تاپيك اصلي پست خواهم زد.
    و اين تاپيك براي تمرين جانبي.
    .
     
  10. saalek

    saalek مدیر بازنشسته

    تاریخ عضویت:
    ‏24 می 2005
    نوشته ها:
    654
    تشکر شده:
    53
    محل سکونت:
    در پاي كوهپايه ها
    شما كه استاديد
    شرمنده مي كنيد.
    ===========================
    براي ساختن سورس به روش زير مي شود اقدام كرد:

    کد:
    .model small
    
    .stack
    .code
    
    [color=blue]test proc[/color]
    
    mov ah,2h
    mov dl,41h
    int 21h
    
    mov ah,4ch
    int 21h
    
    [color=blue]test endp[/color]
    
           [color=red] end test[/color]
    
    
    در اديتور خود كدها را بنويسيد و با پسوند
    asm
    ذخيره كنيد. نت پد هم ميشه. كدوايز را هم در تاپيك اصلي براي دانلود گذاشته ام كه مخصوص سورس سازي براي زبانهاي مختلف نتي و سيستمي است. ولي فقط يك اديتوره.
    بعد در محيط dos دستورات زير را براي كامپايل وارد كنيد:

    کد:
    tasm test.asm
    
    link test
    
    همان طور كه تابع اصلي داريم در سي ، اينجا هم داريم. دستوري كه قرمز نشان داده شده ، باعث بستن اين برنامه اصلي مي شود. و با قالب قسمت اصلي مي توانيم زير برنامه هم داشته باشيم كه با proc شروع و با endp تمام مي شود.

    در موقعي كه برنامه لينك را بكار مي بريم يكسري سئوال مي پرسه كه ميشه با زدن اينتر از آنها گذشت.

    در مورد خروج كه در debug با int 20 خروج مي كرديم ، در برنامه هاي exe بايد با سرويس 4c از int 21 خارج بشويم. در debug فايل com بود. و با debug نميشه فايل exe ساخت.
    برنامه هايي براي تبديل اين دو بهم وجود دارد. يعني exe به com و بالعكس.
     
  11. saalek

    saalek مدیر بازنشسته

    تاریخ عضویت:
    ‏24 می 2005
    نوشته ها:
    654
    تشکر شده:
    53
    محل سکونت:
    در پاي كوهپايه ها
    حالا كه محيط debug را گفتيم و طرز سورس asm سازي و كامپايل با tasm را گفتيم ، ديگه ازش استفاده نمي كنيم و به محيط سي برمي گرديم.
    اول همان برنامه چاپ A را در سي مي سازيم.

    کد:
    #include<conio.h>
    #include<dos.h>
    
    void main()						
    {
    clrscr();
             union REGS i,o;
    		 i.h.ah=[color=red]0x[/color]2;
    		 i.h.dl=[color=red]0x[/color]41;
    		 int86([color=red]0x[/color]21,&i,&o);
           getch();
    }
    
    
    با قرار دادن 0x (صفر و ايكس) به كامپايلر مي گيم كه داريم مبناي 16 وارد مي كنيم.
    به زبان ساده شرح برنامه بالا اين ميشه كه ah را 2 كرديم تا از سرويس شماره 2 وقفه 21 استفاده كنيم و بعد حرف A كه كدش 41 است را چاپ كرده ايم.

    در برنامه بعدي، (پاييني) ، من قبل احضار وقفه و بعد احضارش ax و ah را چاپ كرده ام.

    کد:
    #include<conio.h>
    #include<iostream.h>
    #include<dos.h>
    
    
    void main()						
    {
    clrscr();
    	 union REGS i,o;
    		 i.h.ah=0x2;
    		  i.h.al=0x0;
    		 i.h.dl=0x41;
    
    
    cout<<endl<<"i.h.dl="<<i.h.dl<<endl;
    cout<<"i.h.ah="<<i.h.ah<<endl;
    cout<<"i.x.ax="<<i.x.ax<<endl;
    			 int86(0x21,&i,&o);
    cout<<endl<<"i.h.ah="<<i.h.ah<<endl;
    cout<<"i.x.ax="<<i.x.ax<<endl;
    			 getch();
    }
    

    [​IMG]

    در نتيجه اجرا ، يك حرف فارسي ((آ)) ديده ميشه كه وقتي فول اسكرين باشيم ، يك شكلك چاپ ميشه.
    از نتيجه اجراي برنامه بالا نتيجه مي گيريم كه سي ، بايتها را كاراكتري چاپ مي كنه و دو بايت ها را عددي . در بكارگيري ماوس هم چون از dx و cx ، مكان قرار گيري ماوس را استخراج مي كرديم ، به شكل عددي برمي گرداند و استفاده ازش ساده بود.
    ولي چرا ax شده 512 ؟ علتش اينه كه سي چون ax را تركيب al و ah مي دونه ، اين دو را با هم تركيبي گزارش مي دهد. يعني ah را در 256 ضرب مي كنه و با al جمع مي كنه و نتيجه را در ax قرار مي دهد.
    من براي اينكه يادم بمونه اين طوري مي گم. 4 بيت مي تواند 16 حالت داشته باشه(حاصلرب 2 ، چهار بار ضرب در خودش ، و بايت كه 8 بيته ميشه 16*16 كه ميشه 256 .
    پس ah ضرب در 256 ميشه و با al جمع ميشه.
    حالا ما مي توانيم با اين محاسبات بفهميم كه مثلا الان al چند است. چون ax الان 512 است و وقتي بر 256 تقسيم كنيم ، هيچ باقي مانده اي ندارد ، پس براي al چيزي نمي مونه و حاصل تقسيم را هم ah در نظر مي گيريم.

    در برنامه زير اين تقسيم كردن و محاسبه باقي مانده را به خود سي واگذار مي كنم و براي اينكه مطمئن بشم al را درست محاسبه مي كند، مقدار 7 بهش مي دهم. و مي بينم كه 7 هم چاپ ميشه.يعني محاسبات درسته.

    کد:
    #include<conio.h>
    #include<iostream.h>
    #include<dos.h>
    
    
    void main()						
    {
    clrscr();
    	 union REGS i,o;
    		 i.h.ah=0x2;
    		  i.h.al=0x7;
    		 i.h.dl=0x41;
    
    
    cout<<endl<<"i.h.dl="<<i.h.dl<<endl;
    cout<<"i.h.ah="<<i.h.ah<<endl;
    cout<<"i.x.ax="<<i.x.ax<<endl;
    cout<<"    my_ah="<<i.x.ax/256<<endl;
    cout<<"    my_al="<<i.x.ax%256<<endl;
    
    			[color=red] int86(0x21,&i,&o);[/color]
    cout<<endl<<"i.h.dl="<<i.h.dl<<endl;
    cout<<"i.h.ah="<<i.h.ah<<endl;
    cout<<"i.x.ax="<<i.x.ax<<endl;
    cout<<"    my_ah="<<i.x.ax/256<<endl;
    cout<<"    my_al="<<i.x.ax%256<<endl;
    			 getch();
    }
    
    

    [​IMG]

    احضار وقفه همان خط قرمز است كه در نتيجه چاپ ، همان حرف A را چاپ كرده. بقيه نتايج اجرا cout هاي خودمان است.
    .
    ================
    ولي يادتان باشد كه عددي كه سي برمي گردانددر مبناي 10 است و عددي كه ما وارد كرديم در مبناي 16 وارد كرديم. من بجاي 7 آمدم 27 وارد كردم و 39 برگشت .
    يعني 27 در مبناي 16 ، داخلش يك(( 2ضرب در 16 )) هست و يك 7 كه ميشه 39.
    .
    ================
    و مي توانيد اصلا به مبناي 10 بدهيد كه اين مشكلات پيش نياد.
    يعني
    0x
    نگذاريد. ولي شماره سرويس ها را درست تبديل كنيد به مبناي 10
    2 همان 2 ميشه در اينجا ولي سرويسهاي بالاي 9 ديگه اعدادشان در مبناي 10 و 16 بايد حساب بشه.
    همين طور شماره وقفه ها را درست بايد بدهيد. اگر از شماره سرويس را درست ندهيد و وقفه اي را كار بياندازيد ممكنه قفل كنه كه با كنترل +
    break
    خارج بشيد.
    .
     
  12. saalek

    saalek مدیر بازنشسته

    تاریخ عضویت:
    ‏24 می 2005
    نوشته ها:
    654
    تشکر شده:
    53
    محل سکونت:
    در پاي كوهپايه ها
    کد:
    #include<conio.h>
    #include<iostream.h>
    #include<dos.h>
    
    void main()						
    {
    clrscr();
    	 union REGS i,o;
    		 i.h.ah=0x2;
    		 i.h.dl=0x41;
    int86(0x21,&i,&o);  // chaap +++++++++++++++++++
    
    cout<<endl<<"my_dl="<<i.x.dx%256<<endl; // +++++
    
    asm{
    mov ah,2
    
    int 21h            // chaap ++++++++++++++++++++
    
    mov dl,24h
       }
    
    cout<<endl<<"my_dl="<<i.x.dx%256<<endl; // +++++
    
    			 getch();
    }
    
    
    من سعي كردم كه dl را از قسمت سي به قسمت اسمبلي منتقل كنم و از قسمت اسمبلي به سي.
    ولي در هر دو مورد با شكست مواجه شدم. ولي اين شكست ، نوعي پيروزي است چون خيالم راحت شد كه با تغيير رجيسترها در هر قسمت ، اين تغيير به قسمت ديگر منتقل نميشه.

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

    ولي حالا ما بايد راهي پيدا كنيم كه ارتباطي بين اين دو قسمت ايجاد كنيم. يكي از اساتيد با تعريف يك متغير در قسمت سي ، اين ارتباط را با مقدار دهي در دو سو ايجاد كرده بود..
    .
     
  13. saeedsmk

    saeedsmk مدیر بازنشسته

    تاریخ عضویت:
    ‏6 سپتامبر 2003
    نوشته ها:
    1,519
    تشکر شده:
    4
    سلام سالك جان
    با اجازه شما
    فكر كنم دنيال يه همچين چيزي بوديد نه ؟
    کد:
    #include<conio.h>
    #include<string.h>
    
    void main(){
    char strDumm[10];
    	clrscr();
    	// set strDumm="saeed"
    	strcpy(strDumm, "saeed");
    	asm{
    		// set zero al reg need
    		xor ax,ax
    		xor dx,dx
    		xor bx,bx
    		// set bx pointer to strDumm
    		lea bx,strDumm
    		// set function parameter (print char)
    		mov ah,2h}
    loopSec:
    asm{
    		// dl=what bx point to ( another function parameter)
    		mov dl,[bx]
    		// test if dl=0 : we reach to end of string
    		// so we must break loop
    		test dl,dl
    		jz endSec
    		// increase pointer value
    		inc bx
    		// call function
    		int 21h
    		jmp loopSec}
    endSec:
    	getch();
    }
    
     
  14. avajang.com .leftavajang.com.right
  15. saalek

    saalek مدیر بازنشسته

    تاریخ عضویت:
    ‏24 می 2005
    نوشته ها:
    654
    تشکر شده:
    53
    محل سکونت:
    در پاي كوهپايه ها
    احسن.
    عاليه.
    شما آمديد رشته اي ايجاد كرديد و با ايجاد حلقه اي اين رشته را با اسمبلي چاپ كرده ايد.

    و در ضمن يك مانور انجام داده ايد كه آن اين است كه از داخل قسمت اسمبلي به قسمت سي لينك داده ايد با ليبل. كه من در خواب هم اين كار به ذهنم نمي رسيد.

    كلا برنامه خيلي قشنگه. دستتون درد نكنه. اجرا هم كردم . نتيجه اجرا saeed بود.
    من بايد چند تا برنامه بنويسم و كارهاي مختلفي كه شما همه را در اين برنامه واحد انجام داده ايد را تمرين كنم.

    ولي هنوز نكات زيادي در برنامه شما براي من رمزآلود است. شما در قسمت اول اسمبلي ah را تنظيم كرده ايم. (سرويس 2 ) ولي در قسمت دوم اسمبلي وقفه را بكار انداخته ايد.
    چه مكانيسمي نهفته است در اين كار؟ آيا قسمتهاي مختلف اسمبلي براي خود save رجيستر دارند؟ يا ما دو سري رجيستر داريم؟ يكسري براي سي و يكسري براي اسمبلي. چنين چيزي هم بعيد است.
    از دستور test در اسمبلي استفاده كرديد كه من تابحال تجربه استفاده ازش را نداشتم. ولي jz را زياد استفاده كرده ام. كه معنايش اينه كه وقتي فلاگ صفر ، تنظيم بود پرش كن.

    دستور inc هم يك دستور يك بايتي است كه باعث يك واحد افزايش است.
    دستور jmp كه مخفف jump است مثل دستور goto عمل مي كند.

    وقتي يك عدد با خودش xor بشه ، صفر ميشه.
    دستور lea خيلي برام جالبه و دوست دارم سريع يادش بگيرم، خواهشا توضيح بدهيد. .
    دستور strcpy را قبلا در تاپيك اصلي باهاش كار كرديم. يك رشته را در رشته ديگر قرار مي دهد. براي توضيحات بيشتر به آن پست مراجعه كنيد.
    = == == == == == == == === ==
    تكليف شب خودم: من ثابت كردم كه تغيير رجيسترها از سي به اسمبلي و بالعكس منتقل نميشه ولي با نوشتن اين سورس توسط آقا سعيد گل ، من حالا بايد بروم ببينم بين دو تكه اسمبلي چه چيزهايي منتقل ميشه.

    تكليف شب دوم: با lea كمي ور بروم و ببينيم چه كاربردهاي عملي داره.
    = === === === == === = == = =
    بايد برنامه آقا سعيد را بيشتر توضيح مي دادم تا همه طرز كارش را بوضوح بدانند ولي به بعد موكول مي كنم و در برنامه هاي ساده تري كه خواهيم ساخت اين دستورات را تمرين مي كنيم.

    === ==== ==== === === === =
    آقا سعيد دستت درد نكنه ، خيلي قشنگ بود و گره قسمتهاي مهمي را باز مي كنه.
    برنامه دوستمان را توضيح دادم ولي براي آموزش كامل بيشتر لازم بود بحث بشه(شايد تا 6 برابر). آقا سعيد منتظر بعدي ها هم هستيم.
    .
     
  16. saeedsmk

    saeedsmk مدیر بازنشسته

    تاریخ عضویت:
    ‏6 سپتامبر 2003
    نوشته ها:
    1,519
    تشکر شده:
    4
    سلام سالك جان خوبي عزيز ؟

    درمورد سئوال ها تا اونجايي كه علمم اجازه ميده جواب ها تو ميدم
    پرسيده بودي : چه مكانيسمي نهفته است در اين كار؟

    ببين عزيز هنگامي كه يك برنامه كامپايل ميشه توي سي ، بر عكس بيشتر زبان ها تعريف هاي اضافي و فانكشنهاي اضافي صدا زده نميشه به همين دليل اگه شما توي قسمت قبلي asm يه چيز رو تعريف كنيد و دوياره بلا فاصله دستورات asm بعدي را احضار كنيد ( ليبل ها در واقعه كار خاصي را انجام نميدهند) هيچ ريجستري تغيير نميكنه پس انگار توي يك دفعه اين كار را انجام داديد. توي Asm توي سي شما نميتوانيد از ليبل ها استفاده كنيد پس با اين روش اين مشكل حل ميشه
    ببينيد كد برنامه ما پس از كامپايل به اسمبيلي ميشه :
    کد:
       @[email protected]: //start main function
    	call	far ptr _clrscr // call clrscr
    	mov	ax,offset DGROUP:[email protected] 
    	push	ax  // stor address of “saeed”
    	lea	ax,word ptr [bp-10]
    	push	ax // stor address of our array
    	call	far ptr _strcpy
    	pop	cx // correct stack pointer sp
    	pop	cx // correct stack pointer sp
    	xor	 ax,ax // our first asm code
    	xor	 dx,dx
    	xor	 bx,bx
    	lea	 bx,[bp-10] // by this code bx is pointer to our array
    	mov	 ah,2h
    @[email protected]:
    	mov	 dl,c[bx] //now our sec code start u see there is no code between tow 
    			  //separated code	 
    	test	 dl,dl
    	je	short @[email protected]
    	inc	 bx
    	int	 21h
    	jmp	short @[email protected]
    @[email protected]:
    	call	far ptr _getch // call getch
    	mov	sp,bp // restore sp pointer 
    	pop	bp // restore bp 
    	ret	// end main function
    حالا ما اگر كدي ببين اين دو حلقه بگذاريم مثلا همان دستور كپي استرينگ ديگر برنامه ما درست عمل نميكند چون ax تغيير مينماييد.
    پس يكسري reg بيشتر نداريم و چيزي هم ذخيره نميشود.
    - دستور test dl,dl برابر cmp dl,0 است
    - اما دستور lea باعث بار گذاري ادرس متغيير دوم در متغيير اول ميشه
    توي سي اشاره گر ها ادرس متغيير را حمل ميكردن درسته ؟. خوب ما با استفاده از دستور lea توي اسمبلي اشاره گر به حافظه مي سازيم يعني اين دستور
    کد:
    Lea bx,strDumm
    معادله دستور زيره
    کد:
    char *s;
     char b[10];
     s=&b[0];
    فكر كنم كد زير توضيج خوبي براي اين مطلب باشه

    کد:
    #include<conio.h>
    #include<string.h>
    #include<iostream.h>
    
    void main(){
    char strDumm[10];
    char *strP;
    	clrscr();
    	// set strDumm="saeed"
    	strcpy(strDumm, "saeed");
    	asm{
    		// set bx pointer to strDumm
    		lea bx,strDumm
    		// transport this address to our pointer
    		mov strP,bx
    	    }
    	    // print our string;
    	cout << strP;
    	getch();
    }
    - خوب در مورد انتقال اطلاعات همونطور كه توضيح دادم بر اساس نوع برنامه نويسي تغييرات حاصل ميشه من كد پست قبلي رو بصورت زير تغيير دادم تا متوجه ببنيد چه مشكلي ايجاد ميشه

    کد:
    #include<conio.h>
    #include<string.h>
    
    void main(){
    char strDumm[10];
    	clrscr();
    	// set strDumm="saeed"
    	asm{
    		// set zero al reg need
    		xor ax,ax
    		xor dx,dx
    		xor bx,bx
    		// set bx pointer to strDumm
    		lea bx,strDumm
    		// set function parameter (print char)
    		mov ah,2h
    		}
    	strcpy(strDumm, "saeed");
    loopSec:
    asm{
    		// dl=what bx point to ( another function parameter)
    		mov dx,[bx]
    		// test if dl=0 : we reach to end of string
    		// so we must break loop
    		test dl,dl
    		jz endSec
    		// increase pointer value
    		inc bx
    		// call function
    		int 21h
    		jmp loopSec}
    endSec:
    	getch();
    }
    خوب همونطور كه ميبنيد به احتمال زياد عبارتي چاپ نميشود يا عبارت درست چاپ نميشود چون مقدار ax تغيير نموده است
    براي حل اين مشكل كد رو بصورت زير تغيير ميشه داد تا مشكل حل بشه .

    کد:
    void main(){
    char strDumm[10];
    	clrscr();
    
    	asm{
    		// set zero al reg need
    		xor ax,ax
    		xor dx,dx
    		xor bx,bx
    		// set bx pointer to strDumm
    		lea bx,strDumm
    		// set function parameter (print char)
    		mov ah,2h
    		// stor ax in stack
    		push ax}
    	// set strDumm="saeed"
    	// by storing ax in memory we can call our function
    	// and after return from it we can restore our ax
    	strcpy(strDumm, "saeed");
    		// restore ax from stack 
    		asm pop ax;
    loopSec:
    asm{
    		// dl=what bx point to ( another function parameter)
    		mov dx,[bx]
    		// test if dl=0 : we reach to end of string
    		// so we must break loop
    		test dl,dl
    		jz endSec
    		// increase pointer value
    		inc bx
    		// call function
    		int 21h
    		jmp loopSec}
    endSec:
    	getch();
    } 

    اميدوارم كمك كنه :lol:
     
  17. saalek

    saalek مدیر بازنشسته

    تاریخ عضویت:
    ‏24 می 2005
    نوشته ها:
    654
    تشکر شده:
    53
    محل سکونت:
    در پاي كوهپايه ها
    فهميدم قضيه از چه قراره كه منتقل مي شود بين دو قسمت اسمبلي.
    كدهايي را كه نوشتيد را بايد كار كنم.
    .
    ============
    ================
    ====================
    ==============================
    =========================================

    توضيحات كامل بود و بي نقص.
    فهميدم كه با اجراي دستورات سي بين دو قسمت اسمبلي ، رجيسترها تغيير مي كنند و ميشه با push كردن قبل اجراي دستورات سي و pop كردن بعد اجراي دستورات سي ، آن رجيستر را حفظ كرد.
    == == == == === === === ==
    توضيح راجع به پشته: پشته قسمتي از Ram است و رجيسترها حافظه هايي داخل cpu .
    اين جور كه معلومه دستورات سي مدام از رجيسترها استفاده مي كنند. و بايد هم اين طور باشه چون رجيسترها را ميشه gate (دروازه ) هاي cpu دانست. پس وقتي cpu به كار واداشته بشه ، دروازه ها هم محل عبور و مرور خواهند بود. من دقيقا نمي دانم كه توابع سي چه تغييراتي روي رجيسترها مي دهند. ولي در برنامه هاي بالا كه آقا سعيد نوشته بود ، توابع رشته اي مذكور ، رجسترها را تغيير داده اند.

    و پشته هم مي تواند توسط توابع سي مورد استفاده قرار گيرد. و ما هم مي توانيم از آن استفاده كنيم. ولي بايد نوبت رعايت شود. چون هم ما و هم توابع يكجا محموله خود را تلنبار مي كنيم. در برنامه اي كه قبل اجراي توابع رشته اي ما چيزي به پشته ارسال كرديم(محتواي رجيستر ax را) بعدش ممكنه تابع سي هم چيزي روي پشته قرار بدهد كه روي محموله ما قرار مي گيرد. ولي اگر چيزي ان تابع آنجا قرار بدهد ، مسلمه كه بعدش ، خودش آن را برمي دارد و روي محموله ما خالي ميشه و وقتي نوبت ما برسه ، محموله ما (كه اينجا ax است) ، روي آن خالي شده و مي توانيم برش داريم. و من دقيقا نمي دانم آيا توابع سي چيزي روي پشته مي اندازند يا نه ولي دانستنش مهم نيست چون ما وقتي از راه مي رسيم كه تابع هر چه انداخته بوده ، برداشته است. البته ميشه بجاي رجيسترها يك عدد هم روي پشته انداخت ولي معمولا رجيسترها را به پشته مي فرستند تا دست و بالمون خالي بشه تا از رجيستر كار ديگري بخواهيم يا مثل اينجا كه مي خواهيم رجيستري مقدارش محفوظ بماند. پس چون رجيسترها هم توسط ما و هم توسط فرايندهاي خودكار برنامه استفاده مي شه ، مي توانيم مثل كار بالا گاهي از پشته براي ذخيره رجيسترها استفاده كنيم.
    كلا يك قانون ميگه هر مقدار به پشته محموله مي فرستيد ، همان مقدار بايد برداريد. چون نبايد پشته پر بشه. البته اگر 10 تا 20 مقدار هم بگذاريد روي پشته و برنداريد ، مسئله اي نيست چون پشته بزرگه ولي اگر برنامه اي بسازيد كه مدام با دفعات بسيار بالا به پشته چيزي ارسال كند و برندارد ، مسلمه كه پشته دچار مشكل ميشه.

    راجع به پشته بايد مواظب باشيم كه چيزي را كه روند هاي خودكار برنامه در پشته قرار مي دهند را برنداريم. و يا وقتي اين روندها آمده اند چيزي را كه قبلا گذاشته بودند ، بردارند ، با چيزي كه ما گذاشته ايم مواجه نشوند. چون آنها فكر مي كنند كه آن امانتي خوشان است و متوجه نمي شوند كه ما انداخته ايم و برنامه دچار مشكلات جدي مي شود. مثلا در اسمبلي وقتي زير برنامه اي احضار ميشه ، آدرس برگشت در پشته گذاشته ميشه تا بعد اين كه زير برنامه تمام شد توسط آن آدرس به دستور بعدي بعد احضار برگرده . و مي دانيد كه اگر آن آدرس را ما برداشته باشيم با pop يا چيزي روي آن قرار داده باشيم با push چه اتفاقي مي افتد. بله ديگه زيربرنامه نمي تواند به برنامه اصلي برگردد و برنامه فلج مي شود. پس نوبت را رعايت كنيد و وقتي قسمت خودكار چيزي روي پشته گذاشته ، اولا آن را برنداريد و اگر برداشتيد بدانيد چه زماني صاحبش به سراغش ميايد كه سر جايش بگذاريد و چيزي روي پشته نگذاريد و اگر گذاشته ايد بايد بدانيد كه روندهاي خودكار چه زماني به پشته مراجعه مي كنند و قبل مراجعه آنها ، آن چيز را برداريد.
    = == == === == == = == = = = = == = = == =
    راجع به دستور lea خيلي جالب بود و بسيار كاربرد دارد. من بارها اين دستور را ديده بودم ولي نمي دانستم نقشش چيست و حالا فهميدم چه قدر به درد بخور است . مثل اشاره گر است و همان قدر كه اشاره گر كاربرد مي تواند داشته باشد ، اين دستور هم مي تواند كاربرد داشته باشد. ولي هنوز در بكارگيري اش تمرين نكرده ام.
    === == == = = == = = == = == = == = = ===
    راجع به دستور test هم ممنون. من قبلا با cmp آشنا بودم.
    شرح دستور cmp : اين دستور تفريقي است كه انجام نمي شود. پس چه فايده اي دارد؟
    ببينيد عالم محاسبات اسمبلي رابطه تنگاتنگي با فلاگها دارد. فلاگها هم كنار رجيسترها ، حافظه هايي در cpu هستند ولي يك بيتي هستند.
    بهترين اسمي كه من مي توانم به آنها بدهم ، ((نشانه )) است. همين طور كه مي دانيد يك بيت فقط دو حالت دارد. پس كسي كه به يك بيت نگاه مي كند ، يا ان را روشن مي بيند يا خاموش. مثل اينه كه برويد جايي و ببينيد دري باز است. در دو حالت دارد ، يا باز است و يا بسته. اين باز بودن در يعني كسي از در عبور كرده.
    حالا فلاگها را چه چيزي تغيير مي دهد؟ پاسخ : هر محاسبه اي كه انجام شود ، مي تواند يك يا چند فلاگ را تغيير دهد . با باز كردن ديباگ و زدن حرف r همراه نمايش رجيسترها ، 8 فلاگ هم به نمايش در مي آيد. كه با دو حرف نشان داده شده اند.
    مثلا nz . حالا فرض كنيد با دستور add دو عدد را جمع كنيم و ((ده بر يك)) داشته باشيم. اين ((ده بر يك)) باعث ميشه كه چند فلاگ تغيير كنند. پس اگر دو عدد را جمع كنيد و به فلاگها نگاه نكنيد نمي توانيد به جمع كردن خود اعتمادي داشته باشيد.

    شرح cmp : cmp باعث ميشه فلاگها به نوعي تغيير كنند كه انگار تفريق انجام شده ولي دو عدد از هم كم نمي شوند. اگر واقعا مي خواهيد دو عدد را از هم كم كنيد از دستور sub استفاده كنيد.
    == == = = == = == = = == = == == =
    دستورات شرطي در اسمبلي:
    دستورات شرطي در اسمبلي با نگاه كردن به فلاگها ، پرش مي كنند. و دستور غير شرطي هم كه همان jmp بود.
    مثلا دو عدد را تفريق مي كنيم و بعد jz مي نويسيم كه به فلاگ صفر نگاه مي كند . فلاگ صفر وقتي مثلا دو عدد مساوي از هم كم شوند ، صفر مي شود ، و پرش مي كند. اين طوري مي تواينم كارهاي محاسباتي را در اسمبلي انجام دهيم. پس براي ((انجام محاسبات درست)) ، دانستن نحوه تغيير فلاگها ضروري است. من نمي خواهم وارد اين گونه محاسبات در اسمبلي بشويم و قسمتهاي محاسبات رياضي را در سي انجام مي دهيم كه راحت است و كارهاي خاصي را در اسمبلي تست و احيانا استفاده مي كنيم.
    من بيشتر به خاطر جنبه آموزشي اين تاپيك را باز كرده ام و فكر نمي كنم سي به اسمبلي محتاج باشد. كه بعد بحث هايي معلوم ميشه كه اين حرف من تا چند درصد درسته. البته اين حرف را با اين محاسبه مي گويم كه با همان استيل سي رجيسترها را تغيير دهيم و از وقفه ها استفاده كنيم. منظورم از نياز نداشتن سي به اسمبلي ، به استيل(سبك كدنويسي) اسمبلي است نه كلا به اسمبلي.
    .
     
  18. balabala

    balabala کاربر قدیمی پرشین تولز

    تاریخ عضویت:
    ‏22 می 2005
    نوشته ها:
    7,350
    تشکر شده:
    1,321
    محل سکونت:
    یه خورده اونورتر
    اقا تشکر،
    تاپیکهاتون واقعا پرمحتواست. :)
     
  19. saalek

    saalek مدیر بازنشسته

    تاریخ عضویت:
    ‏24 می 2005
    نوشته ها:
    654
    تشکر شده:
    53
    محل سکونت:
    در پاي كوهپايه ها
    آقا افتخار داديد. شرمنده هم كرديد عزيز. قربان شما.
    .
     
  20. saalek

    saalek مدیر بازنشسته

    تاریخ عضویت:
    ‏24 می 2005
    نوشته ها:
    654
    تشکر شده:
    53
    محل سکونت:
    در پاي كوهپايه ها
    از استادم مطلبي را نقل مي كنم.
    .
    نام تاپيك:آموزش assembly
    آدرس:
    http://www.myimei.com/forum/showthread.php?tid=112&pid=4658#pid4658
    =============


    ثبات ها:
    ثبات ها حافظه های 8 ، 16 ، 32 و 64 بيتی در داخل پردازنده هستند.
    که سرعت بسار بالايي دارند و می توان از آنها در ساختارهايی که به شمارنده و محاسبات زيادی نياز دارند استفاده کرد
    در اين بخش با ثباتهای 16 بيتی کار داريم.

    ثباتهای عمومی:
    AX-BX-CX-DX
    که هر کدام از دو قسمت 8 بيتی کم ارزش ، و پر ارزش تشکيل شده اند.
    مثلاً AL بخش کم ارزش و AH بخش پر ارزش AX می باشد
    و برای DX نيز DL, DH
    خانه ها را از 0 شماره گذاری می کنيم (بنابر اين هر ثبات 16 بيتی از 0 تا 15 شماره گذاری ميشه
    از ثبات CX در شمارنده ها استفاده ميشه ( متغيير کنترلی اتمام حلقه ها هستند)
    نتيجه ضرب و تقسيم اعداد بزرگ در DX قرار می گيره.
    به ثبات BX هم ثبات پايه می گن وبه عنوان انديس ازش استفاده می کنن.
    ثبات AX هم در محاسبات زياد مورد استفاده قرار می گيره.

    ثباتهای سگمنت:
    ES-SS-DS-CS
    در اين ثباتها آدرس شروع سگمنت هايی که قبلاً گفتم ذخيره می شه.

    براي سگمنت پشته SS يا satack segment .
    براي سگمنت داده DS يا data segment .
    براي سگمت كد CS يا code segment .
    براي سگمنت اضافي ES يا extra segment .

    ثباتهای انديس:
    BP-SP-SI-DI

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

    ثبات BP=base pointer شامل افست(سالك : افست ، آدرس دوم است. در بالا همان كوچه فرض كرديم و آدرس اول را خيابان فرض كرديم.)

    ثبات SP=stack pointer حاوي آفست بالاي پشته است. بالاي پشته يعني آخرين ورودي.
    پشته در زبان اسمبلي از بالا بسته است و رو به پايين پر ميشه. (از نظر آدرس)

    ثبات SI=source index براي عمليات بر روي رشته ها است و آدرس رشته منبع را نگهداري مي كند.

    ثبات DI=destination index براي عمليات بر روي رشته ها است و آدرس رشته مقصد را نگهداري مي كند.

    ==== == ==== == == ==

    ثباتهای وضعيت و کنترلی:
    IP-FLAG

    ثبات IP آفست دستور بعدی اجرايی برنامه را دارد

    ثبات فلگها
    CF-PF-AF-ZF-SF-TF-IF-DF-OF
    يک ثبات 16 بيتی که 9 بيت آن به طور مشخص نامگذاری شده و وضعيت پردازنده رو نشون ميده.


    [​IMG]

    بيت (c (carry برای نشان دادن وجود رقم نقلی در محاسبات که آنرا CF می نامند
    بيت (D (direction به معنی جهت می باشد و جهت پردازش روی رشته ها رو مشخص میکنه (از چپ به راست یا برعکس) و با DF نشون داده میشه
    بيت (P (parity) اگر صفر باشه نشاندهنده اينه که تعداد شيفت ها زوج بوده وگرنه فرد بوده و با PF نشون داده ميشه
    بيت (A (auxiliary carry رقم نقلی کمکی هست و در محاسبات 8 بيتی اگه رقم نقلی استفاده ميشه و با AF نمايش داده ميشه
    بيت (Z(zero اگر در نتيجه محاسبات صفر ايجاد بشه اين بيت برابر يک ميشه و با ZF نشون داده ميشه
    بيت (S (sign اگر نتيجه محاسبات منفی باشه اين بيت 1 ميشه و با SF نشون داده ميشه
    بيت (T(trap اگر اين بيت برابر يک باشه اجرای برنامه به صورت دستور به دستور در مياد و با TF نشون داده ميشه
    بيت (I(interrupt اگر اين بيت 1 باشه سيستم به وقفه ها پاسخ ميده وگرنه اونارو ناديده می گيره که با IF نشون داده ميشه
    بيت (O(overflow اگه در محاسبات بيت پر ارزش سرريز بشه بيت O صفر ميشه , fh OF نشون داده ميشه.
    .
     
  21. saalek

    saalek مدیر بازنشسته

    تاریخ عضویت:
    ‏24 می 2005
    نوشته ها:
    654
    تشکر شده:
    53
    محل سکونت:
    در پاي كوهپايه ها
    يك سئوال:
    من كمي با توربوديباگر كار كرده ام.
    فكر كنم كارش اينه كه برنامه ما را به كد اسمبلي تبديل مي كنه. مي خواهم باز نصبش كنم و كمي كار كنم.
    حالا سئوالم اينه كه ايا ميشه هر چند دستور اسمبلي را يك دستور سي دانست و به تدريج با ماهر شدن با ديدن سورس اسمبلي ، فهميد سورس سي چي بوده. البته اين يك كار ايده آل است. براي انجام نمي گم. چون مي دانم دوستان خيلي اطلاعات در اين مورد دارند مي خواهم استفاده كنم.
    .
     
  22. saeedsmk

    saeedsmk مدیر بازنشسته

    تاریخ عضویت:
    ‏6 سپتامبر 2003
    نوشته ها:
    1,519
    تشکر شده:
    4
    سلام سالك جان
    در كل بايد بگم به اين راحتي نيست
    ميشه براي چندين دستور توي سي توي اسمبلي با تجربه اين كاري رو شما گفتيد انجام داد. ولي چون ما 4 تا ثابت همكاره و 4 تا ثابت معين داريم و بي نهايت سل حافظه ( درسته تعدادشون محدود ه اما چون باز هم اين تعداد زياد است باز ميشه با تقريب گفت بي نهايت ) ميشه يك كد رو به چندين حالت نوشت و در بعضي از مواقع فهميدن كد ها خط به خط سخته چه برسه به كلي نگريستن . فكر كنم با مثال دقيق تر بشه صحبت كرد
    شما تابعي داريد با دو تا ورودي و يك خروجي توي سي مينويسيد
    a=funcx(c,d) //call function
    اما با اسمبلي بصورت ساده ميشه (1)
    کد:
    mov ax,d
    push ax
    mov ax,c
    push ax
    mov ax, offset funcx
    call ax
    ( بجاي ax ميتونيد از bxو ... هم استفاده كرد)
    کد:
    push d
    push c 
    call word ptr xxx
    كه xxx ادرس تابع ما درونش هست
    و ....
    كه همانطور كه ميبيني خيلي راحت نيست و در بعضي از موارد شما c و d كه حاصل توابع ديگري هستند ( funcc و funcd ) رو به تابع ميفرستيد همين دستور توي سي ميشه
    کد:
    d=funcd();
    c=funcc();
    a=func(c,d) // callfuction
    كه توي اسمبلي ميشه
    کد:
    call call word ptr ddd
    push ax
    call call word ptr ccc
    push ax
    call word ptr xxx
    كه ddd ادرس تايع funcd و cccادرس تايع funccو xxx ادرس تايع funcx را در خود دارد كه ميبيني با حالت 1 خيلي متفاوت شده ولي دستورات سي اون تقريبا شبيه هم هستند ( اينجا هم شبيه اند اما توي يه برنامه چندين خطي و يا هزارن خطي كاري كه شما گفتيد خيلي راحت انجام پذير نيست )

    اميدوارم كمك كنه