-
Notifications
You must be signed in to change notification settings - Fork 0
Project Report
دانشگاه شهید بهشتی - دکتر عطارزاده
محمدمعین عربی – محمدعلی آقایانی
در این پروژه یک بازی با نام flappy-bird پیادهسازی شده است. این بازی با زبان اسمبلی x86
مبتنی بر سیستمعامل DOS و به صورت گرافیکی ساخته شده است. یک دسته کنترلر سختافزاری هم به همراه آن برای نمایش امتیاز طراحی شده است.
در این بازی کاربر یک پرنده در حال سقوط را کنترل میکند. کاربر میتواند با زدن یکی از دکمههای روی کیبوردش پرنده را به بالا ببرد. زمانی که پرنده از صفحه بازی خارج شود یا به یکی از موانع برخورد کند، بازی تمام میشود.
از زمان شروع بازی، کد در یک حلقه بینهایت عملیاتهای زیر را انجام میدهد:
- تاخیر: یک تاخیر چند میلیثانیهای ایجاد میشود تا سرعت بازی برای کاربر مناسبسازی شود.
- ورودی کیبورد: اگر کاربر دکمهای از کیبورد را بزند پرنده به سمت بالا پرواز میکند.
- جایگاه پرنده و مکان موانع بازی در هر چرخه تغییر میکند و به سمت پرنده میروند و موانع جدیدی تولید میشود.
- امتیاز کاربر در هر چرخه افزوده میشود و نمایش داده میشود.
- برخورد پرنده با موانع بررسی میشود که اگر برخورد کرده باشد، بازی تمام میشود.
در این بازی کاربر میتواند از کیبورد برای بالا بردن پرنده استفاده کند. هر بار که کاربر یک دکمه از کیبورد را میزند، یک کاراکتر بافر میشود. برنامه ما در هر بار اجرای حلقه بینهایتش این بافر را چک میکند. اگر بافر خالی باشد یعنی کاربر در این مدت هیچ دکمهای را فشار نداده و پرنده باید به سقوط خود به پایین ادامه دهد. اگر بافر پر باشد، ابتدا بافر را خالی میکند و سپس بردار سرعت پرنده را به سمت بالا تغییر میدهد.
وقفهی 0x16
قابلیتی برای چک کردن بافر کیبورد در اختیار قرار میدهد. اگر بافر پر باشد فلگ Zero
را 0 میکند.
سپس اگر بافر پر باشد به کمک این نوع وقفه میتوان بافر را خواند و خالی کرد.
تعیین حالت صفحه به کمک وقفههای شماره 0x10
صورت میگیرد. داس مودهای گرافیکی زیادی دارد ولی در این پروژه ما از مود شماره 0x13
استفاده کردهایم:
دلیل انتخاب رزولوشن 320 در 200، جا شدن تمام پیکسلهای آن در یک سگمنت 64 کیلوبایتی از حافظه است و این باعث راحتی کار با آن میشود. طبق توضیحات دستورالعمل، سگمنت مورد نظر از آدرس 0xA000
شروع میشود. دسترسی به پیکسلها و تغییر رنگ آنها به این صورت محاسبه میشود:
دلیل ضرب در 320 آن است که در هر ردیف 320 پیکسل وجود دارد و هر پیکسل یک مقدار متناظر 1 بایتی در حافظه دارد. به طور مثال اگر مقدار خانه مورد نظر حافظه برابر 0 باشد، پیکسل متناظر با آن بدون رنگ (خاموش) میشود یا اگر مقدار آن 4 باشد، رنگ پیکسل قرمز میشود.
فونتهایی به صورت پیشفرض وجود دارند که یک کاراکتر را به پیکسلهای صفحه نظیر میکند و آن را نشان میدهد. برای نمایش امتیاز کاربر در بالای صفحه، ابتدا باید امتیاز عددی را به کارکتر تبدیل کنیم و سپس به کمک وقفه شماره 0x10
نمایش دهیم. جزئیات وقفه مورد نظر در پایین قابل مشاهده است:
با قرار دادن کاراکتر مورد نظر در رجیستر AL و صدا زدن وقفه، کارکتر در صفحه چاپ میشود. برای پاک کردن همین کارکترها از صفحه میتوان کاراکتر back-space را نوشت تا کارکترها تک به تک پاک شوند.
در حرکت پرنده از چند قانون فیزیک استفاده کردیم. فرمول مکان-زمان:
فرمول سرعت-زمان:
جهت مثبت را رو به پایین فرض شده و هر بار موقعیت جدید پرنده و سرعت آن را به کمک این فرمول محاسبه میشود. اگر کاربر از کیبورد برای بالا بردن پرنده استفاده کند، سرعت پرنده، یک عدد منفی میشود. منفی شدن سرعت به معنای تغییر جهت آن به سمت بالا است و بعد از مدتی به خاطر مثبت بودن شتاب، بردار سرعت پرنده دوباره رو به پایین قرار میگیرد.
برای نمایش موانع از اعداد تصادفی استفاده شده است. به این صورت که موانع جدید با طول، ارتفاع و جایگاه (بالا یا پایین صفحه) متفاوت به صورت تصادفی ایجاد میشوند.
اعداد تصادفی به کمک وقفه 0x1A
ایجاد میشوند. این وقفه ساعت سیستم را در رجیسترهایی ذخیره میکند و ما از آن برای تولید عدد تصادفی استفاده کردهایم:
تغییر پیکسلهای صفحه باعث چشمک زدن آنها میشد. به خاطر همین به جای اینکه هر بار کل صفحه را پاک کنیم و دوباره آبجکتهای جدید را در مکانهای جدید بکشیم، صرفا پیکسلهای قبلی همان آبجکتها را خاموش کردیم و آبجکت جدید با موقعیت جدید را ایجاد کردیم. به این شکل باعث بهبود کارایی شد. همچنین برای کمتر شدن چشمک زدن موانع مستطیل شکلی که از راست به چپ در حال حرکت هستند، صرفا دو ضلع راست و چپی آنها را برای انتقال پاک میکردیم. پاک نکردن ضلع بالا و پایین باعث افزایش کارایی میشد.
از آنجایی که هر کدام از آبجکتها یک مختصات مشخص در صفحه دارند، به سادگی میتوان برخورد پرنده با مانع را تشخیص داد. مثلا برای تمام آبجکتهای مستطیل شکل مختصات گوشه سمت چپ بالا و گوشه سمت راست پایین وجود دارد. با مقایسه مختصات پرنده که آیا وارد محدوده یک مانع شده است یا نه، میتوان فهمید که پرنده با مانع برخورد کرده است یا نه. مشابه همین اتفاق در بررسی برخورد پرنده با حاشیههای بالا و پایین اتفاق میافتد.
در این قسمت یک دسته کنترلر به کمک STM32 پیادهسازی شده است. این کنترلر از یک LCD برای نمایش امتیاز کاربر در بازی استفاده میکند. ارتباط بین محیط DOSbox و کنترلر به کمک ارتباط سریال UART انجام میشود.
در فایل کانفیگ DOSbox باید مشخص شود که از کدام پورت COM
موجود در سیستم جهت ارتباط باید استفاده کند. مانند زیر:
serial1=directserial realport:COM1
سپس به کمک وقفههای شماره 14
باید تنظیمات UART را انجام دهیم.
از رجیستر AL
برای مشخص کردن baudrate، parity، تعداد stop بیت و طول دیتا استفاده میکنیم. چون از serial1 استفاده کردیم، رجیستر DX
را برابر صفر قرار میدهیم.
از دستورات IN
و OUT
برای ارسال و دریافت دیتا استفاده میشود. آدرس پورت COM1
برابر 3F8
است و دیتا در این آدرس IO خوانده و نوشته میشود. در DOS وقفههایی برابر ارسال و دریافت وجود دارد ولی متاسفانه به خوبی کار نمیکردند!
از یک نرمافزار مجازیساز پورت COM
مثل com0com برای ایجاد دو پورت مجازی در سیستم استفاده کردیم. به طور مثال پورت با نام COM1
به DOSbox متصل میشود و پورت دیگر با نام COM9 به پروتئوس.
اتصال به پورت COM
در پروتئوس به کمک COMPIM انجام میشود. تنظیمات لازم مثل baudrate باید روی COMPIM انجام شود.
پین TXD
باید به پین Transfer بورد یا ترمینال متصل شود و پین RXD
به پین Receiver بورد یا ترمینال متصل شود.
پس از آنکه از صحت کار کردن UART در DOSbox مطمئن شدیم، یک پروژه جدید با CubeMX ایجاد کردیم و تنظیمات مربوط به پورت سریال را انجام دادیم. از طریق همان COMPIM در پروتئوس، دو سیم RX
و TX
را متصل کردیم. به کمک کدهای موجود از آزمایشات قبلی یک LCD برای نمایش دیتای دریافتی استفاده کردیم.
برای کاهش تاخیر ناشی از کار با IO از سمت DOSbox تنها امتیازات با مضرب 10 ارسال میشوند. امتیازات کاربر به صورت یک عدد 16 بیتی در دو بسته 8 بیتی از سمت DOSbox ارسال میشود. به ازای دریافت هر بسته یک بایتی، یک شماره بسته (مثلا 1 یا 2) به عنوان یک بسته ACK ارسال میشود تا 8 بیت دوم امتیاز کاربر ارسال شود. این کار ارسال یک بسته ACK به دلیل این است که از وقفههای خود DOS برای ارسال استفاده نشده است در نتیجه از بیتهای وضعیت UART نمیتوان استفاده کرد و مطمئن شد که یک بایت دیتا ارسال شده است یا نه. پس از دریافت یک بسته 2 بایتی، عدد تبدیل به رشته کاراکتر میشود و در LCD نمایش داده میشود.
دریافت بستههای UART در STM32 مبتنی بر وقفه پیادهسازی شده است تا هر موقع بسته جدیدی آمد، در interrupt handler اقدامات لازم صورت بگیرد. یک سوییچ هم به عنوان دکمهی پرش پرنده استفاده شده است. این سوییچ مبتنی بر وقفه کار میکند و هر زمان که کاربر این دکمه را بزند، در زمانی که دریافت امتیاز انجام نمیشود، یک بایت دیتای مشخص ارسال میشود. این دیتا توسط بازی خوانده میشود و پرش پرنده را انجام میدهد.
کارکرد LCD به این صورت است که 8 پین ورودی دیتا دارد و 3 پین کنترلی. پین RS
مشخص میکند که در حال حاضر قرار است یک بایت دیتای کاراکتر به LCD منتقل شود یا یک بایت دیتای دستور (دستوراتی مثل پاک کردن صفحه نمایش، جابجایی cursor و ...). پین RW
آن تعیین کننده خواندن یا نوشتن در LCD است که در این آزمایش ما فقط از حالت نوشتن آن استفاده کردهایم. پین EN
زمانی 1 میشود که دیتا روی 8 پین LCD پایدار شده و آماده دریافت توسط LCD است و پس از حدود یک میلیثانیه دوباره صفر میشود.
یک منبع خوب مورد استفاده، داکیومنت وقفههای داس در این لینک است.