چگونه می توان یک شبیه ساز (ایمولاتور) برای مفسر CHIP-8 نوشت

Post image

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

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

من شخصاً از اواخر دهه 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 چیست؟

درحقیقت 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 شروع کنیم؟

نوشتن شبیه ساز CHIP-8 احتمالاً ساده ترین پروژه شبیه سازی(ایمولاتور) است که می توانید انجام دهید. با توجه به تعداد کم کدهای عملیاتی (opcodes) (در مجموع 35 کد برای CHIP-8) و این واقعیت که دستورالعمل های زیادی در _CPU _های پیشرفته تر استفاده می شود، پروژه ای مانند این آموزشی است (درکی بهتر از نحوه کار CPU و نحوه اجرای کد ماشین ). همچنین قابل مدیریت (تعداد کم کدهای عملیاتی برای پیاده سازی) و زمان بر نبودن پیاده سازی آن (پروژه در چند روز به پایان می رسد) بهترین گزینه برای مطالعه آن است.

نکاتی برای قبل از شروع …

  • یک زبان برنامه نویسی را انتخاب کنید که با آن آشنا هستید (C/C++ یا Java رایج هستند)
  • مثال‌های زیر از C/C++ استفاده می‌کنند
  • از این پروژه به عنوان راهی برای یادگیری برنامه نویسی استفاده نکنید.

(اگر عملیات بیتی شما را گیج می کند، ابتدا آنها را مطالعه کنید)

  • احتمالاً برای مدیریت خروجی صدا/تصویر و ورودی کاربر (GLUT / SDL / DirectX) نیاز به استفاده از کتابخانه های شخص ثالث دارید.

حالا بریم سراغش!

مشخصات CPU

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

در مورد CHIP-8 ، توصیه می کنم به توضیحات CHIP-8 در ویکی پدیا نگاهی بیندازید.

من یک نمای کلی از سیستم CHIP-8 و نکاتی در مورد نحوه اجرای قطعات ضروری به شما ارائه خواهم کرد:

  • این CHIP-8 دارای 35 opcodes است که همگی دو بایتی هستند. برای ذخیره opcode فعلی، به یک نوع داده نیاز داریم که به ما امکان ذخیره دو بایت را بدهد. یک short بدون علامت (unsigned short) دو بایت طول دارد و بنابراین با نیازهای ما مطابقت دارد:
  • چیپ 8 در مجموع دارای حافظه 4K (چهار کیلوبایت) است که می توانیم آن را به صورت زیر شبیه سازی کنیم:
  • رجیسترهای CPU: چیپ 8 دارای 15 رجیستر 8 بیتی همه منظوره با نام های V1، V0 تا VE است. رجیستر شانزدهم به عنوان “پرچم یا flag برای رقم نَقلی” (carry flag) استفاده می شود. هر هشت بیت یک بایت است بنابراین می توانیم از یک char بدون علامت برای این منظور استفاده کنیم:
  • یک رجیستر اندیس گذاری با نام (I) و یک شمارنده برنامه (pc) وجود دارد که می تواند مقداری از 0x000 تا 0xFFF داشته باشد.
  • نقشه حافظه سیستم:
  • سیستم گرافیکی: تراشه 8 دارای یک کد عملیاتی است که اسپرایت(sprite) را در صفحه نمایش می کِشد. این ترسیم در حالت XOR انجام می شود و اگر یک پیکسل در نتیجه ترسیم خاموش شود، رجیستر VF تنظیم می شود. این برای تشخیص برخورد استفاده می شود.
  • گرافیک چیپ 8 سیاه و سفید است و صفحه نمایش آن در مجموع 2048 پیکسل (64 در 32) دارد. این را می توان به راحتی با استفاده از آرایه ای که حالت پیکسل (1 یا 0) را نگه می دارد پیاده سازی کرد:
  • وقفه ها و ثبات سخت افزار. چیپ 8 هیچ کدام را ندارد، اما دو تایمر ثبت کننده وجود دارد که روی 60 هرتز شمارش می کنند. وقتی با مقداری بالای صفر تنظیم شوند انها شمارش معکوس تا مقدار صفر خواهند کرد.
  • هر زمان که تایمر صدا به صفر برسد، زنگ سیستم به صدا در می آید.
  • مهم است که بدانید مجموعه دستورالعمل تراشه 8 دارای کدهای عملیاتی است که به برنامه اجازه می دهد به یک آدرس خاص بپرد یا یک برنامه فرعی فراخوانی کند. در حالی که در مشخصات CHIP-8 ، یک پشته ذکر نشده است، شما باید خودتان یکی را به عنوان بخشی از مفسر پیاده سازی کنید. پشته برای به خاطر سپردن مکان فعلی قبل از انجام پرش استفاده می شود. بنابراین هر زمان که یک پرش انجام دادید یا یک برنامه فرعی فراخوانی کردید، پیش از ادامه، شمارنده برنامه را در پشته ذخیره کنید. این سیستم دارای 16 سطح پشته است و برای اینکه به خاطر بسپارید از کدام سطح پشته استفاده می شود، باید یک نشانگر پشته (sp) یا Stack Pointer را پیاده سازی کنید.
  • در نهایت، تراشه 8 دارای یک صفحه کلید مبتنی بر HEX (0x0-0xF) از 0 تا F است، می توانید از یک آرایه برای ذخیره وضعیت فعلی کلید استفاده کنید.

چرخه اجرای بازی یا Game Loop

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

  • خط 3-5: در این مثال فرض می کنیم که یک کلاس جداگانه برای مدیریت کدهای عملیاتی (opcodes) ایجاد می کند.
  • خط 10-11: تنظیمات گرافیکی (اندازه پنجره، حالت نمایش، و غیره) و سیستم ورودی به صورت توابع صدا می کند
  • خط 14: حافظه، رجیسترها و صفحه نمایش را پاک می کند
  • خط 15: برنامه را در حافظه کپی می کند
  • خط 21: یک چرخه از سیستم را شبیه سازی می کند
  • خط 24: از آنجایی که سیستم در هر چرخه صفحه نمایش را ترسیم نمی کند، زمانی که باید صفحه نمایش خود را به روز کنیم باید یک پرچم یا flag ترسیم تنظیم کنیم. فقط دو کد عملیاتی باید این پرچم را تنظیم کنند:
0x00E0 - صفحه را پاک می کند
0xDXYN - یک اسپرایت روی صفحه می‌کشد
  • خط 28: اگر کلیدی را فشار دهیم یا رها کنیم، باید این حالت را در قسمتی که صفحه کلید را شبیه سازی می کند، ذخیره کنیم.

چرخه شبیه سازی یا Emulation cycle

در ادامه به چرخه شبیه سازی خواهیم پرداخت.

در هر چرخه ، متد emulateCycle صدا زده می شود که یک چرخه از CPU تراشه 8 را شبیه سازی می کند. در طول این چرخه، شبیه ساز یک opcode را واکشی، دیکد و اجرا می کند.

واکشی کد عملیاتی یا Fetch opcode

در طی این مرحله، سیستم یک opcode را از حافظه در مکانی که توسط شمارنده برنامه (pc) مشخص شده است واکشی می کند. در شبیه ساز Chip-8 ما داده ها در آرایه ای ذخیره می شوند که در آن هر آدرس حاوی یک بایت است. از آنجایی که یک opcode برابر با 2 بایت است، باید دو بایت متوالی را واکشی کنیم و آنها را با هم ادغام کنیم تا opcode واقعی را بدست آوریم.

برای نشان دادن اینکه چگونه این کار می کند، از کد اپکد 0xA2F0 استفاده خواهیم کرد.

برای ادغام هر دو بایت و ذخیره آنها در یک short بدون علامت (نوع داده 2 بایتی) از عملیات بیتی OR استفاده می کنیم:

پس واقعا چه اتفاقی افتاد؟

ابتدا 0xA2 را 8 بیت به سمت چپ منتقل کردیم که 8 صفر اضافه می کند.

در مرحله بعد از عملیات بیتی OR برای ادغام آنها استفاده می کنیم:

دیکد opcode

همانطور که ما opcode فعلی خود را ذخیره کرده ایم، باید opcode را دیکد کنیم و جدول opcode را بررسی کنیم تا ببینیم به چه معناست. با همان opcode ادامه می دهیم:

اگر به جدول opcode نگاهی بیندازیم، موارد زیر را به ما می گوید:

مثلا ANNN رجیستر I را روی آدرس NNN تنظیم می کند

ما باید مقدار رجیستر اندیس I را برابر آدرس NNN یعنی (0x2F0) تنظیم کنیم.

اجرای opcode

اکنون که می دانیم با opcode چه کار کنیم، می توانیم opcode را در شبیه ساز خود اجرا کنیم. برای دستورالعمل مثال ما 0xA2F0 به این معنی است که ما باید مقدار 0x2F0 را در ثبات اندیس I ذخیره کنیم. از آنجایی که فقط 12 بیت حاوی مقداری است که باید ذخیره کنیم، برای خلاص شدن از شر چهار بیت اول از عملگر AND (&) استفاده می کنیم. (به چهار بیت nibble گفته می شود):

کد آن:

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

تایمرها یا Timers

علاوه بر اجرای کدهای عملیاتی، تراشه 8 دارای دو تایمر است که باید پیاده سازی کنید. همانطور که در بالا ذکر شد، هر دو تایمر (تایمر تاخیر و تایمر صدا) اگر روی مقداری بزرگتر از صفر تنظیم شده باشند، تا صفر شمارش معکوس می کنند. از آنجایی که این تایمرها روی 60 هرتز شمارش معکوس می کنند، ممکن است بخواهید چیزی را پیاده سازی کنید که چرخه شبیه سازی شما را کُند کند (60 کد عملیاتی را در یک ثانیه اجرا کنید).

دست به کار شوید

اکنون که اصول اولیه شبیه‌سازی و نحوه عملکرد سیستم را می‌دانید، زمان آن است که همه قطعات را کنار هم قرار دهید و شروع به کدنویسی شبیه‌ساز(ایمولاتور) کنید.

مقدار دهی اولیه سیستم یا Initialize system

قبل از اجرای اولین چرخه شبیه سازی، باید وضعیت سیستم خود را آماده کنید. شروع به پاکسازی حافظه و صفر کردن رجیسترها کنید. در حالی که تراشه 8 واقعاً دارای بایوس (BIOS) یا سیستم عامل نیست، یک مجموعه فونت اساسی در حافظه دارد. این فونت باید در محل حافظه 0x50 == 80 و به بعد بارگذاری شود. جزئیات بیشتر در مورد نحوه عملکرد فونت‌ست را می‌توانید در انتهای این راهنما بیابید.

نکته مهم دیگری که باید به خاطر بسپارید این است که سیستم انتظار دارد برنامه در محل حافظه 0x200 بارگذاری شود. این بدان معناست که شمارنده برنامه (pc) شما نیز باید روی این مکان تنظیم شود.

بارگذاری برنامه یا بازی نوشته شده برای CHIP-8 در حافظه

پس از اینکه شبیه ساز را مقداردهی اولیه کردید، برنامه را در حافظه بارگذاری کنید (از fopen در حالت باینری استفاده کنید) و شروع به پر کردن حافظه در مکان 0x200 == 512 کنید.

شبیه سازی را شروع کنید

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

در برخی موارد ما نمی‌توانیم تنها به چهار بیت اول تکیه کنیم تا ببینیم کد opcode چیست. به عنوان مثال، 0x00E0 و 0x00EE هر دو با 0x0 شروع می شوند. در این مورد یک سوئیچ اضافی اضافه می کنیم و چهار بیت آخر را با هم مقایسه می کنیم:

نمونه های opcode

اجازه دهید نگاهی به کدهای عملیاتی دیگری بیندازیم که ممکن است در ابتدا دلهره آور به نظر برسند.

مثال اول opcode 0x2NNN

این opcode زیر روال (subroutine) را در آدرس NNN فراخوانی می کند. از آنجایی که برای آدرس NNN نیاز به پرش موقت داریم، به این معنی است که باید آدرس فعلی شمارنده برنامه را در پشته ذخیره کنیم. پس از ذخیره مقدار شمارنده برنامه در پشته، نشانگر پشته را افزایش دهید تا از بازنویسی پشته فعلی جلوگیری شود. اکنون که شمارنده برنامه را ذخیره کرده ایم، می توانیم آن را به آدرس NNN تنظیم کنیم. به یاد داشته باشید، چون ما یک زیربرنامه را در یک آدرس خاص فراخوانی می کنیم، نباید شمارنده برنامه را دو برابر کنید.

مثال دوم opcode 0x8XY4

این اپکد مقدار VY را به VX اضافه می کند. ثبات VF در صورت وجود رقم نَقلی بر روی 1 و در صورت عدم وجود روی 0 تنظیم می شود. از آنجا که ثبات فقط می تواند مقادیر 0 تا 255 (مقدار 8 بیتی) را ذخیره کند، به این معنی است که اگر مجموع VX و VY بزرگتر از 255 باشد، نمی توان آن را در ثبات ذخیره کرد (یا در واقع دوباره از 0 شروع به شمارش می کند. ). اگر مجموع VX و VY بزرگتر از 255 باشد، از flag یا پرچم رقم نَقلی استفاده می کنیم تا به سیستم بفهمانیم که مجموع هر دو مقدار واقعاً بزرگتر از 255 بوده است. فراموش نکنید که شمارنده برنامه را پس از اجرای کد عملیاتی دو برابر افزایش دهید.

مثال سوم opcode x0FX33

نمایش ده دهی با کد باینری VX را در آدرس های I ذخیره می کند، I به اضافه 1، و I به علاوه 2

باید اعتراف کنم که نمی‌توانستم نحوه پیاده‌سازی این opcode را بفهمم، بنابراین از راه‌حل TJA استفاده کردم.

مدیریت گرافیک و ورودی

ترسیم پیکسل ها

آن opcode ای که مسئول رسم به صفحه نمایش ما هست برابر 0xDXYN است. توضیحات ویکی پدیا موارد زیر را به ما می گوید:

  • یک sprites در مختصات (VX, VY) می‌کشد که عرض آن 8 پیکسل و ارتفاع N پیکسل است. هر ردیف 8 پیکسلی با شروع از مقداری که در محل حافظه ثبات I به صورت بیت کد خوانده می شود. ارزش ثبات I بعد از اجرای این دستورالعمل تغییر نمی کند. همانطور که در بالا توضیح داده شد، اگر پیکسل های صفحه نمایش از حالت تنظیم به حالت تنظیم نشده در هنگام ترسیم اسپرایت برگردند، VF روی 1 تنظیم می شود و اگر این اتفاق نیفتد، روی 0 تنظیم می شود.

همانطور که شرح کد عملیاتی به ما می گوید، تراشه 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 تغییر کرده است یا خیر. (این آزمایشی برای تشخیص برخورد خواهد بود).

نمونه ای از پیاده سازی Opcode 0xDXYN

  • خط 3-4: موقعیت و ارتفاع اسپرایت را مشخص میکند
  • خط 5: مقدار پیکسل
  • خط 8: تنظیم مجدد VF ثبات
  • خط 9: روی هر سطر حلقه می زند
  • خط 11: مقدار پیکسل را از حافظه که از محل I شروع می شود واکشی می کند
  • خط 12: روی 8 بیت از یک سطرحلقه می زند
  • خط 14: بررسی می کندکه آیا پیکسل ارزیابی شده فعلی روی 1 تنظیم شده است (توجه داشته باشید که 0x80 » xline اسکن از طریق بایت، یک بیت در آن زمان است)
  • خط 16-17: بررسی می کند که آیا پیکسل روی نمایشگر روی 1 تنظیم شده است یا خیر. اگر تنظیم شده است، باید با تنظیم رجیستر VF برخورد را ثبت کنیم.
  • خط 18: مقدار پیکسل را با استفاده از XOR تنظیم می کند
  • خط 23: ما آرایه [ ]gfx خود را تغییر دادیم و بنابراین باید صفحه را به روز کنیم.
  • خط 24: شمارنده برنامه را به روز کنید تا به کد عملیاتی بعدی بروید

ورودی کاربر

سیستم Chip 8 از یک صفحه کلید HEX ساده استفاده می کند که به کاربران اجازه می دهد با سیستم تعامل داشته باشند. برای شبیه ساز ما این بدان معناست که ما باید روشی را پیاده سازی کنیم که وضعیت هر کلید را در متغیری که حالت های کلید را کنترل می کند، تنظیم کند. در هر چرخه باید وضعیت ورودی کلید را بررسی کنید و آن را در [ ]key ذخیره می کند.

در واقع مهم نیست که چه مقداری را ذخیره می‌کنید، زیرا کد opcode 0xEX9E و 0xEXA1 فقط بررسی می‌کنند که آیا کلید خاصی فشار داده شده یا فشرده نشده است. Opcode 0xFX0A فقط برای فشار دادن کلید منتظر می ماند و زمانی که یک کلید دریافت کرد، نام کلید را در ثبات ذخیره می کند و نه حالت کلید را.

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

مجموعه فونت CHIP-8

این مجموعه فونت Chip 8 است. عرض هر عدد یا کاراکتر 4 پیکسل و ارتفاع آن 5 پیکسل است.

ممکن است درست شبیه آرایه ای از اعداد تصادفی به نظر برسد، اما به موارد زیر دقت کنید:

به مثال سمت چپ نگاه کنید که ما داریم عدد 0 را ترسیم می کنیم. همانطور که می بینید می بینید که از 5 مقدار تشکیل شده است. از هر مقدار، ما از نمایش باینری برای ترسیم استفاده می کنیم. توجه داشته باشید که فقط چهار بیت اول (nibble) برای ترسیم یک عدد یا کاراکتر استفاده می شود.

جمع بندی

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

من پیاده سازی خود را از یک مفسر چیپ 8 قرار داده ام که در زیر می توانید از آن به عنوان مرجع استفاده کنید. فایل فشرده zip حاوی یک باینری برای ویندوز است اما همچنین شامل سورس کد کامل شبیه ساز(ایمولاتور) است. از آنجایی که سورس کد کامل ارائه شده است، توصیه می‌کنم فقط به فایل chip8.cpp به عنوان آخرین راه‌حل نگاه کنید تا ببینید چگونه یک کد عملیاتی خاص را پیاده‌سازی کرده‌ام. فایل chip8.h و main.cpp باید بدون لو دادن (Spoil) بیش از حد قابل مطالعه باشد. در واقع، main.cpp عمدتا حاوی کد GLUT است که می توانید در پروژه های دیگر (غیر مرتبط با شبیه ساز) نیز دوباره از آن استفاده کنید.

اگر این راهنما برای شما مفید بود به من اطلاع دهید! اگر سوالی دارید یا فکر می‌کنید که بخش‌های اساسی از دست رفته است، لطفاً از بخش نظرات این سایت استفاده کنید 🙂 !

You May Also Like