اطلاعیه

Collapse
No announcement yet.

یک کامپایلر برای همه میکروها ، همه زبانها برای یک کامپایلر

Collapse
X
 
  • فیلتر
  • زمان
  • Show
Clear All
new posts

    #31
    پاسخ : یک کامپایلر برای همه میکروها ، همه زبانها برای یک کامپایلر

    نظر شما رو به این نکات جلب میکنم: (البته اینا یک سری نکته هستن. هنوز جمع بندی شون نکردم)

    1- قبلا میخواستم بخاطر توسعه و نگهداری پروژه های مبتنی بر میکرو کنترلر از یک زبان مشترک استفاده کنم. مثل #C اما بعلت اینکه ساختار پایه زمان اجرای این زبان بشدت پیچیده بود منصرف شدم. روی جاوا سوئیچ کردم. اما جاوا هم با اینکه کد کوچکی تولید میکرد که به نسبه میشد روی میکرو های متفاوت اجراش کرد ولی در نهایت خلاصه سازی به این مشکل بر خوردم که خیلی جاها نمیشه بین یک عدد و رفرنس حافظه تفاوتی قائل شد. و این به نحوه تولید کد جاوا برمیگرده. یعنی باید یک آنالایزر برای کد تولید شده توسط کامپایلر نوشت که خودش منجر به تولید یک سری کد های بالاسری میشد. و البته نیاز های من رو هم رفع نمیکرد. ولی من حیث المجموع کد جاوا از همه بهتره.

    2- در دات نت یک مدل برای کد اسمبلی پیش بینی شده که بهش قدرت میده وگرنه این دو خیلی فرقی با هم ندارن. مثلا فرض کنین در هر لحظه عددی که در eval stack نگهداری میشه در جاوا مشخصه چون دستورات تفکیک شده ای داره ولی در دات نت به نوع کلاس مورد بخث بستگی داره. ضمنا یک مورد مهمی که هست من میخواستم روی یک میکرو با حداقل میزان امکانات هم کد هام براحتی اجرا بشه و این طلب میکرد که کد نهایی بشدت اپتیمم بشه. یعنی نمیشه انتظار داشت برای همه میکرو ها یک کد مشخص تولید بشه. یک مورد این رو میشه در دستور add دید. این دستور کاری نداره که شما دارین 2 عدد 8 بیتی رو جمع میکنین یا 64 بیتی.

    3- یکی از قدرت های این تیپ زبونها استفاده از حافظه مجازی به همراه memory compaction هست. بدون این ها و ایضا مواردی مثل exception handling که یک ساختار high level هست پیاده سازی این طرح هیچ gain خاصی نداره.

    4- توصیه میکنم معماری لایه ای رو استفاده کنین و بخش GUI رو از بخش عملیاتی جدا کنین.

    5- راجع به سخت افزار حجم فلش و رم و ایپرام و mmc و غیره خیلی ممکنه در کد تولید شده نقش پیدا کنه. الان پروژه ای که من دارم باید با 64 کیلو بایت رم کل کارهام راه بندازم و این برای پروژه من یعنی مصیبت. در یک ساختار 8 بیتی؛ یک متغیر محلی از نوع بایت فقط یک بایت جا میبره ولی در یک ساختار 32 بیتی چهار بایت چون قراره اینها در پشته نگهداری بشن. تازه مواردی مثل alignment هم باعث دردسره. مثلا در ARM اگر یک int32 رو در آدرسی که قابل تقسیم به 4 نیست قرار بدین برنامه تون اجرا نمیشه. ولی در یک 80486 یا AVR اینطوری نیست.

    6- یک مورد مهم در این سری طرح ها کم کردن مشکلات برنامه نویس و افزایش ضریب امنیت کد تولید شده هست. اگر قرار باشه که هزار اما و اگر جلوی پای برنامه نویس بذاریم که چه کاریه. سورس ها ی کتابخانه ها رو به زبان اصلی مثلا C نگهداری میکنیم و میذتریم خود کامپایلر در لحظه نهایی تولید کد زحمتش رو بکشه.

    7- اشکالات زمان اجرا بسیار مهم هستن. و باید بشه با حداقل سرباری مشکل رو کشف و حل کرد. اینجا یک میکرویی داریم که ممکنه یک 8051 باشه با فرکانس 12 مگا هرتز (سرعت حدود یک MIPS یا کمتر) ضمنا یک سیستم Embedded داریم آماده میکنیم. ممکنه در یک لحظه بحرانی فرصتی برای کشف و مدیریت خطا هم وجود نداشته باشه. چون بحث میکرو ثانیه ها وسط هست. معمولا هم اپراتوری وجود نداره که بخواد پیغام خطا رو مشاهده کنه.

    بگذریم... دیگه دارم جوش میارم.

    دیدگاه


      #32
      پاسخ : یک کامپایلر برای همه میکروها ، همه زبا

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

      یک راه حل دیگه که به ذهنم می رسه اینکه که از رجیسترهای خاصی از میکرو برای انتقال داده استفاده کنیم (یعنی نه از پشته) که پوش و پاپ کردن رو می تونه حذف کنه (البته برای 1 یا 2 تا از داده ها نمی توان از آن استفاده کرد) و همچنین نوع داده نیز همراه با داده ذخیره شود
      فعلا چیز جدید به ذهنم نمی رسه
      راستی یک چیز دیگر ، سرعت عمل شما در پاسخ دادن واقعا قابل ستایشه ( من که کف کردم :applause
      هیچ کس و هیچ چیز نمی تونه مانع رسیدنت به آرزوهات بشه مگر خودت

      دیدگاه


        #33
        پاسخ : یک کامپایلر برای همه میکروها ، همه زبانها برای یک کامپایلر

        تابخانه ها به زبان MML ذخیره شوند بهتر است
        دقیقا. و این به نوعی استقلال ایجاد میکنه بین سورس کامپایل شده و کد نهایی.

        نکته دیگه اینکه خود .net هنگامی که می خواهدیک لحقه ایجاد کند از یک متغیر محلی استفاده می کند و نه از پشته
        عجب! اونوقت ممکنه بفرمایید که متغیر محلی رو کجا نگه میداره؟ لابد روی پشت بوم!! :mrgreen:
        تازه تظرتون راجع به این حلقه چیه؟


        for( ; test_my_condition() ; )
        {
        .....
        }


        اینجا که اصلا متغیر محلی نداریم. ثانیا نظرتون راجع به عباراتی که کد پیچیده ای دارن چیه؟ اگر نتونه همه اطلاعات رو داخل رجیستر ها نگهداری کنه باید بگه "ببخشید کد شما رو نمیتونم پردازش کنم"؟ یه کمی جوک نیست؟!!

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

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

        همچنین نوع داده نیز همراه با داده ذخیره شود
        میگین "ذخیره" ممکنه بگین کجا؟ منظورتون که احتمالا رجیستر ها نیست. از پشته هم که نمیخوایین استفاده کنین. پس لابد heap؟

        من که کف کردم
        بازم جای شکرش باقیه. من که از روز اول توی کف بزرگ شدم!
        :mrgreen:

        دیدگاه


          #34
          پاسخ : یک کامپایلر برای همه میکروها ، همه زبانها برای یک کامپایلر

          نوشته اصلی توسط رضا آقازاده
          عجب! اونوقت ممکنه بفرمایید که متغیر محلی رو کجا نگه میداره؟ لابد روی پشت بوم!! :mrgreen:
          جناب آقا زاده ....
          وقتی شما متغیر محلی تعریف می کنید با توجه به نوع داده و تابعی که در آن هستید توسط پوینتری موقعیت آن را تا انتهای تابع مشخص کنید ، همان گونه که بقیه کامپایلرها (میکرو) متغییر محلی را تعریف می کنند برای مثال در میکرو AVR از رجیستر R28:R29 که همان Y می باشد برای اینکار در نظر گرفته اند. با اینکه متغیر محلی و پشته هر دو از RAM استفاده می کنند ولی هرکدام فضاهای جدا گانه ای از RAM را اشغال می کنند و روش دسترسی به هر کدام نیز متفاوت است.
          تازه تظرتون راجع به این حلقه چیه؟


          for( ; test_my_condition() ; )
          {
          .....
          }


          اینجا که اصلا متغیر محلی نداریم.
          در مورد مثالی که زده اید ، خروجی Visual Studio به صورت زیر است
          کد:
          [left]
          .method private hidebysig instance void Form1_Load(object sender,
                                    class [mscorlib]System.EventArgs e) cil managed
          {
           // Code size    16 (0x10)
           .maxstack 1
           .locals init ([0] bool CS$4$0000)
           IL_0000: nop
           IL_0001: br.s    IL_0005
           IL_0003: nop
           IL_0004: nop
           IL_0005: ldarg.0
           IL_0006: call    instance bool WindowsFormsApplication1.Form1::test_my_condition()
           IL_000b: stloc.0
           IL_000c: ldloc.0
           IL_000d: brtrue.s  IL_0003
           IL_000f: ret
          } // end of method Form1::Form1_Load
          [/left]
          همانطور که می بینید خروجی تابع که یک Bool است درون یک متغییر محلی صفر ریخته می شود (stloc.0) و در خط بعدی از همان متغیر محلی لود می کند و سپس پرش می کند ، همانطور که می بینید قبل از پرش به جز یک داده که توسط خود دستور پرش مورد استفاده قرار می گیرد ، پس از پرش هیچ گونه داده ای درون پشته قرار ندارد
          ثانیا نظرتون راجع به عباراتی که کد پیچیده ای دارن چیه؟ اگر نتونه همه اطلاعات رو داخل رجیستر ها نگهداری کنه باید بگه "ببخشید کد شما رو نمیتونم پردازش کنم"؟ یه کمی جوک نیست؟!!
          عبارات پیچیده در بخش سطح بالا قرار دارند که اصلا در حالتی که من دارم بررسی می کنم قرار ندارند و آنها مربوط به چگونگی تبدیل زبان سطح بالا به زبان میانی می شوند ، در ضمن در مورد کم آورده رجیسترها، هیچ وقت اتفاق نمی افتد، چون رجیسترهایی که در بازه های زمانی مورد استفاده قرار نمی گیرند و برنامه نیاز به رجیستر داشته باشد به پشته منتقل می شوند و زمانی که نیاز باشد دوباره از پشته فراخوانی می شوند یا از یک رجیسترها با هم جابجا می شوند (با توجه به نیازی که لازم به هر رجیستر هست) البته همه اینکار ها زمان تبدیل زبان MML به زبان میکرو انجام می شود و به فرض اینکه شما گفتید رخ دهد همان زمان کامپایلر کردن برنامه به کاربر اطلاع می دهد نه زمان اجرا
          برای چند تا؟ یکی دو تا 10 تا؟ چند تا؟ همیشه میشه کد پیچیده تری تولید کرد که حافظه های بیشتری بخواد. و اصلا چرا دات نت میزان پشته مصرفی براش مهمه؟ (داخل اطلاعات یک متد حجم پشته مصرفی قید میشه).
          برای یک یا دو تا ، اگر بیشتر شد از پشته استفاده می کند .
          البته همه کامپایلرها تعداد بایتهایی که آن متد مصرف می کنه برایشان مهم است و نه فقط .net حتی این کامپایلرهای میکرو
          میگین "ذخیره" ممکنه بگین کجا؟ منظورتون که احتمالا رجیستر ها نیست. از پشته هم که نمیخوایین استفاده کنین. پس لابد heap؟
          میشود از یک رجیستر برای نگه داری نوع داده قبل از پرش استفاده کرد

          اما در مورد
          هیچ کس و هیچ چیز نمی تونه مانع رسیدنت به آرزوهات بشه مگر خودت

          دیدگاه


            #35
            پاسخ : یک کامپایلر برای همه میکروها ، همه زبانها برای یک کامپایلر

            برای مثال در میکرو AVR از رجیستر R28:R29 که همان Y می باشد برای اینکار در نظر گرفته اند
            شرمنده. این رو شما میگین. چون مشخصا دارین از کد تولیدی توسط کد ویژن استفاده میکنین. یک سری به کامپایلر هایی مثل WinAVR هم بزنین. چون به شخصه اعتقاد دارم چیزی مثل کدویژن اصولا یک کامپایلر خوب محسوب نمیشه.

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

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

            call some_proc
            tst r24
            brne L1


            میبینین که از پشته برای نگهداری مقدار برگشتی تابع هم استفاده نکردم. حتی از متغیر محلی هم استفاده نشده.

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

            راستی چیزی راجع به پروسسورهای اسپارک هم شنیدین؟ 32 رجیستر دارن در 256 window ی متفاوت. که عملا بار کلی سیستم رو خیلی پایین میاره. ولی بازهم شرایط فرقی نمیکنه چون اینا شرایط خاص هستن و برنامه شما باید در شرایط عام کار کنه. و کامپایلر های مربوطه فرض رو بر این قرار نمیدن که "فقط رجیستر" و یا "بدون پوش و پاپ".

            در باره مثالی هم که گفتین؛ این یک کد واسطه هست و در کد نهایی در همون دات نت لوکیشن صفر استفاده نمیشه (البته برای نگهداری مقدار برگشتی و جز در موارد خاص). کد نهایی هم روی رجیستر eax و یا جفت edx:eax کار میکنه.

            به فرض اینکه شما گفتید رخ دهد همان زمان کامپایلر کردن برنامه به کاربر اطلاع می دهد نه زمان اجرا
            اصلا چرا باید اطلاع بده؟ این ضعف نیست؟

            تعداد بایتهایی که آن متد مصرف می کنه برایشان مهم است
            اتفاقا تعداد "بایتها" مهم نیست. تعداد المانهای پشته مهمه. حالا این المانها در AVR هشت بیتی یک بایت هست و در ARM سی و دو بیتی یک 32 بیت.

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

            دیدگاه


              #36
              پاسخ : یک کامپایلر برای همه میکروها ، همه زبا

              شرمنده. این رو شما میگین. چون مشخصا دارین از کد تولیدی توسط کد ویژن استفاده میکنین. یک سری به کامپایلر هایی مثل WinAVR هم بزنین. چون به شخصه اعتقاد دارم چیزی مثل کدویژن اصولا یک کامپایلر خوب محسوب نمیشه.
              شرمنده. ولی تا اونجایی که من می دونم از این روش برای تعریف متغیر در winavr و FastAvr نیز استفاده می شود، اگر به حرفم اطمینان ندارید لطفا خروجی برنامه winavr (فایل Hex ) را با یک نرم افزار diassembly باز کنید و چگونگی پیاده سازی متغییر های محلی آن رو ببینید.

              مسئله دیگه اینه که ساختار حافظه تون رو چجوری تعریف میکنین. کسی که میاد از رجیستر Y برای نگهداری مقادیر استفاده میکنه استک نرم افزاری و سخت افزاری رو جدا کرده.
              با استفاده از این روش می توان به راحتی متغییر محلی تعریف کرد و همچنین از قابلیت توابع تو در تو نیز استفاده کرد ، حتی از این روش برای تخصیص داده برای کلاسهایی که در حین برنامه نمونه سازی می شوند استفاده کرد ، در حالی که با پشته نمی شود (چون آدرس مشخصی ندارد)
              دقت کنید منظورم آدرس مشخص + آدرس متغییر می باشد که توسط پوینتر می توان به آن دسترسی داشت.
              در ضمن اگر جدا نباشد زمانی که از پوش استفاده می کنید آدرس دهی متغییرتون می ریزه تو هم .
              تمام میکرو ها اینطوری نیستن. چون اون باگ طراحی AVR در اونها وجود نداره. که مجبور بشن دو تا پشته داشته باشن.
              کامپایلر های مربوطه فرض رو بر این قرار نمیدن که "فقط رجیستر" و یا "بدون پوش و پاپ".
              بله قرار نیست تمام میکرو ها اینطور کار کنند ، الان من دارم قسمت MMLtoAVR رو طراحی می کنم ، پس باید با توجه به توانایی های میکرو AVR تبدیل را انجام دهم تا کمترین و سریعترین خروجی را از کامپایلر بگیرم

              ضمنا شما مخیرید هر جور که میخوایین کد تولید کنین. اما من یکی این کد رو بیشتر میپسندم:
              کدی که قرار دادم رو من تولید نکردم بلکه خروجی خود Visual Studio بود

              در باره مثالی هم که گفتین؛ این یک کد واسطه هست و در کد نهایی در همون دات نت لوکیشن صفر استفاده نمیشه (البته برای نگهداری مقدار برگشتی و جز در موارد خاص). کد نهایی هم روی رجیستر eax و یا جفت edx:eax کار میکنه.
              این کد مربوط به مثال خودتون بود که از اتفاق مقدار بازگشتی هم داشت ، و در مورد کد نهایی که گفتید (eax) متوجه نشدم چی گفتید

              اصلا چرا باید اطلاع بده؟ این ضعف نیست؟
              بعضی وقتها کاربر بیشتر از ظرفیت (ram) از یک میکرو انتظار دارد که دیگه کاریش نمی شه کرد ، هرچند قبول دارم که هر چه کاربر از اتفاقی که قرار است پشت صحنه بیفتد بی خبرتر باشد بهتر است.
              اصلا چه احتیاجی به نگهداری "نوع" هست؟
              "نوع" جزء اطلاعات زمان کامپایل هست و نه اجرا.
              وقتی که نمی دانیم چه نوع داده ای درون پشته (یا رجیسترها) قرار گرفته ، پس نمی توانیم تعداد دستوراتی که قرار است پاپ کنیم را حدس بزنیم و ممکن است داده های مربوط به داده دیگری به همراه داده مورد نظر از پشته میکرو پاپ کنیم (مثلا داده integer دو بایتی پوش کرده ایم ، اما الان یک دستوری که نیاز به داده intیک بایتی هست داده را از پشته پاپ کنه، حالا باید از کجا بفهمیم که بایت دیگر رو باید دور بیندازیم؟؟)


              در آخر فکر می کنم بهتر انرژیمان را روی مواردی که واقعا اهمیت دارند بگذاریم
              هیچ کس و هیچ چیز نمی تونه مانع رسیدنت به آرزوهات بشه مگر خودت

              دیدگاه


                #37
                پاسخ : یک کامپایلر برای همه میکروها ، همه زبانها برای یک کامپایلر

                شرمنده. ولی تا اونجایی که من می دونم از این روش برای تعریف متغیر در winavr و FastAvr نیز استفاده می شود، اگر به حرفم اطمینان ندارید لطفا خروجی برنامه winavr (فایل Hex ) را با یک نرم افزار diassembly باز کنید و چگونگی پیاده سازی متغییر های محلی آن رو ببینید.
                باز هم من شرمنده!!
                احتیاجی به دیس اسمبلی فایل هگز نیست. هم کد ویژن و هم winavr فایل listing تولید میکنن. توی اون سورس تولید شده رو میتونین ببینین. ضمنا winavr از رجیستر Y فقط موقعی استفاده میکنه که جا برای نگهداری در رجیستر ها کم بیاد و بخواد آدرس frame رو نگهداری کنه. یک چیزی مثل BP در 8086 به بالا.

                نمونه از winavr»:

                SIGNAL(__vector_Relocated_UART_RECV)
                {
                d8: 1f 92 push r1
                da: 0f 92 push r0
                dc: 0f b6 in r0, 0x3f ; 63
                de: 0f 92 push r0
                e0: 11 24 eor r1, r1
                e2: 2f 93 push r18
                e4: 8f 93 push r24
                e6: 9f 93 push r25
                e8: af 93 push r26
                ea: bf 93 push r27
                ec: ef 93 push r30
                ee: ff 93 push r31
                uint8_t c;
                uint8_t st;
                serial_t *p;

                از ابتدای یک روال وقفه. و نمونه ای دیگر:

                static uint8_t begin_reply(serial_t *p, uint8_t size)
                {
                336: cf 93 push r28
                338: df 93 push r29
                33a: dc 01 movw r26, r24
                if( system_id == 0xFF )
                33c: 80 91 47 02 lds r24, 0x0247
                340: 8f 3f cpi r24, 0xFF ; 255
                342: 09 f4 brne .+2 ; 0x346 <begin_reply+0x10>
                344: 54 c0 rjmp .+168 ; 0x3ee <begin_reply+0xb8>
                return 0;


                که رجیستر Y فقط برای نگهداری مقادیر واسطه یک اشاره گر به یک ساختار داده ای استفاده شده.

                ضمنا ساختار فریم بندی پشته رو در 8086 نگاه کنین متوجه منظورم میشین.

                در ضمن اگر جدا نباشد زمانی که از پوش استفاده می کنید آدرس دهی متغییرتون می ریزه تو هم .
                باز هم شرمنده. معمولا عملیات پوش و پاپ در میانه راه evaluate کردن یک عبارت استفاده میشه و حتی در این حالت هم حتی اگر دسترسی به متغیر های محلی صورت بگیره از طریق رجیستر ها مشکل حل میشه. ولی استثنائا اگر متغیر ها در پشته تخصیص داده بشن باید یک چیزی به عنوان frame pointer وجود داشته باشه که winavr فقط در این حالت از Y استفاده میکنه.

                الان من دارم قسمت MMLtoAVR رو طراحی می کنم ، پس باید با توجه به توانایی های میکرو AVR تبدیل را انجام دهم تا کمترین و سریعترین خروجی را از کامپایلر بگیرم
                دمتون گرم. اما میدونین که بعضی وقتها پیش میاد که شما نباید سریعترین رو استفاده کنین. و یا حتی کمترین. مثلا این کد:

                for(i=0; i<100; i++)
                a = b;


                در بعضی مواقع به این کد اپتیمم میشه:

                i=100;
                a=b;


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

                کدی که قرار دادم رو من تولید نکردم بلکه خروجی خود Visual Studio بود
                منظورم این نبود که شما کامپایل کردید. سورسی که شما نوشتین نتیجه اش این بود.

                دیدگاه


                  #38
                  پاسخ : یک کامپایلر برای همه میکروها ، همه زبانها برای یک کامپایلر

                  این کد مربوط به مثال خودتون بود که از اتفاق مقدار بازگشتی هم داشت ، و در مورد کد نهایی که گفتید (eax) متوجه نشدم چی گفتید
                  eax رجیستر آکومولاتور ساختار 386 با بالا هست. و این کد:

                  stloc.0
                  ldloc.0
                  brtrue.s IL_0003


                  توسط JIT بهینه میشه به این کد:

                  and eax,eax
                  jnz L_0003


                  که میبینین اصلا به پشته کاری نداره.

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

                  وقتی که نمی دانیم چه نوع داده ای درون پشته (یا رجیسترها) قرار گرفته ، پس نمی توانیم تعداد دستوراتی که قرار است پاپ کنیم را حدس بزنیم و ممکن است داده های مربوط به داده دیگری به همراه داده مورد نظر از پشته میکرو پاپ کنیم (مثلا داده integer دو بایتی پوش کرده ایم ، اما الان یک دستوری که نیاز به داده intیک بایتی هست داده را از پشته پاپ کنه، حالا باید از کجا بفهمیم که بایت دیگر رو باید دور بیندازیم؟؟)
                  چون فرض رو بر این گذاشتین که نمیدونین ساختار برنامه تون چطوریه. در صورتیکه اگر کامپایلر یا برنامه تولید کدتون همین رو هم نفهمه که فاتحه اش خونده س. شما در یک جا یک عدد 32 بیتی رو پوش میکنین و بعد نمیدونین که اگر خواستین 8 بیتی پاپ کنین چند بایت باقی میمونه؟ اینجا هم توصیه میکنم برای راحتی کارتون یک سری به کد های تولیدی کامپایلر جاوا بزنین. و یک کمی راجع به نوشتن یک VM برای اون وقت بذارین. خیلی دیدتون رو باز میکنه. چون کد های جاوا نسبت به کد های دات نت خیلی قابل فهم تره. ضمنا یک نسخه کوچیک برای کار با جاوا روی میکرو هست بنام nanovm ولی پیاده سازی بسیار بدی داره. بخونینش و سعی کنین پیدا کنین چرا میگم "پیاده سازی بد". من عین همین کد رو با نصف حجمش (تقریبا) نوشتم. یک فرض بسیار مهم رو این VM لحاظ نمیکنه که منجر به نوشتن کلی کد مزخرف میشه.

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

                  دیدگاه


                    #39
                    پاسخ : یک کامپایلر برای همه میکروها ، همه زبا

                    احتیاجی به دیس اسمبلی فایل هگز نیست. هم کد ویژن و هم winavr فایل listing تولید میکنن. توی اون سورس تولید شده رو میتونین ببینین. ضمنا winavr از رجیستر Y فقط موقعی استفاده میکنه که جا برای نگهداری در رجیستر ها کم بیاد و بخواد آدرس frame رو نگهداری کنه.
                    مرسی که گفتید من درباره فایلهای list نمی دانستم.
                    من از AVRStudio 5 برای کامپیایل کردن استفاده کردم و فایل list من این شد

                    AVRGCC5.elf: file format elf32-avr

                    Sections:
                    Idx Name Size VMA LMA File off Algn
                    0 .text 000000ac 00000000 00000000 00000054 2**1
                    CONTENTS, ALLOC, LOAD, READONLY, CODE
                    1 .stab 000006cc 00000000 00000000 00000100 2**2
                    CONTENTS, READONLY, DEBUGGING
                    2 .stabstr 00000085 00000000 00000000 000007cc 2**0
                    CONTENTS, READONLY, DEBUGGING
                    3 .debug_aranges 00000020 00000000 00000000 00000851 2**0
                    CONTENTS, READONLY, DEBUGGING
                    4 .debug_pubnames 00000026 00000000 00000000 00000871 2**0
                    CONTENTS, READONLY, DEBUGGING
                    5 .debug_info 000000e4 00000000 00000000 00000897 2**0
                    CONTENTS, READONLY, DEBUGGING
                    6 .debug_abbrev 0000008c 00000000 00000000 0000097b 2**0
                    CONTENTS, READONLY, DEBUGGING
                    7 .debug_line 00000101 00000000 00000000 00000a07 2**0
                    CONTENTS, READONLY, DEBUGGING
                    8 .debug_frame 00000030 00000000 00000000 00000b08 2**2
                    CONTENTS, READONLY, DEBUGGING
                    9 .debug_str 000000b6 00000000 00000000 00000b38 2**0
                    CONTENTS, READONLY, DEBUGGING
                    10 .debug_pubtypes 0000001e 00000000 00000000 00000bee 2**0
                    CONTENTS, READONLY, DEBUGGING

                    Disassembly of section .text:

                    00000000 <__vectors>:
                    0: 0c 94 2a 00 jmp 0x54 ; 0x54 <__ctors_end>
                    4: 0c 94 34 00 jmp 0x68 ; 0x68 <__bad_interrupt>
                    8: 0c 94 34 00 jmp 0x68 ; 0x68 <__bad_interrupt>
                    c: 0c 94 34 00 jmp 0x68 ; 0x68 <__bad_interrupt>
                    10: 0c 94 34 00 jmp 0x68 ; 0x68 <__bad_interrupt>
                    14: 0c 94 34 00 jmp 0x68 ; 0x68 <__bad_interrupt>
                    18: 0c 94 34 00 jmp 0x68 ; 0x68 <__bad_interrupt>
                    1c: 0c 94 34 00 jmp 0x68 ; 0x68 <__bad_interrupt>
                    20: 0c 94 34 00 jmp 0x68 ; 0x68 <__bad_interrupt>
                    24: 0c 94 34 00 jmp 0x68 ; 0x68 <__bad_interrupt>
                    28: 0c 94 34 00 jmp 0x68 ; 0x68 <__bad_interrupt>
                    2c: 0c 94 34 00 jmp 0x68 ; 0x68 <__bad_interrupt>
                    30: 0c 94 34 00 jmp 0x68 ; 0x68 <__bad_interrupt>
                    34: 0c 94 34 00 jmp 0x68 ; 0x68 <__bad_interrupt>
                    38: 0c 94 34 00 jmp 0x68 ; 0x68 <__bad_interrupt>
                    3c: 0c 94 34 00 jmp 0x68 ; 0x68 <__bad_interrupt>
                    40: 0c 94 34 00 jmp 0x68 ; 0x68 <__bad_interrupt>
                    44: 0c 94 34 00 jmp 0x68 ; 0x68 <__bad_interrupt>
                    48: 0c 94 34 00 jmp 0x68 ; 0x68 <__bad_interrupt>
                    4c: 0c 94 34 00 jmp 0x68 ; 0x68 <__bad_interrupt>
                    50: 0c 94 34 00 jmp 0x68 ; 0x68 <__bad_interrupt>

                    00000054 <__ctors_end>:
                    54: 11 24 eor r1, r1
                    56: 1f be out 0x3f, r1 ; 63
                    58: cf e5 ldi r28, 0x5F ; 95
                    5a: d8 e0 ldi r29, 0x08 ; 8
                    5c: de bf out 0x3e, r29 ; 62
                    5e: cd bf out 0x3d, r28 ; 61
                    60: 0e 94 36 00 call 0x6c ; 0x6c <main>
                    64: 0c 94 54 00 jmp 0xa8 ; 0xa8 <_exit>

                    00000068 <__bad_interrupt>:
                    68: 0c 94 00 00 jmp 0 ; 0x0 <__vectors>

                    0000006c <main>:
                    */

                    #include <avr/io.h>

                    int main(void)
                    {
                    6c: df 93 push r29
                    6e: cf 93 push r28
                    70: 0f 92 push r0
                    72: cd b7 in r28, 0x3d ; 61
                    74: de b7 in r29, 0x3e ; 62
                    while(1)
                    {
                    char g;
                    g=g+1;
                    76: 89 81 ldd r24, Y+1 ; 0x01
                    78: 8f 5f subi r24, 0xFF ; 255
                    7a: 89 83 std Y+1, r24 ; 0x01
                    g=54;
                    7c: 86 e3 ldi r24, 0x36 ; 54
                    7e: 89 83 std Y+1, r24 ; 0x01
                    //TODO:: Please write your application code
                    }
                    80: fa cf rjmp .-12 ; 0x76 <main+0xa>

                    00000082 <SIGNAL>:
                    }
                    SIGNAL(__vector_Relocated_UART_RECV)
                    {
                    82: df 93 push r29
                    84: cf 93 push r28
                    86: 00 d0 rcall .+0 ; 0x88 <SIGNAL+0x6>
                    88: 00 d0 rcall .+0 ; 0x8a <SIGNAL+0x8>
                    8a: cd b7 in r28, 0x3d ; 61
                    8c: de b7 in r29, 0x3e ; 62
                    8e: 9c 83 std Y+4, r25 ; 0x04
                    90: 8b 83 std Y+3, r24 ; 0x03
                    uint8_t c;
                    uint8_t st;

                    c=90;
                    92: 8a e5 ldi r24, 0x5A ; 90
                    94: 89 83 std Y+1, r24 ; 0x01
                    st=14;
                    96: 8e e0 ldi r24, 0x0E ; 14
                    98: 8a 83 std Y+2, r24 ; 0x02
                    9a: 0f 90 pop r0
                    9c: 0f 90 pop r0
                    9e: 0f 90 pop r0
                    a0: 0f 90 pop r0
                    a2: cf 91 pop r28
                    a4: df 91 pop r29
                    a6: 08 95 ret

                    000000a8 <_exit>:
                    a8: f8 94 cli

                    000000aa <__stop_program>:
                    aa: ff cf rjmp .-2 ; 0xaa <__stop_program>



                    صد در صد ولی بحث راجع به تعداد رجیستر ها بود که به نحوه تولید کد برمیگرده نه محدودیت های سخت افزاری سیستم.
                    بله شما درست می فرمایید، اما رجیسترها در بازه ای از زمانی که داده ای را مورد استفاده قرار نگیرد به درون پشته ارسال می کند و منظور من پر شدن پشته بود.
                    در کل از 2 روش برای تخصیص رجیستر استفاده شده است. همان طور که در پست قبل گفت در حال اول ما فرض می گیریم که بی نهایت رجیستر داریم (که من از آن به عنوان OP یا عملوند نام بردم) سپس به آنها با توجه رجیستر خالی و نیازی که دارند رجیستر تخصیص می دهیم
                    1- با توجه به اینکه رجیستر منحصر به فرد است پس می توانیم آخرین باری که استفاده شده است را بدست بیاوریم ،بنابراین ، اولین باری که از آن استفاده می شود این رجیستر از میکرو (واقعی یعنی r0-r31) به آن اختصاص می دهدو پس از آخرین باری که استفاده شده ، می توان رجیستر واقعی را برای استفاده در یک op دیگر به کار برد.
                    2- زمانهایی وجود دارد که در آن 2 یا بیشتر op نیاز به یک رجیستر خاص دارند (مثل رجیستر Z) و یا کلا رجیستر کم می آورند ، که در این حالت 2 راه حل وجود دارد 1- می توان در بین دو بازه ای که op خاص (یا کلا opیی) استفاده نمی شود و یک op در آن بازه اولین بار ایجاد می شود و از بین می رود رجیستر مربوط به آن OP را پوش (در میکرو) سپس پس از پایان استفاده آن را پاپ کرد.
                    مثلا
                    ldi op0001,10
                    .
                    .
                    ldi op0002,89
                    sub op0002,op0003
                    .
                    .
                    add op0001,op0003
                    برای op0002 می خواهیم یک رجیستر تخصیص بدهیم ولی همه رجیسترها پر هستند و رجیستر تخصیص داده شده به op0001 نیز r17 می باشد و رجیستر op0003 نیز r8 باشد، در نتیجه کد به صورت زیر در خواهد آمد
                    ldi r17,10
                    .
                    .
                    push r17
                    ldi r17,89
                    sub r17,r8
                    pop r17
                    .
                    .
                    add r17,r8
                    2- اما زمانی که نیاز به یک رجیستر خاص باشد و رجیستر آزاد نیز داشته باشیم (مثلا رجیستر r16 به بالا نیاز داریم ولی r16 به پایین در دسترس می باشد)، در این حالت رجیسترها جاهایشان را با هم عوض می کنند یعنی اگر تا الان از رجیستر r17 به عنوان op0001 و از رجیستر r8 به عنوان op003 استفاده می شود از خط بعد که نیاز به جابهجایی دارد (مثلا دستوری که فقط با رجیسترهای r16 به بالا کار می کنند) ،از رجیستر r8 به عنوان op0001 و از رجیستر r17 به عنوان op0003 استفاده می کند.
                    3- رجیستر خالی نداریم و فاصله بین دو op نیز نمی توانیم استفاده کنیم. پس از هر بار صدا زدن یک رجیستری که ممکن هست (یعنی در آن عملیات نیازی به آن نیست) را پوش (درون میکرو) می کنیم و سپس بعد از اینکه استفاده تمام شد، پاپ می کنیم در همان رجیستر و در هر بار ظاهر شدن آن op یک عمل را تکرار می کنیم.
                    همانطور که می بینید سعی کرذم هیچ موردی پیدا نشه که رجیستر کم بیاریم مگر اینکه پشته کامل پر شود.

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


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

                    دیدگاه


                      #40
                      پاسخ : یک کامپایلر برای همه میکروها ، همه زبانها برای یک کامپایلر

                      میدونی عزیزم!
                      در جایی که من کار میکنم انتظار دارن که اول طراحی انجام بشه و بعد پیاده سازی. اما بارها به این موضوع بر خوردم که خیلی از طراحی ها رو نمیشه بدون یک سری تست های اولیه انجام داد. از من میشنوی این بحث ها رو رها کن. بشین همون MMLtoAVR رو بنویس. بعد باز هم بهم میرسیم. :evil: :evil:

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

                      ضمنا فقط یک نکته. میدونی WinAVR از r0 و r1 چه استفاده ای میکنه؟ :evil: :evil:

                      دیدگاه


                        #41
                        پاسخ : یک کامپایلر برای همه میکروها ، همه زبانها برای یک کامپایلر

                        ضمنا فقط یک نکته. میدونی WinAVR از r0 و r1 چه استفاده ای میکنه؟
                        راستش نمی دونستم ، ولی توی نت گشتم فهمیدم :redface:

                        کد:
                        __tmp_reg__    :   Register r0, used for temporary storage
                         
                        __zero_reg__    :  Register r1, always zero


                        البته در میکروهای Tiny هم فکر کنم برای ارسال و دریافت داده به فلش استفاده شود (این رو با توجه به محدودیت دستوراتی که tiny دارند می گم)

                        از اینکه نظرات و پیشنهاداتتون رو در اختیارم قرار می دهید و من رو همراهی می کنید بسیار سپاسگذارم.

                        هیچ کس و هیچ چیز نمی تونه مانع رسیدنت به آرزوهات بشه مگر خودت

                        دیدگاه


                          #42
                          پاسخ : یک کامپایلر برای همه میکروها ، همه زبانها برای یک کامپایلر

                          تازه یک مورد دیگه: به دستورالعمل mul یک نگاهی بنداز متوجه میشی.
                          یا به دستور LPM. هدفم هم اینه که بدونی صرف تولید کد برای دو تا کد میانی خاص الزامی نداره همه جا کار کنه. چون مثلا دستور mul r5,r6 روی r0 و r1 هم کار میکنه.

                          دیدگاه


                            #43
                            پاسخ : یک کامپایلر برای همه میکروها ، همه زبا

                            ممنون از یادآوریتون


                            هیچ کس و هیچ چیز نمی تونه مانع رسیدنت به آرزوهات بشه مگر خودت

                            دیدگاه


                              #44
                              پاسخ : یک کامپایلر برای همه میکروها ، همه زبانها برای یک کامپایلر

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

                              دیدگاه


                                #45
                                پاسخ : یک کامپایلر برای همه میکروها ، همه زبانها برای یک کامپایلر

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

                                دیدگاه

                                لطفا صبر کنید...
                                X