في 23 يوليو 2013، و الكود لعرض واقع الثاني (1993) ونشرت . مثل الكثيرين ، كنت متلهفًا للنظر إلى جوانب العرض التوضيحي التي ألهمتنا كثيرًا على مر السنين.كنت أتوقع أن أرى فوضى متجانسة من المجمع ، ولكن بدلاً من ذلك ، لدهشتي ، اكتشفت بنية معقدة تجمع بأمان عدة لغات. لم يسبق لي أن رأيت مثل هذا الرمز من قبل ، ويمثل تمامًا جانبين لا يتجزأ من تطوير عرض توضيحي:- العمل بروح الفريق الواحد.
- التعتيم.
كالعادة ، قمت بتشكيل مقال لملاحظاتي: آمل أن يوفر ذلك شخصًا لبضع ساعات ، وربما يلهم الآخرين لقراءة المزيد من التعليمات البرمجية المصدر وأن يصبحوا مهندسين أكثر خبرة.الجزء الأول: مقدمة
تجريبي
قبل الشروع في الرمز ، سأقدم رابطًا لالتقاط العرض التوضيحي الأسطوري في الفيديو عالي الدقة (مايكل هت). اليوم ، هذه هي الطريقة الوحيدة لتقييم العرض التوضيحي بالكامل دون حدوث أخطاء رسومية (حتى DOSBox لا يمكنه تشغيله بشكل صحيح).الاتصال الأول مع الرمز
يتم نشر كود المصدر على جيثب. فقط أدخل أمر واحد git
:git clone git@github.com:mtuomi/SecondReality.git
في البداية ، المحتوى مربك: 32 مجلدًا وغامضًا U2.EXE
لا يبدأ في DosBox.العرض التوضيحي كان له عنوان العمل "Unreal 2" (أول "Unreal" كان عرض طاقم المستقبل السابق ، الذي صدر عن الجمعية الأولى في عام 1992). وفقط أثناء عملية التطوير ، تم تغيير الاسم إلى "الواقع الثاني". يوضح هذا اسم الملف "U2.EXE" ، ولكن ليس سبب عدم عمل الملف ...إذا قمت بتشغيل CLOC ، فسوف نحصل على مقاييس مثيرة للاهتمام: -------------------------------------------------------------------------------
-------------------------------------------------------------------------------
Assembly 99 3029 1947 33350
C++ 121 1977 915 24551
C/C++ Header 8 86 240 654
make 17 159 25 294
DOS Batch 71 3 1 253
-------------------------------------------------------------------------------
SUM: 316 5254 3128 59102
-------------------------------------------------------------------------------
- «» 50% .
- Doom.
- لديها سبعة عشر makefiles. لماذا لا واحد فقط؟
إطلاق عرض توضيحي
من الصعب معرفة ذلك ، ولكن يمكن إطلاق الإصدار التجريبي الذي تم إصداره في DosBox: تحتاج إلى إعادة تسميته U2.EXE
وتشغيله من المكان الصحيح.عندما علمت بالتعليمات الداخلية للكود ، بدأت تبدو منطقية للغاية: القرص المضغوط الرئيسي
نقل U2.EXE DATA / SECOND.EXE
بيانات القرص المضغوط
SECOND.EXE
وفويلا!هندسة معمارية
في التسعينات ، تم توزيع العروض التوضيحية في الغالب على أقراص مرنة. بعد التفريغ ، كان من الضروري تثبيت ملفين كبيرين: SECOND.EXE
و REALITY.FC
: . <دير> 08/08/2013 16:40
.. <DIR> 08/01/2013 16:40
FCINFO10 TXT 48،462 04-10-1993 11:48
FILE_ID DIZ 378 04-10-1993 11:30
اقرأ 1ST 4.222 04-10-1993 12:59
REALITY FC 992.188 07-10-1993 12:59
الإصدار الثاني 1،451،093 07-10-1993 13:35
5 ملفات 2.496،343 بايت.
2 درهم 262111144 بايت مجاناً.
بناءً على خبرتي في تطوير اللعبة ، أتوقع دائمًا أن تبدو الصورة بأكملها كما يلي:SECOND.EXE
: محرك بجميع التأثيرات في ملف قابل للتنفيذ.REALITY.FC
: الأصول (الموسيقى ، المؤثرات الصوتية ، الصور) بتنسيق خاص / مشفر WAD
.
ولكن بعد قراءتها ، MAIN/PACK.C
وجدت أنني كنت مخطئًا إلى حد كبير: محرك الواقع الثاني هو مجرد محمل وخادم مقاطعة (يسمى DIS). كل مشهد (يسمى أيضًا "جزء") هو عرض DOS تنفيذي كامل الوظائف. يتم تحميل كل جزء بواسطة محمل Loader ويتم تشغيله واحدًا تلو الآخر. يتم تخزين الأجزاء في شكل مشفر في النهاية SECOND.EXE
:REALITY.FC
يحتوي على مقطعين موسيقيين تم تشغيلهما أثناء العرض التوضيحي (لملء التعتيم المضاف والتعبئة في البداية).SECOND.EXE
يحتوي على محمل إقلاع وخادم مقاطعة تجريبي (DIS).- بعد النهاية
SECOND.EXE
، تتم إضافة 32 جزءًا (جزء) من العرض التوضيحي كملفات DOS قابلة للتنفيذ (مشفرة).
توفر هذه الهندسة العديد من المزايا:- : PART ,
_start
(450 ). - EXE
SECOND.EXE
-. - : Loader DIS 20 . DOS .
- : PART PART .
- / : , PART ( ), : EXE , .
- يمكن استخدام أي لغة لبرمجة PART: في الكود نجد C و Assembly ... و Pascal.
اقتراحات للقراءة
الركائز الثلاث لفهم كود مصدر الواقع الثاني هي VGA ، المجمع ، وهندسة الكمبيوتر (برمجة PIC و PIT). فيما يلي بعض الروابط المفيدة للغاية:الجزء الثاني: محرك الواقع الثاني
كما نوقش في الجزء الأول ، يتكون أساس الواقع الثاني من:- أداة تحميل التشغيل باعتبارها DOS قابلة للتنفيذ.
- مدير الذاكرة (تجمع مكدس بسيط)
- خادم المقاطعة التجريبية (DIS).
في هذا الجزء سأقدم توصيات للمبرمجين الذين يرغبون في قراءة المحرك ومحمل الإقلاع (سيتم مناقشة DIS في الجزء التالي).كود المحرك
رمز المحرك هو 100 ٪ ASM ، ولكنه مكتوب بشكل جيد للغاية وموثق جيدًا إلى حد ما:في الرمز الزائف يمكن كتابته على النحو التالي: exemus db 'STARTMUS.EXE',0
exe0 db 'START.EXE',0
...
exe23 db 'ENDSCRL.EXE',0
start:
cli ; Disable all interrupts
mov ah,4ah ; Deallocate all memory
call checkall ; Check for 570,000 bytes of mem, 386 CPU and VGA
call file_getexepath
call dis_setint ; Install Demo Interrupt Server on Interrupt 0fch
call file_initpacking ; Check exe signature (no tempering) !
call file_setint ; Replace DOS routines (only OPENFILE, SEEK and READ) on Interrupt 021h
call flushkbd ; Flush the keyboard buffer
call checkcmdline ; check/process commandline
;======== Here we go! ========
call vmode_init ; Init VGA (not necessarly Mode13h or ModeX), each PARTs had its own resolution
mov si,OFFSET exe0
call executehigh ; loaded to high in memory. Used for loading music loaders and stuff.
call _zinit ; Start music
call restartmus
mov si,OFFSET exe1 ;Parameter for partexecute: Offset to exec name
call partexecute
; Execute all parts until exe23
call fademusic
;======== And Done! (or fatal exit) ========
fatalexit:
mov cs:notextmode,0
call vmode_deinit
جميع الخطوات سهلة القراءة:- تعيين خادم المقاطعة DIS كمقاطعة
0fch
. - استبدال مكالمات نظام DOS عن طريق الانقطاع
021h
(لمزيد من التفاصيل ، راجع قسم "أوضاع التطوير والتحسين" ). - قم بتنزيل الموسيقى على بطاقة صوت عبر ذاكرة EMS.
- تشغيل الموسيقى.
- أداء كل جزء من العرض التوضيحي.
- منجز!
تفاصيل الإجراءات execute
: execute:
cld
call openfile ; Open the DOS executable for this PART
call loadexe ; loads the specified exe file to memory, does relocations and creates psp
call closefile
call runexe ;runs the exe file loaded previously with loadexe.
; returns after exe executed, and frees the memory
; it uses.
مدير الذاكرة
كانت هناك العديد من الأساطير التي تستخدم Second Reality مدير ذاكرة معقد عبر MMU ؛ لم تكن هناك آثار في المحرك. يتم نقل إدارة الذاكرة بالفعل إلى DOS: يبدأ المحرك عن طريق تحرير جميع ذاكرة الوصول العشوائي ثم توزيعها عند الطلب . الحيلة الصعبة الوحيدة هي القدرة على تخصيص ذاكرة الوصول العشوائي من نهاية الكومة: يتم ذلك باستخدام قيمة إرجاع DOS malloc عند طلب ذاكرة وصول عشوائي كبيرة .الجزء 3: DIS
يوفر Demo Interrupt Server (DIS) مجموعة كبيرة من الخدمات لكل جزء: من تبادل البيانات بين جزء مختلف إلى المزامنة مع VGA.خدمات المفرزة
في وقت التشغيل ، PART ، يوفر خادم DIS الخدمات له. يمكن العثور على قائمة الميزات في DIS/DIS.H
.اهم الخدمات:- التبادل بين PART (
dis_msgarea
) مختلف : يوفر DIS ثلاثة مخازن مؤقتة كل منها 64 بايت بحيث يمكن لـ PART استقبال المعلمات من محمل PART السابق. - Emulation Copper (
dis_setcopper
): محاكي Amiga Copper الذي يسمح لك بتنفيذ العمليات التي يتم تبديلها بواسطة حالة VGA. - وضع Dev / Prod (
dis_indemo
): يسمح لـ PART بمعرفة أنه يعمل في وضع DEV (مما يعني أنه يجب تهيئة الفيديو) أو تشغيله من أداة تحميل التشغيل في وضع PROD. - عدد إطارات VGA (
_dis_getmframe
) - في انتظار VGA backstop (
dis_waitb
).
عرض تجريبي لرمز خادم المقاطعة
رمز مصدر DIS هو أيضًا 100 ٪ ASM ... وتم التعليق عليه جيدًا:كيف تعمل
تم تعيين DIS كمعالج مقاطعة int 0fch
. الشيء العظيم في ذلك هو أنه يمكن تشغيله داخليًا SECOND.EXE
عند تشغيل العرض التوضيحي ، أو كبرنامج مقيم ( TSR ) في وضع Dev. تتيح لك هذه المرونة اختبار عروض PART المختلفة بشكل فردي أثناء التطوير: // لنتظاهر بأننا مطور FC ونريد أن نبدأ جزء STAR مباشرة.
C: \> CD DDSTARS
C: \ DDSTARS> ك
خطأ: لم يتم تحميل DIS.
// عفوًا ، تعذر على PART العثور على DIS عند int 0fch.
C: \ DDSTARS> CD .. \ DIS
C: \ DIS> DIS
Demo Int Server (DIS) V1.0 Copyright (C) 1993 The Future Crew
نسخة بيتا - تجميع: 07/26/93 03:15:53
تم التثبيت (int fc).
ملاحظة: لا يدعم خادم DIS هذا مزامنة النحاس أو الموسيقى!
// DIS مثبت ، فلنحاول مرة أخرى.
C: \ DIS> CD ../DDSTARS
C: \ DDSTARS> ك
وفويلا!نحاس
"النحاس" هو المعالج المشترك الذي أحبه مطورو النسخة التجريبية لأميغا. كانت جزءًا من مجموعة الشرائح الأصلية وسمحت لك بتنفيذ دفق قابل للبرمجة من الأوامر المتزامنة مع معدات الفيديو. لم يكن هناك مثل هذا المعالج المشترك على جهاز الكمبيوتر ، وكان على Future Crew كتابة محاكي نحاسي يعمل داخل DIS.استخدم فريق FC شرائح الأجهزة 8254-PIT و 8259-PIC لمحاكاة النحاس. لقد أنشأت نظامًا متزامنًا مع تردد VGA ، قادرًا على بدء الإجراءات في ثلاثة أماكن من الحزمة الخلفية الرأسية :- وضع 0: بعد تشغيل الشاشة (تقريبًا على خط المسح 25)
- ضع 1: مباشرة بعد المسح الشعاعي العكسي (من الممكن تجنبه ممكن)
- ضع 2: في الشعاع العكسي لحزمة المسح
يمكن قراءة كيفية القيام بذلك MAIN/COPPER.ASM
(انظر في الرسم البياني أدناه):- تم ضبط موقت شريحة 8254 لتشغيل IRQ0 بالتردد المطلوب.
- يتم استبدال معالج المقاطعة 8h (والذي يتم استدعاؤه بواسطة 8259 PIC بعد تلقي IRQ0) بإجراء هنا
intti8
.
ملاحظة: يتم توفير خدمة عد إطارات DIS في الواقع بواسطة محاكي النحاس.الجزء الرابع: وضعي التطوير والتحفيز
عند قراءة شفرة المصدر لـ Second Reality ، ستندهش كثيرًا من مقدار الاهتمام الذي أولاه الفريق للتحول السلس من DEV إلى PROD.وضع التنمية
في وضع التطوير ، كان كل مكون من العرض التوضيحي عبارة عن ملف تنفيذي منفصل.- تم تحميل DIS في TSR المقيم وتم الوصول إليه من خلال مقاطعة
0cfh
. - تسبب برنامج bootloader في مقاطعة DOS
21h
لفتح الملفات وقراءتها والبحث عنها وإغلاقها.
يتميز تكوين DEV هذا بالمزايا التالية:- يمكن لكل مبرمج وفنان العمل على الملف القابل للتنفيذ واختباره بشكل منفصل ، دون التأثير على بقية الفريق.
- يمكن اختبار العرض التوضيحي الكامل في أي وقت باستخدام عرض صغير
SECOND.EXE
(بدون إضافة كل EXEs إلى النهاية). تم تحميل الملف التنفيذي لكل جزء باستخدام مقاطعة DOS 021h
من ملف منفصل.
الإنتاج (الوضع التجريبي)
في وضع الإنتاج ، SECOND.EXE
تم دمج العناصر الصغيرة (التي تحتوي على أداة تحميل التشغيل) و DIS وأجزاء من الإصدار التجريبي ك EXEs منفصلة في واحدة سميكة SECOND.EXE
.- تم الوصول إلى DIS عبر المقاطعة
0fch
. - تم تصحيح واجهة برمجة تطبيقات مقاطعة DOS 21h من خلال إجراءات Future Crew الخاصة بها ، والتي تفتح الملفات من نهاية ملف كبير
SECOND.EXE
.
يتميز تكوين PROD هذا بميزة وقت التحميل والحماية من الهندسة العكسية ... ولكن الأهم من وجهة نظر البرمجة أو تحميل PART ، لا يتغير شيء عند التبديل من DEV إلى PROD.الجزء 5: جزء منفصل
كل من التأثيرات المرئية للواقع الثاني هو DOS قابل للتنفيذ يعمل بكامل طاقته. يطلق عليها PART وجميعها 23. سمح هذا الحل المعماري بالنماذج الأولية السريعة والتطور الموازي (حيث أن FC على الأرجح لم يكن لديه أدوات التحكم في الإصدار) والاختيار الحر للغات (ASM و C وحتى Pascal موجودة في المصدر).جزء منفصل
يمكن العثور على قائمة بجميع أجزاء PART / EXE في شفرة مصدر المحرك: U2.ASM . فيما يلي وصف موجز أكثر ملاءمة لجميع الأجزاء الـ 23 (مع موقع شفرة المصدر ، على الرغم من أن الأسماء يمكن أن تكون مربكة للغاية):يبدو أن لكل مطور تخصصه الخاص ، والذي يمكن مشاركته في جزء واحد. هذا ملحوظ بشكل خاص في المشهد الأول مع التمرير والسفن والانفجارات (Alkutekstit). على الرغم من أن هذا يبدو وكأنه تأثير واحد مستمر ، إلا أنه في الواقع هو ثلاثة ملفات قابلة للتنفيذ كتبها ثلاثة أشخاص مختلفين:الأصول
.LBM
يتم إنشاء Image Assets ( ) باستخدام Deluxe Paint ، وهو محرر صور نقطية شائع للغاية في التسعينات. ومن المثير للاهتمام ، يتم تحويلها إلى مجموعة من البايتات وتجميعها داخل الجزء. ونتيجة لذلك ، يقوم ملف exe أيضًا بتنزيل جميع الأصول. بالإضافة إلى ذلك ، هذا يعقد الهندسة العكسية.بين بارد مجموعات الأصول هي مشهورة CITY و سفينة من أحدث مشهد 3D:الجزء الداخلي
نظرًا لأنه تم تجميعها جميعًا في ملفات DOS التنفيذية ، في PART ، يمكن استخدام أي لغة:بالنسبة لاستخدام الذاكرة ، قرأت الكثير عن MMU على ويكيبيديا والمواقع الأخرى ... ولكن في الواقع ، يمكن لكل جزء استخدام أي شيء ، لأنه بعد التنفيذ تم تفريغه بالكامل من الذاكرة.عند العمل مع VGA ، استخدم كل جزء مجموعة الحيل الخاصة به وعمل في حلها. في كل منهم ، لم يتم استخدام الوضع 13h وليس ModeX ، بل تم استخدام وضع 13h المعدل مع الدقة الخاصة به. غالبًا ما يذكر ملف SCRIPT 320 × 200 و 320 × 400.لسوء الحظ ، عند تحليل PART ، تصبح قراءة التعليمات البرمجية المصدر مهمة شاقة: تنخفض جودة التعليمات البرمجية والتعليقات بشكل كبير. ربما حدث هذا بسبب الاندفاع أو لأن كل جزء عمل على مطوره الخاص (أي لم تكن هناك حاجة "حقيقية" للتعليقات أو فهم الشفرة) ، لكن النتيجة كانت مربكة تمامًا:خوارزميات متطورة يصعب فهمها حتى أسماء المتغيرات ليس فقط ( a
، b
، co[]
...). سيكون الرمز أكثر قابلية للقراءة إذا ترك لنا المطورون تلميحات في ملاحظات الإصدار. ونتيجة لذلك ، لم أخصص الكثير من الوقت لدراسة كل جزء. كان الاستثناء هو المحرك ثلاثي الأبعاد المسؤول عن U2A.EXE و U2E.EXE.محرك ثلاثي الأبعاد الواقع الثاني
على أي حال ، قررت أن أدرس بالتفصيل محرك 3D ، الذي تم استخدامه في جزأين: U2A.EXE
و U2E.EXE
.رمز المصدر هو C مع الإجراءات المحسنة للمجمع (خاصةً تعبئة وظل جورو):إن بنية هذه المكونات رائعة للغاية: VISU
تقوم المكتبة بجميع المهام المعقدة ، على سبيل المثال ، تحميل الأصول: كائنات 3DS والمواد والتدفقات (تحركات الكاميرا والسفن).يقوم المحرك بفرز الكائنات التي يجب رسمها ، ويجعلها تستخدم خوارزمية الفنان. هذا يؤدي إلى كمية كبيرة من إعادة الرسم ، ولكن نظرًا لأن مزالج VGA تسمح لك بتسجيل 4 بكسل في نفس الوقت ، فهذا ليس سيئًا للغاية.حقيقة مثيرة للاهتمام: يقوم المحرك بإجراء تحولات بطريقة "المدرسة القديمة": بدلاً من استخدام مصفوفات 4x4 متجانسة شائعة ، فإنه يستخدم مصفوفات دوران 3 * 3 وناقل إزاحة.في ما يلي ملخص برمز زائف: main(){
scenem=readfile(tmpname);
scene0=readfile(tmpname);
for(f=-1,c=1;c<d;c++){
sprintf(tmpname,"%s.%03i",scene,e);
co[c].o=vis_loadobject(tmpname);
}
vid_init(1);
vid_setpal(cp);
for(;;){
vid_switch();
_asm mov bx,1 _asm int 0fch
vid_clear();
for(;;){}
vid_cameraangle(fov);
for(a=1;ac<conum;a++) if(co[a].on)
calc_applyrmatrix(o->r,&cam);
for(a=0;ac<ordernum;a++)
for(b=a-1;b>=0 && dis>co[order[b]].dist;b--)
for(a=0;ac<ordernum;a++)
vis_drawobject(o);
}
}
return(0);
}
موانئ للأنظمة الحديثة
بعد إصدار هذه المقالة ، بدأ العديد من المطورين في نقل الواقع الثاني إلى الأنظمة الحديثة. بدأ Claudio Matsuoka في إنشاء منفذ sr ، منفذ C لنظام Linux و OpenGL ES 2.0 ، والذي يبدو حتى الآن مثيرًا للإعجاب. قام Nick Kovacs بعمل رائع على PART PLZ ، ونقله إلى C (الآن هو جزء من شفرة مصدر sr-port) ، بالإضافة إلى جافا سكريبت :