A Sam
A Sam
خواندن ۹ دقیقه·۴ سال پیش

گرافیک کامپیوتری توسط زبان اسمبلی در شبیه ساز DOSbox




فهرست

  • Development Environment Setup
  • Drawing Pixels
  • Bigger Pixels
  • Creating Time Movement Illusion
  • Boundaries and collision detection
  • More objects
  • Keyboard Input
  • More collisions
  • The Code




توضیحات در ویدئوی آپارات موجود است:
https://www.aparat.com/v/p2QSX




منابع مهم:
https://en.wikipedia.org/wiki/INT_10H
https://stanislavs.org/helppc/int_10-0.html
https://en.wikipedia.org/wiki/BIOS_color_attributes
http://spike.scu.edu.au/~barry/interrupts.html
http://spike.scu.edu.au/~barry/interrupts.html#ah2c
https://www.stanislavs.org/helppc/int_16.html
http://www.asciitable.com
https://www.azurefromthetrenches.com/introductory-guide-to-aabb-tree-collision-detection/




یک. Development Environment Setup

برای محیط کار، به یک شبیه‌ساز، یک اسمبلر و یک ویرایشگر متن احتیاج است. برای شبیه‌ساز، از شبیه‌ساز DOSbox محصول شرکت IBM استفاده شده. این برنامه، متن باز و رایگان می‌باشد. - برای اسمبلر، فایلها را می‌توانید از این لینک زیر تهیه نمایید:

https://drive.google.com/drive/folders/1akM4UNg6StiVE3ehzEstOgOhEw1JBxA0

برای ویرایشگر متن نیز از ++ notepad استفاده کرده‌ام.

سپس، یک پوشه در محل دلخواه ایجاد کرده و فایلهای اسمبلر را در آن قرار میدهیم. در ++notepad نیز زبان پیشفرض را assembly قرار داده و یک فایل با پسوند asm .در پوشهی ساخته شده ذخیره میکنیم. حال باید شبیه‌ساز DOS را با کامپیوتر آشنا کنیم. ابتدا پوشه را در محل مورد نظر (در این مثال در دسکتاپ کامپیوتر) ایجاد کرده، شبیه‌ساز را اجرا کرده و دایرکتوری دلخواه (در اینجا C) را در آن بارگذاری می‌نمائیم.

مراحل بارگذاری به این صورت انجام می‌گردد (همانند تصویر):

Z:\> mount [optional directory name] [local directory]
Z:\> [navigate to local directory]:


توجه شود که در [name dir optional] یک کاراکتر دلخواه وارد می‌کنیم و یک دایرکتوری جدید تعریف می‌کنیم. در [Directory local] محل پوشه ساخته شده را وارد میکنیم که در این مثال، آدرس پوشهی ساخته شده به آن داده شد. از برنامه‌ی exe.MASM (مخفف assembler macro) که داخل پوشه هست (یکی از فایل‌های اسمبلر) میتوانیم جهت بررسی کد اسمبلی و خطایابی آن استفاده کنیم.

C:\> masm /a my_simulation.asm


با اجرای دستور [asm.file [link نیز یک فایل اجرایی از کد اسمبلی ایجاد میکنیم.

برای تست این خطایابی، یک برنامه‌ی ساده در فایل asm.sim (همان فایل اسمبلی ایجاد شده) قرار داده شد و بعد masm اجرا گردید و همانطور که مشاهده می‌کنید خطا و اخطار خاصی در آن یافت نشد. اینکار یک نسخه با پسوند شی (obj.) از فایل میسازد.

  • نکته: برای ایجاد فایل اجرایی و موقع استفاده از برنامهی LINK توجه داشته باشید که هنگام دریافت ورودی، ‘;’ را وارد کنید.
  • نکته: هنگام استفاده از MASM ،موقع دریافت ورودی با ‘Enter ‘مراحل آن را skip کنید.
  • نکته: در این شبیهساز، حروف به طور پیش فرض حرف بزرگ هستند و برعکس سیستمهای امروزی، برای کوچک‌نویسی از shift استفاده می‌شود.
  • نکته: درصورت تولید شدن فایل اجرایی توسط linker میتوانید در شبیه‌ساز و در دایرکتوری مربوطه نام فایل را تایپ کرده آنرا اجرا کنید.





دو. Drawing Pixels

قبل از ادامه، یک مشکل وجود دارد که باید به رفع آن بپردازیم. با هر بار بستن محیط شبیه‌ساز، تمام تنظیمات آن reset شده و باید از سر گرفته شود. خوشبختانه DOSbox شامل فایل تنظیمات است. کافیست -DOSbox options را جستجو کرده یا در پوشه‌ی نصبی آن به دنبال فایل conf. بگردید و آن را با یک ویرایشگر متن باز کنید. در انتهای این فایل متنی، بخشی با عنوان autoexec وجود دارد. هر دستوری را زیر این بخش بنویسید حین باز شدن برنامه به طور خودکار اجرا میشوند. بد نیست که تنظیمات خودمان را در این شبیه‌ساز پیاده کنیم. (به جهت slashها دقت کنید.)

حال با هربار باز و بستن محیط شبیه‌ساز، نیازی به تنظیم دستی این تنظیمات نیست. در صورت تمایل می‌توانید دیگر دستورات دلخواهتان را به این بخش اضافه کنید. حال می‌توانیم به ادامه کار بپردازیم. جهت ترسیم یک پیکسل بر صفحه از وقفه‌ی 10h استفاده میکنیم. نام این وقفه مخفف 10hex call interrupt BIOS است که سرویس‌های ویدیویی را اداره میکند. رجیستر AH به ازای مقادیر مختلف وارد مدهای مختلف می‌شود. این مقادیر بر اینترنت موجود هستند، اما اینجا آنهایی را بررسی میکنیم که مورد استفاده خواهند بود. حالت 00h:AH حالت mode video است. در این مود باید مقدار AL را تعیین کنیم. مقدار 13 حالت مد نظر ماست که یک صفحه با ابعاد 320x200 رسم می‌کند و 256 رنگ را پشتیبانی می‌کند. در بدنهی اصلی برنامه‌ی اسمبلی، این تنظیمات را چنین بیان میکنیم:

MOV AH,00h
MOV AL,13h
INT 10h


فراخوانی 10h INT در انتهای هر قطعه کد، تنظیمات را ثبت می‌کند. اگر برنامه را تا همینجا اجرا کنید (فراموش نکنید ابتدا linker را فراخوانی کنید تا فایل اجرایی آپدیت شود.) میبینید همه چیز درشت‌تر شده است. دلیل آن این است که به مود گرافیکی تغییر حالت دادیم. در حال حاضر چیزی ترسیم نمی‌شود و کار خاصی انجام نمی‌شود. اما در مود گرافیکی می‌توان همه چیز صفحه را کنترل کرد. برای مثال اگر بخواهیم رنگ پس زمینه را به دلخواه تغییر دهیم باید کد صحیح آنرا بیابیم و استفاده کنیم. برای تعیین رنگ پس‌زمینه و رنگ حاشیه باید مقدار AH را برابر 0Bh و مقدار BH را معادل 01h قرار داده تا وارد بخش تنظیمات آن شویم. کافیست در بدنه‌ی اصلی برنامه، این کدها را ضمیمه نمود:

MOV AH,0Bh
MOV BH,00h
MOV BL,00h
INT 10h


به طور پیش فرض، 16 رنگ برای متن و 8 رنگ برای پس‌زمینه قابل استفاده است. لیست رنگ‌های موجود در اینترنت به باینتری موجود است. کد مربوط به ترسیم یک پیکسل معادل 0Ch=AH و کد مربوط به خوانش پیکسل‌ها معادل 0Dh=AH است. کد مربوط به رسم پیکسل شامل صفاتی از قبیل رنگ (AL)، شماره صفحه (BH)، مختصات نسبت به محور x در CX و مختصات نسبت به محور y در DX می‌باشد. کد مربوط به رسم پیکسل:

MOV AH,0Ch

MOV AL,0Ah

MOV BH,00h

MOV CX,96h

MOV DX,64h

INT 10h


رنگ پیکسل را سبز درنظر گرفتم و مختصات پیکسل را وسط صفحه گرفتم. (وسط صفحه‌ی 320x200 پیکسلی معادل 100y – 160x بوده و معادل آن در هگز، 64h x 96h است.)





سه. Bigger Pixels

ابتدا مختصات x و y را در یک رجیستر ذخیره می‌کنیم (خارج از تابع اصلی main) که راحتتر از آنها استفاده کنیم. بنابرین دو متغییر (یکی برای مختصات عمودی و دیگری مختصات افقی) تعریف میکنیم:

bigx DW 96h bigy DW 64h


از رجیستر DW استفاده کردیم که مخفف Word Define است که 16بیت حافظه به خود اختصاص می‌دهد. یک متغییر هم برای تعیین اندازهی پیکسل تعریف میکنیم:

pixel_size DW 05h


حال یک procedure جدید لازم داریم که پیکسل با اندازه‌ی دلخواه را ترسیم کند. آنرا خارج از بدنه‌ی اصلی تعریف کرده و ابتدا و انتهایش را مشخص می‌کنیم:

generate_resized_pixel PROC NEAR RET generate_resized_pixel ENDP


هنگام تعریف کردن این بدنه‌ی جدید، از کلیدواژه‌ی NEAR استفاده کردیم، چراکه می‌خواهیم این را در بدنه‌ی اصلی (که FAR است) فراخوانی کنیم. کد مرتبط با رسم پیکسل را نیز در همین بدنه قرار می‌دهیم و بجای آن، دستور فراخوانی را قرار می‌دهیم:

CALL generate_resized_pixel


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

generate_resized_pixel PROC NEAR MOV CX,pixel_x ;initial position x MOV DX,pixel_y ;initial position y generate_resized_pixel_horizontal: MOV AH,0Ch MOV AL,0Ch MOV BH,00h INT 10h INC CX MOV AX,CX SUB AX,pixel_x CMP AX,pixel_size JNG generate_resized_pixel_horizontal RET generate_resized_pixel ENDP


همانطور که مشاهده می‌کنید، ابتدا مختصات مبدا پیکسل را ذخیره کرده‌ایم. سپس یک label قرار داده‌ایم که بتوانیم بعدا به آن برگردیم، و مراحل ترسیم پیکسل را در label قرار داده‌ایم. پس از هر بار ترسیم پیکسل، یک واحد به رجیستر CX (که مختصات x را ذخیره کرده) اضافه شده و پس از آن، شرط حلقه بررسی میشود. به این صورت که تفاضل خانهای که آخرین پیکسل در آن ترسیم شده است با مبدا رسم را با اندازهی تعریف شده برای پیکسل مقایسه شده و بنابر نتیجهی این مقایسه، شرط JNG (مخفف jump if not greater) اجرا میشود. کد باال، یک ردیف از پیکسل‌ها را کنار یکدیگر ترسیم می‌کند. برای اینکه اینکار را به صورت عمودی نیز تکرار کند، شرطی که نوشتیم را اینبار نسبت به DX (ذخیره کننده‌ی مختصات y) می‌نویسیم:

MOV CX,pixel_x INC DX MOV AX,DX SUB AX,pixel_y CMP AX,pixel_size JNG generate_resized_pixel_horizontal


ابتدا CX را معادل مقدار اولیه‌ی خود قرار می‌دهیم (چراکه میخواهیم در سطر، دوباره تعداد پیکسل رسم کند) و یک واحد به DX اضافه می‌کنیم تا به اندازه‌ی یک پیکسل در ارتفاع پایین رود. (توجه داشته باشید که مبدا مختصات یا خانهی 0=y,x گوشه‌ی بالا سمت چپ تصویر قرار دارد و برای حرکت در سطر به x مقادیر مثبت و برای حرکت در ستون به y مقادیر مثبت نسبت می‌دهیم.) حاصل این اقدامات چنین می‌شود:




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

آرادحمیدسمیعیزبان اسمبلیگرافیک کامپیوتریphysics enginegraphics engine
شاید از این پست‌ها خوشتان بیاید