چگونه به پولدارترین فرد کره زمین تبدیل شویم (و یادگیری کمی گولنگ در مسیر)
پیاده سازی یک نمونه شبیه سازی شده از روند توزیع ثروت میان فقیر و غنی در جامعه
بیشتر بخوانیداین راهنما قصد دارد به معرفی مختصری از دنیای شبیهسازی بپردازد و همچنین به شما یاد میدهد که چگونه خودتان یکی را از ابتدا بنویسید.
پیشنهاد می کنم اگه انگلیسی تون خوب هست اصل مقاله رو از _اینجا _بخوانید.
من شخصاً از اواخر دهه 90 در مورد شبیه سازها هیجان زده بودم.از آنجایی که آن روزها کنسولی نداشتم (فقط یک C64 داشتم)، وقتی فهمیدم میتوانید از یک شبیهساز برای اجرای بازیهای کنسول روی رایانه شخصی استفاده کنید، شگفتزده شدم. هنوز به یاد دارم که بازی Super Mario 3 را روی رایانه شخصی با استفاده از شبیه ساز Snes9x برای کنسول SNES/Super Famicom انجام دادم و چند سال بعد با استفاده از Bleem، بازی Metal Gear Solid را تکمیل کردم! (شبیه ساز PSX).
اما این روزها بیشتر بر روی ارائه پشتیبانی از پروژه های شبیه ساز کنسول های اخیر مانند: شبیه ساز PCSX2 (Sony PlayStation 2) و Dolphin-emu (Nintendo GameCube and Wii) و nullDC (Sega Dreamcast) کار میکنم.
در حالی که این راهنما از شما انتظار دارد که دانش پایه ای در مورد سیستم های کامپیوتری داشته باشید و فرض می کند که یک زبان برنامه نویسی را می دانید، برای افرادی که به طور کلی به شبیه سازی علاقه مند هستند نیز باید خواندنی جالب باشد.
من فکر می کنم مهم است که ابتدا بفهمیم شبیه ساز چیست و چه چیزی نیست.
شبیه ساز یک برنامه کامپیوتری است که طراحی داخلی و عملکرد یک سیستم کامپیوتری (سیستم A) را تقلید می کند. این به کاربران اجازه می دهد تا نرم افزار طراحی شده برای این سیستم خاص (سیستم A) را بر روی یک سیستم کامپیوتری یا معماری کاملاً متفاوت (سیستم B) اجرا کنند.
اغلب مردم یک ایمولاتور را با یک سیمولاتور اشتباه می گیرند و بالعکس. فقط به یاد داشته باشید که این کلمات مترادف نیستند.
(نکته: معمولا در زبان فارسی هر دو کلمه ایمولاتور و سیمولاتور ، “شبیه ساز” ترجمه می شوند )
بیایید به مثال زیر نگاهی بیندازیم:
بازی **Pong **یک بازی تنیس دو بعدی است که توسط **Atari **ساخته شده و بر روی سخت افزار خود اجرا می شود. با این حال، این بازی نه تنها در سیستمهای آتاری، بلکه بر روی پلتفرمهای رقیب مانند Amstrad، Amiga و C64 نیز در دسترس بود.
از آنجایی که آتاری مجوز اجرای هر بازی پُنگ را بر روی این پلتفرم ها نداشت، به این معنی بود که همه بازی های مشابه ، کدهای آتاری را اجرا نمی کردند. اساساً اتفاقی که افتاد این است که مردم پیاده سازی (کلون) خود را از بازی Pong ایجاد کردند. در این مورد آنها ظاهر و رفتار بازی Pong را simulate شبیه سازی کردند.
در صورت وجود ایمولاتور ، ما تصمیم می گیریم که بازی Pong را برای سیستم بومی خود مجدداً بازنویسی نکنیم. در عوض، ما محیط را با یک برنامه کامپیوتری دوباره ایجاد می کنیم که به ما امکان می دهد کد اصلی ماشین Pong را اجرا کنیم. یکی از مزایای این کار این است که نه تنها به ما اجازه می دهد تا Pong را اجرا کنیم، بلکه به هر برنامه دیگری که برای آن پلتفرم توسعه یافته است نیز اجازه اجرای آن را می دهد.
درحقیقت Chip-8 یک زبان برنامه نویسی ساده، تفسیر شده است که برای اولین بار در اواخر دهه 1970 و اوایل دهه 1980 بر روی برخی از سیستم های کامپیوتری ، بخوانید "خودتان انجام دهید (do-it-yourself)" طراحی شد. کامپیوترهای COSMAC VIP، DREAM 6800 و ETI 660 چند نمونه هستند. این رایانه ها معمولاً برای استفاده از تلویزیون به عنوان نمایشگر طراحی شده بودند، دارای حافظه رم (RAM) بین 1 تا 4K (کیلوبایت)بودند و از صفحه کلید هگزادسیمال با 16 کلید برای ورودی استفاده می کردند. مفسر فقط 512 بایت حافظه اشغال می کرد و برنامه هایی که به صورت هگزادسیمال وارد کامپیوتر می شدند، حتی کوچکتر بودند.
در حقیقت CHIP-8 هرگز یک سیستم واقعی نبود، بلکه بیشتر شبیه یک ماشین مجازی (VM) بود که در دهه 70 توسط جوزف ویزبکر(Joseph Weisbecker) توسعه یافت. بازی هایی که به زبان CHIP-8 نوشته شده اند، می توانند به راحتی روی سیستم هایی اجرا شوند که دارای مفسر CHIP-8 هستند.
در اوایل دهه 1990، زبان Chip-8 توسط فردی به نام آندریاس گوستافسون احیا شد. او یک مترجم Chip-8 برای ماشین حساب نموداری HP48 به نام Chip-48 ایجاد کرد. HP48 در آن زمان راهی برای ساخت آسان بازیهای سریع نداشت و Chip-8 پاسخ آن بود. Chip-48 بعداً Super Chip-48 را ایجاد کرد، اصلاحی در Chip-48 که امکان گرافیک با وضوح بالاتر و همچنین سایر پیشرفت های گرافیکی را فراهم می کرد ، Chip-48 الهام بخش یک محصول کاملاً جدید از مترجمان Chip-8 برای پلتفرم های مختلف، از جمله MS-DOS، Windows 3.1، Amiga، HP48، MSX، Adam و ColecoVision است.
نوشتن شبیه ساز CHIP-8 احتمالاً ساده ترین پروژه شبیه سازی(ایمولاتور) است که می توانید انجام دهید. با توجه به تعداد کم کدهای عملیاتی (opcodes) (در مجموع 35 کد برای CHIP-8) و این واقعیت که دستورالعمل های زیادی در _CPU _های پیشرفته تر استفاده می شود، پروژه ای مانند این آموزشی است (درکی بهتر از نحوه کار CPU و نحوه اجرای کد ماشین ). همچنین قابل مدیریت (تعداد کم کدهای عملیاتی برای پیاده سازی) و زمان بر نبودن پیاده سازی آن (پروژه در چند روز به پایان می رسد) بهترین گزینه برای مطالعه آن است.
(اگر عملیات بیتی شما را گیج می کند، ابتدا آنها را مطالعه کنید)
حالا بریم سراغش!
هنگامی که شروع به نوشتن یک شبیه ساز می کنید، مهم است که اطلاعات بیشتری در مورد سیستمی که می خواهید شبیه سازی کنید پیدا کنید. سعی کنید دریابید که چه مقدار حافظه و رجیسترها در سیستم استفاده می شود، از چه معماری استفاده می کند و ببینید آیا می توانید اسناد فنی را که مجموعه دستورالعمل را توصیف می کند، در دست داشته باشید.
در مورد CHIP-8 ، توصیه می کنم به توضیحات CHIP-8 در ویکی پدیا نگاهی بیندازید.
من یک نمای کلی از سیستم CHIP-8 و نکاتی در مورد نحوه اجرای قطعات ضروری به شما ارائه خواهم کرد:
برای اینکه به شما ایده بدهم که چگونه شبیه ساز خود را طراحی کنید، یک نمونه کوچک از یک طرح را ایجاد کردم. این به شما یاد نمی دهد که چگونه از GLUT یا SDL برای مدیریت گرافیک و ورودی استفاده کنید، بلکه فقط به شما نشان می دهد که جریان شبیه ساز شما چگونه باید باشد.
0x00E0 - صفحه را پاک می کند
0xDXYN - یک اسپرایت روی صفحه میکشد
در ادامه به چرخه شبیه سازی خواهیم پرداخت.
در هر چرخه ، متد emulateCycle صدا زده می شود که یک چرخه از CPU تراشه 8 را شبیه سازی می کند. در طول این چرخه، شبیه ساز یک opcode را واکشی، دیکد و اجرا می کند.
در طی این مرحله، سیستم یک opcode را از حافظه در مکانی که توسط شمارنده برنامه (pc) مشخص شده است واکشی می کند. در شبیه ساز Chip-8 ما داده ها در آرایه ای ذخیره می شوند که در آن هر آدرس حاوی یک بایت است. از آنجایی که یک opcode برابر با 2 بایت است، باید دو بایت متوالی را واکشی کنیم و آنها را با هم ادغام کنیم تا opcode واقعی را بدست آوریم.
برای نشان دادن اینکه چگونه این کار می کند، از کد اپکد 0xA2F0 استفاده خواهیم کرد.
برای ادغام هر دو بایت و ذخیره آنها در یک short بدون علامت (نوع داده 2 بایتی) از عملیات بیتی OR استفاده می کنیم:
پس واقعا چه اتفاقی افتاد؟
ابتدا 0xA2 را 8 بیت به سمت چپ منتقل کردیم که 8 صفر اضافه می کند.
در مرحله بعد از عملیات بیتی OR برای ادغام آنها استفاده می کنیم:
همانطور که ما opcode فعلی خود را ذخیره کرده ایم، باید opcode را دیکد کنیم و جدول opcode را بررسی کنیم تا ببینیم به چه معناست. با همان opcode ادامه می دهیم:
اگر به جدول opcode نگاهی بیندازیم، موارد زیر را به ما می گوید:
مثلا ANNN رجیستر I را روی آدرس NNN تنظیم می کند
ما باید مقدار رجیستر اندیس I را برابر آدرس NNN یعنی (0x2F0) تنظیم کنیم.
اکنون که می دانیم با opcode چه کار کنیم، می توانیم opcode را در شبیه ساز خود اجرا کنیم. برای دستورالعمل مثال ما 0xA2F0 به این معنی است که ما باید مقدار 0x2F0 را در ثبات اندیس I ذخیره کنیم. از آنجایی که فقط 12 بیت حاوی مقداری است که باید ذخیره کنیم، برای خلاص شدن از شر چهار بیت اول از عملگر AND (&) استفاده می کنیم. (به چهار بیت nibble گفته می شود):
کد آن:
از آنجایی که هر دستورالعمل 2 بایت است، پس از هر کد عملیاتی باید شمارنده برنامه را دو بایت افزایش دهیم. این درست است مگر اینکه به آدرس خاصی در حافظه بروید یا یک برنامه فرعی را فراخوانی کنید (در این صورت باید شمارنده برنامه را در پشته ذخیره کنید). اگر کد عملیات بعدی باید نادیده گرفته شود، شمارنده برنامه را به چهار افزایش دهید.
علاوه بر اجرای کدهای عملیاتی، تراشه 8 دارای دو تایمر است که باید پیاده سازی کنید. همانطور که در بالا ذکر شد، هر دو تایمر (تایمر تاخیر و تایمر صدا) اگر روی مقداری بزرگتر از صفر تنظیم شده باشند، تا صفر شمارش معکوس می کنند. از آنجایی که این تایمرها روی 60 هرتز شمارش معکوس می کنند، ممکن است بخواهید چیزی را پیاده سازی کنید که چرخه شبیه سازی شما را کُند کند (60 کد عملیاتی را در یک ثانیه اجرا کنید).
اکنون که اصول اولیه شبیهسازی و نحوه عملکرد سیستم را میدانید، زمان آن است که همه قطعات را کنار هم قرار دهید و شروع به کدنویسی شبیهساز(ایمولاتور) کنید.
قبل از اجرای اولین چرخه شبیه سازی، باید وضعیت سیستم خود را آماده کنید. شروع به پاکسازی حافظه و صفر کردن رجیسترها کنید. در حالی که تراشه 8 واقعاً دارای بایوس (BIOS) یا سیستم عامل نیست، یک مجموعه فونت اساسی در حافظه دارد. این فونت باید در محل حافظه 0x50 == 80 و به بعد بارگذاری شود. جزئیات بیشتر در مورد نحوه عملکرد فونتست را میتوانید در انتهای این راهنما بیابید.
نکته مهم دیگری که باید به خاطر بسپارید این است که سیستم انتظار دارد برنامه در محل حافظه 0x200 بارگذاری شود. این بدان معناست که شمارنده برنامه (pc) شما نیز باید روی این مکان تنظیم شود.
پس از اینکه شبیه ساز را مقداردهی اولیه کردید، برنامه را در حافظه بارگذاری کنید (از fopen در حالت باینری استفاده کنید) و شروع به پر کردن حافظه در مکان 0x200 == 512 کنید.
سیستم ما اکنون آماده اجرای اولین کد عملیاتی خود است. همانطور که در بالا ذکر شد، ما باید opcode را واکشی، دیکد و اجرا کنیم. در این مثال ما با خواندن 4 بیت اول کد فعلی شروع می کنیم تا بفهمیم که کد opcode چیست و شبیه ساز باید چه کاری انجام دهد:
در برخی موارد ما نمیتوانیم تنها به چهار بیت اول تکیه کنیم تا ببینیم کد opcode چیست. به عنوان مثال، 0x00E0 و 0x00EE هر دو با 0x0 شروع می شوند. در این مورد یک سوئیچ اضافی اضافه می کنیم و چهار بیت آخر را با هم مقایسه می کنیم:
اجازه دهید نگاهی به کدهای عملیاتی دیگری بیندازیم که ممکن است در ابتدا دلهره آور به نظر برسند.
این opcode زیر روال (subroutine) را در آدرس NNN فراخوانی می کند. از آنجایی که برای آدرس NNN نیاز به پرش موقت داریم، به این معنی است که باید آدرس فعلی شمارنده برنامه را در پشته ذخیره کنیم. پس از ذخیره مقدار شمارنده برنامه در پشته، نشانگر پشته را افزایش دهید تا از بازنویسی پشته فعلی جلوگیری شود. اکنون که شمارنده برنامه را ذخیره کرده ایم، می توانیم آن را به آدرس NNN تنظیم کنیم. به یاد داشته باشید، چون ما یک زیربرنامه را در یک آدرس خاص فراخوانی می کنیم، نباید شمارنده برنامه را دو برابر کنید.
این اپکد مقدار VY را به VX اضافه می کند. ثبات VF در صورت وجود رقم نَقلی بر روی 1 و در صورت عدم وجود روی 0 تنظیم می شود. از آنجا که ثبات فقط می تواند مقادیر 0 تا 255 (مقدار 8 بیتی) را ذخیره کند، به این معنی است که اگر مجموع VX و VY بزرگتر از 255 باشد، نمی توان آن را در ثبات ذخیره کرد (یا در واقع دوباره از 0 شروع به شمارش می کند. ). اگر مجموع VX و VY بزرگتر از 255 باشد، از flag یا پرچم رقم نَقلی استفاده می کنیم تا به سیستم بفهمانیم که مجموع هر دو مقدار واقعاً بزرگتر از 255 بوده است. فراموش نکنید که شمارنده برنامه را پس از اجرای کد عملیاتی دو برابر افزایش دهید.
نمایش ده دهی با کد باینری VX را در آدرس های I ذخیره می کند، I به اضافه 1، و I به علاوه 2
باید اعتراف کنم که نمیتوانستم نحوه پیادهسازی این opcode را بفهمم، بنابراین از راهحل TJA استفاده کردم.
آن opcode ای که مسئول رسم به صفحه نمایش ما هست برابر 0xDXYN است. توضیحات ویکی پدیا موارد زیر را به ما می گوید:
همانطور که شرح کد عملیاتی به ما می گوید، تراشه 8 در واقع با کشیدن sprites روی صفحه نمایش می کشد. به ما مکان جایی که sprite باید رسم شود (opcode به ما می گوید که کدام ثبات V را باید بررسی کنیم تا مختصات X و Y را واکشی کنیم) و تعداد ردیف ها (N) را به ما می دهد. عرض هر اسپرایت ثابت است (8 بیت / 1 بایت). وضعیت هر پیکسل با استفاده از عملیات XOR بیتی تنظیم می شود. این بدان معنی است که وضعیت پیکسل فعلی را با مقدار فعلی در حافظه مقایسه می کند. اگر مقدار فعلی با مقدار موجود در حافظه متفاوت باشد، مقدار بیت 1 خواهد بود. اگر هر دو مقدار مطابقت داشته باشند، مقدار بیت 0 خواهد بود.
بیایید فرض کنیم که اپکد 0xD003 بود. این بدان معنی است که می خواهد یک اسپرایت در مکان 0.0 بکشد که 3 ردیف ارتفاع دارد. در محل حافظه I، مقادیر زیر تنظیم شد:
این 3 بایت چگونه یک sprite را نشان می دهد؟ به مقادیر باینری هر بایت نگاهی بیندازید:
شما باید از نمایش باینری برای پر کردن آرایه خود ([ ]gfx) استفاده کنید .قبل از تنظیم مقدار در [ ]gfx با استفاده از عملگر XOR، همچنین باید بررسی کنید که آیا هر یک از پیکسل ها از 1 به 0 تغییر کرده است یا خیر. (این آزمایشی برای تشخیص برخورد خواهد بود).
سیستم Chip 8 از یک صفحه کلید HEX ساده استفاده می کند که به کاربران اجازه می دهد با سیستم تعامل داشته باشند. برای شبیه ساز ما این بدان معناست که ما باید روشی را پیاده سازی کنیم که وضعیت هر کلید را در متغیری که حالت های کلید را کنترل می کند، تنظیم کند. در هر چرخه باید وضعیت ورودی کلید را بررسی کنید و آن را در [ ]key ذخیره می کند.
در واقع مهم نیست که چه مقداری را ذخیره میکنید، زیرا کد opcode 0xEX9E و 0xEXA1 فقط بررسی میکنند که آیا کلید خاصی فشار داده شده یا فشرده نشده است. Opcode 0xFX0A فقط برای فشار دادن کلید منتظر می ماند و زمانی که یک کلید دریافت کرد، نام کلید را در ثبات ذخیره می کند و نه حالت کلید را.
در زیر نمونه ای از طرح اصلی صفحه کلید را خواهید دید. واقعاً مهم نیست که چگونه نگاشت کلید را اجرا می کنید، اما من چیزی را در سمت راست پیشنهاد می کنم.
این مجموعه فونت Chip 8 است. عرض هر عدد یا کاراکتر 4 پیکسل و ارتفاع آن 5 پیکسل است.
ممکن است درست شبیه آرایه ای از اعداد تصادفی به نظر برسد، اما به موارد زیر دقت کنید:
به مثال سمت چپ نگاه کنید که ما داریم عدد 0 را ترسیم می کنیم. همانطور که می بینید می بینید که از 5 مقدار تشکیل شده است. از هر مقدار، ما از نمایش باینری برای ترسیم استفاده می کنیم. توجه داشته باشید که فقط چهار بیت اول (nibble) برای ترسیم یک عدد یا کاراکتر استفاده می شود.
امیدواریم این راهنما اطلاعات کافی برای شروع پروژه شبیه ساز خود در اختیار شما قرار دهد. حداقل اکنون باید درک اولیه ای از نحوه کار شبیه سازی و شاید درک بهتری از نحوه اجرای کدهای عملیاتی توسط یک CPU داشته باشید.
من پیاده سازی خود را از یک مفسر چیپ 8 قرار داده ام که در زیر می توانید از آن به عنوان مرجع استفاده کنید. فایل فشرده zip حاوی یک باینری برای ویندوز است اما همچنین شامل سورس کد کامل شبیه ساز(ایمولاتور) است. از آنجایی که سورس کد کامل ارائه شده است، توصیه میکنم فقط به فایل chip8.cpp به عنوان آخرین راهحل نگاه کنید تا ببینید چگونه یک کد عملیاتی خاص را پیادهسازی کردهام. فایل chip8.h و main.cpp باید بدون لو دادن (Spoil) بیش از حد قابل مطالعه باشد. در واقع، main.cpp عمدتا حاوی کد GLUT است که می توانید در پروژه های دیگر (غیر مرتبط با شبیه ساز) نیز دوباره از آن استفاده کنید.
اگر این راهنما برای شما مفید بود به من اطلاع دهید! اگر سوالی دارید یا فکر میکنید که بخشهای اساسی از دست رفته است، لطفاً از بخش نظرات این سایت استفاده کنید 🙂 !
پیاده سازی یک نمونه شبیه سازی شده از روند توزیع ثروت میان فقیر و غنی در جامعه
بیشتر بخوانیدمقدمه تا کنون، ما یک بلاکچین ساختهایم که همه ویژگیهای کلیدی را دارد آدرسهای ناشناس، امن و بهطور تصادفی تولید شده. ذخیره سازی داده های ب…
بیشتر بخوانید