عرض رمز التحليل التجريبي الواقع الثاني


في 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

جميع الخطوات سهلة القراءة:

  1. تعيين خادم المقاطعة DIS كمقاطعة 0fch.
  2. استبدال مكالمات نظام DOS عن طريق الانقطاع 021h(لمزيد من التفاصيل ، راجع قسم "أوضاع التطوير والتحسين" ).
  3. قم بتنزيل الموسيقى على بطاقة صوت عبر ذاكرة EMS.
  4. تشغيل الموسيقى.
  5. أداء كل جزء من العرض التوضيحي.
  6. منجز!

تفاصيل الإجراءات 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/DIS.ASM(تم تعيين معالج المقاطعة إلى int 0fch).
  • DIS/DISINT.ASM (إجراءات DIS نفسها).
  • بما أن Second Reality مكتوبة جزئيًا أيضًا في C ، فإن الشفرة تحتوي على واجهة لـ C: DIS/DIS.Hand DIS/DISC.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(انظر في الرسم البياني أدناه):

  1. تم ضبط موقت شريحة 8254 لتشغيل IRQ0 بالتردد المطلوب.
  2. يتم استبدال معالج المقاطعة 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 (مع موقع شفرة المصدر ، على الرغم من أن الأسماء يمكن أن تكون مربكة للغاية):

عنوانملف تنفيذيالمبرمجلقطة شاشةمصدر
STARTMUS.EXEالرئيسية / STARTMUS.C
START.EXEويلدفايرSTART / MAIN.c
الجزء المخفيDDSTARS.EXEويلدفايرDDSTARS / STARS.ASM
Alkutekstit iALKU.EXEويلدفايرALKU / MAIN.C
الكوتيكست الثانيU2A.EXEPSIVISU / C / CPLAY.C
الكوتيكست الثالثPAM.EXETRUG / WILDFIREPAM /
BEGLOGO.EXEBEG / BEG.C
غلينزGLENZ.EXEPSIGLENZ /
DottitunneliTUNNELI.EXETRUGTUNNELI / TUN10.PAS
تقنيةTECHNO.EXEPSITECHNO / KOEA.ASM
PanicfakePANICEND.EXEPSIذعر
تمرير VuoriMNTSCRL.EXEFOREST / READ2.PAS
نجوم حلم الصحراءDDSTARS.EXETRUG
عدسةPSI
روتازومرLNS و ZOOM.EXEPSIعدسة /
بلازماويلدفاير
PlasmacubePLZPART.EXEويلدفايرPLZPART /
كرات MiniVectorMINVBALL.EXEPSIDOTS /
بيليبالوسكرولRAYSCRL.EXETRUGالماء / العرض التوضيحي
ثلاثي الأبعاد3DSINFLD.EXEPSICOMAN / DOLOOP.C
JellypicJPLOGO.EXEPSIJPLOGO / JP.C
الجزء الثاني من المتجه "U2E.EXEPSIVISU / C / CPLAY.C
تعليق / شكر وتقدير
ENDLOGO.EXEنهاية / نهاية ج
CRED.EXEويلدفايرالاعتمادات / MAIN.C
ENDSCRL.EXEENDSCRL / MAIN.C

يبدو أن لكل مطور تخصصه الخاص ، والذي يمكن مشاركته في جزء واحد. هذا ملحوظ بشكل خاص في المشهد الأول مع التمرير والسفن والانفجارات (Alkutekstit). على الرغم من أن هذا يبدو وكأنه تأثير واحد مستمر ، إلا أنه في الواقع هو ثلاثة ملفات قابلة للتنفيذ كتبها ثلاثة أشخاص مختلفين:

تسلسل الكوتيكست (الاعتمادات)
ALKU من WILDFIREU2A بواسطة PSIPAM بواسطة TRUG / WILDFIRE

الأصول


.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 مع الإجراءات المحسنة للمجمع (خاصةً تعبئة وظل جورو):

  • CITY.C (الرمز الرئيسي).
  • VISU.C (مكتبة visu.lib).
  • AVID.ASM (فيديو مجمع محسن (التنظيف ، نسخ الشاشة ، إلخ)).
  • ADRAW.ASM (رسم الكائنات والاقتطاع).
  • ACALC.ASM (المصفوفات وحسابات الخطيئة / cos السريعة).


إن بنية هذه المكونات رائعة للغاية: VISUتقوم المكتبة بجميع المهام المعقدة ، على سبيل المثال ، تحميل الأصول: كائنات 3DS والمواد والتدفقات (تحركات الكاميرا والسفن).

يقوم المحرك بفرز الكائنات التي يجب رسمها ، ويجعلها تستخدم خوارزمية الفنان. هذا يؤدي إلى كمية كبيرة من إعادة الرسم ، ولكن نظرًا لأن مزالج VGA تسمح لك بتسجيل 4 بكسل في نفس الوقت ، فهذا ليس سيئًا للغاية.

حقيقة مثيرة للاهتمام: يقوم المحرك بإجراء تحولات بطريقة "المدرسة القديمة": بدلاً من استخدام مصفوفات 4x4 متجانسة شائعة ، فإنه يستخدم مصفوفات دوران 3 * 3 وناقل إزاحة.

في ما يلي ملخص برمز زائف:

      main(){

            scenem=readfile(tmpname);  // Load materials
            scene0=readfile(tmpname);  // Load animation

            for(f=-1,c=1;c<d;c++){  //Load objects
              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 // waitb for retrace via copper simulator interrupt call 
                vid_clear();
                
                // parse animation stream, update objects
                for(;;){}

                vid_cameraangle(fov); // Field of vision

                // Calc matrices and add to order list (only enabled objects)
                for(a=1;ac<conum;a++) if(co[a].on) /* start at 1 to skip camera */
                    calc_applyrmatrix(o->r,&cam);

                // Zsort via Bubble Sort
                for(a=0;ac<ordernum;a++)
                    for(b=a-1;b>=0 && dis>co[order[b]].dist;b--)

                // Draw
                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) ، بالإضافة إلى جافا سكريبت :


All Articles