Code-Analyse-Demo Second Reality


Am 23. Juli 2013 wurde der Quellcode für die Second Reality-Demo (1993) veröffentlicht . Wie viele andere war ich gespannt auf die Innenseiten der Demo, die uns im Laufe der Jahre so sehr inspiriert hat.

Ich hatte erwartet, dass der Assembler ein monolithisches Chaos erleben würde, aber stattdessen entdeckte ich zu meiner Überraschung eine komplexe Architektur, die mehrere Sprachen elegant kombiniert. Ich habe noch nie einen solchen Code gesehen, der zwei wesentliche Aspekte der Entwicklung einer Demo perfekt darstellt:

  • Zusammenarbeit.
  • Verschleierung.

Wie üblich habe ich einen Artikel für meine Notizen verfasst: Ich hoffe, dies spart jemandem ein paar Stunden und inspiriert vielleicht andere, mehr Quellcode zu lesen und erfahrenere Ingenieure zu werden.

Teil 1: Einführung


Demo


Bevor ich mit dem Code beginne, werde ich einen Link geben, um die legendäre Demo in HD-Video (Michael Hut) aufzunehmen. Heute ist dies die einzige Möglichkeit, die Demo ohne grafische Störungen vollständig auszuwerten (selbst DOSBox kann sie nicht korrekt starten).


Erster Kontakt mit Code


Der Quellcode wird auf GitHub veröffentlicht. Geben Sie einfach einen Befehl ein git:

git clone git@github.com:mtuomi/SecondReality.git

Der Inhalt ist zunächst verwirrend: 32 Ordner und ein mysteriöser U2.EXE, der nicht in DosBox startet.


Die Demo hatte den Arbeitstitel "Unreal 2" (das erste "Unreal" war die vorherige Future Crew-Demo, die 1992 für die erste Versammlung veröffentlicht wurde). Und erst während des Entwicklungsprozesses wurde der Name in „Second Reality“ geändert. Dies erklärt den Dateinamen "U2.EXE", aber nicht, warum die Datei nicht funktioniert ...

Wenn Sie CLOC ausführen , erhalten wir interessante Metriken:

    -------------------------------------------------------------------------------
                                                   
    -------------------------------------------------------------------------------
    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.
  • Es hat siebzehn Makefiles. Warum nicht nur einer?

Demo starten


Es ist schwer herauszufinden, aber die veröffentlichte Demo kann in DosBox gestartet werden: Sie müssen sie umbenennen U2.EXEund an der richtigen Stelle ausführen.

Als ich etwas über das Innenleben des Codes erfuhr, sah es sehr logisch aus:

        CD MAIN
        MOVE U2.EXE DATA / SECOND.EXE
        CD-DATEN
        SECOND.EXE

Und voila!


Die Architektur


In den 90er Jahren wurden Demos hauptsächlich auf Disketten verteilt. Nach dem Auspacken mussten zwei große Dateien installiert werden: SECOND.EXEund REALITY.FC:

    . <DIR> 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
    README 1ST 4.222 04-10-1993 12:59
    REALITY FC 992.188 07-10-1993 12:59 
    ZWEITE EXE 1.451.093 07-10-1993 13:35
        5 Dateien 2.496.343 Bytes.
        2 Dir (s) 262,111,744 Bytes frei.

Aufgrund meiner Erfahrung in der Spieleentwicklung erwarte ich immer, dass das ganze Bild so aussieht:

  • SECOND.EXE: Engine mit allen Effekten in einer ausführbaren Datei.
  • REALITY.FC: Assets (Musik, Soundeffekte, Bilder) in einem proprietären / verschlüsselten Format a la WADDoom.

Aber nachdem ich es gelesen hatte, stellte MAIN/PACK.Cich fest, dass ich mich geirrt hatte: Die Second Reality Engine ist nur ein Loader und ein Interrupt-Server (genannt DIS). Jede Szene-Demo (auch als "PART" bezeichnet) ist eine voll funktionsfähige ausführbare DOS-Datei. Jedes Teil wird vom Loader Loader geladen und nacheinander gestartet. Teile werden am Ende verschlüsselt gespeichert SECOND.EXE:


  • REALITY.FC enthält zwei Musikkompositionen, die während der Demo gespielt werden (zum Füllen der Verschleierung hinzugefügt Füllung und Marker am Anfang).
  • SECOND.EXE enthält Bootloader und Demo Interrupt Server (DIS).
  • Nach dem Ende SECOND.EXEwerden 32 Teile (PART) der Demo als ausführbare DOS-Dateien (verschlüsselt) hinzugefügt.

Eine solche Architektur bietet viele Vorteile:

  • : PART , _start (450 ).
  • EXE SECOND.EXE -.
  • : Loader DIS 20 . DOS .
  • : PART PART .
  • / : , PART ( ), : EXE , .
  • Für die PART-Programmierung kann jede Sprache verwendet werden: Im Code finden wir C, Assembly ... und Pascal.

Literatur-Empfehlungen


Die drei Säulen zum Verständnis des Second Reality-Quellcodes sind VGA-, Assembler- und PC-Architektur (PIC- und PIT-Programmierung). Hier sind einige unglaublich nützliche Links:


Teil 2: Second Reality Engine


Wie in Teil 1 besprochen, besteht die Grundlage der zweiten Realität aus:

  • Der Bootloader als ausführbare DOS-Datei.
  • Speichermanager (einfacher Stapelpool)
  • Demo Interrupt Server (DIS).

In diesem Teil werde ich Programmierern Empfehlungen geben, die die Engine und den Bootloader lesen möchten (DIS wird im nächsten Teil besprochen).

Motorcode


Der Engine-Code ist 100% ASM, aber er ist sehr gut geschrieben und ziemlich gut dokumentiert:


Im Pseudocode kann es so geschrieben werden:

    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

Alle Schritte sind ziemlich einfach zu lesen:

  1. Stellen Sie den DIS-Interrupt-Server als Interrupt ein 0fch.
  2. Ersetzen von DOS-Systemaufrufen durch Unterbrechung 021h(weitere Einzelheiten finden Sie im Abschnitt "Dev- und Prod-Modi" ).
  3. Laden Sie Musik über den EMS-Speicher auf eine Soundkarte herunter.
  4. Laufende Musik.
  5. Durchführen jedes Teils der Demo.
  6. Erledigt!

Einzelheiten der Verfahren 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.

Speichermanager


Es gab viele Legenden, dass Second Reality einen komplexen Speichermanager über MMU verwendet, es gab keine Spuren in der Engine. Die Speicherverwaltung wird tatsächlich an DOS übertragen: Die Engine gibt zunächst den gesamten Arbeitsspeicher frei und verteilt ihn dann auf Anfrage . Der einzige schwierige Trick ist die Möglichkeit, RAM vom Ende des Heaps zuzuweisen: Er wird mit dem malloc-DOS-Rückgabewert ausgeführt, wenn zu viel RAM angefordert wird .

Teil 3: DIS


Demo Interrupt Server (DIS) bietet eine breite Palette von Diensten für jedes PART: vom Datenaustausch zwischen verschiedenen PART bis zur Synchronisation mit VGA.

DIS-Dienste


Zur Laufzeit, PART, stellt der DIS-Server Dienste für ihn bereit. Eine Liste der Funktionen finden Sie unter DIS/DIS.H.

Die wichtigsten Dienstleistungen:

  • Austausch zwischen verschiedenen PART ( dis_msgarea): DIS stellt drei Puffer mit jeweils 64 Byte bereit, damit PART Parameter vom Loader des vorherigen PART empfangen kann.
  • Emulation Copper ( dis_setcopper): Amiga Copper-Simulator, mit dem Sie Vorgänge ausführen können, die vom Status des VGA umgeschaltet werden.
  • Dev / Prod ( dis_indemo) -Modus : Ermöglicht dem PART zu erkennen, dass es im DEV-Modus ausgeführt wird (was bedeutet, dass das Video initialisiert werden muss) oder vom Bootloader im PROD-Modus gestartet wird.
  • VGA Frame Count ( _dis_getmframe)
  • Warten auf VGA-Rücklaufsperre ( dis_waitb).

Demo Interrupt Server Code


Der DIS-Quellcode ist ebenfalls 100% ASM ... und ziemlich gut kommentiert:


Wie es funktioniert


DIS ist als Interrupt-Handler für programmatic int eingestellt 0fch. Das Tolle daran ist, dass es intern ausgeführt werden kann, SECOND.EXEwenn die Demo ausgeführt wird, oder als residentes Programm ( TSR ) im Dev-Modus. Diese Flexibilität ermöglicht es Ihnen, verschiedene PART-Demos während der Entwicklung individuell zu testen:

                          // Stellen wir uns vor, wir sind ein FC-Entwickler und möchten den STAR-Teil direkt starten.
  C: \> CD DDSTARS            
  C: \ DDSTARS> K.

  FEHLER: DIS nicht geladen. 

                          // Ups, der PART konnte die DIS bei int 0fch nicht finden.
  C: \ DDSTARS> CD .. \ DIS
  C: \ DIS> DIS

  Demo Int Server (DIS) V1.0 Copyright (C) 1993 Die zukünftige Crew
  BETA VERSION - Zusammengestellt: 26.07.93 03:15:53 
  Installiert (int fc).
  HINWEIS: Dieser DIS-Server unterstützt keine Kupfer- oder Musiksynchronisation!
                          // DIS ist installiert, versuchen wir es erneut.

  C: \ DIS> CD ../DDSTARS
  C: \ DDSTARS> K.

Und voila!


Kupfer



"Copper" ist der Coprozessor, den die Entwickler der Demo für Amiga geliebt haben. Es war Teil des Original-Chipsatzes und ermöglichte es Ihnen, einen programmierbaren Strom von Befehlen auszuführen, die mit Videogeräten synchronisiert sind. Es gab keinen solchen Coprozessor auf dem PC und Future Crew musste einen Kupfersimulator schreiben, der in DIS lief.

Das FC-Team verwendete die PC-Hardware-Chipsätze 8254-PIT und 8259-PIC, um Kupfer zu simulieren. Sie schuf ein mit der VGA-Frequenz synchronisiertes System, mit dem Prozeduren an drei Stellen des vertikalen Rückwärtsstrahls gestartet werden können :

  • Platz 0: nach dem Einschalten des Displays (ungefähr in der Scanzeile 25)
  • Platz 1: unmittelbar nach dem Rückstrahldurchlauf (ES IST MÖGLICH, ES ZU VERMEIDEN)
  • Platzieren Sie 2: im Rückstrahl des Scanstrahls

Wie dies gemacht wird, kann eingelesen werden MAIN/COPPER.ASM(und siehe Abbildung unten):

  1. Der Timer des 8254-Chips ist so konfiguriert, dass er IRQ0 mit der gewünschten Frequenz auslöst.
  2. Der 8-Stunden-Interrupt-Handler (der vom 8259-PIC nach dem Empfang von IRQ0 aufgerufen wird) wird hier durch eine Prozedur ersetzt intti8.

Hinweis: Der DIS-Frame-Count-Service wird tatsächlich vom Kupfersimulator bereitgestellt.

Teil 4: Dev- und Prod-Modi


Wenn Sie den Quellcode von Second Reality lesen, werden Sie am meisten beeindruckt sein, wie viel Aufmerksamkeit das Team dem nahtlosen Wechsel von DEV zu PROD geschenkt hat.

Entwicklungsmodus



Im Entwicklungsmodus war jede Komponente der Demo eine separate ausführbare Datei.

  • DIS wurde in den residenten TSR geladen und über einen Interrupt aufgerufen 0cfh.
  • Der Bootloader verursachte, dass ein DOS-Interrupt 21hDateien öffnete, las, suchte und schloss.

Diese DEV-Konfiguration bietet die folgenden Vorteile:

  • Jeder Codierer und Künstler kann an der ausführbaren Datei arbeiten und sie separat testen, ohne den Rest des Teams zu beeinträchtigen.
  • Die vollständige Demo kann jederzeit mit einer kleinen Demo getestet werden SECOND.EXE(ohne alle EXE-Dateien am Ende hinzuzufügen). Die ausführbare Datei jedes PART wurde mit einem DOS-Interrupt 021haus einer separaten Datei geladen .

Produktion (Demo-Modus)



Im Produktionsmodus wurden Small SECOND.EXE(mit dem Bootloader), DIS und Teile der Demo als separate EXE zu einer dicken zusammengefasst SECOND.EXE.

  • Der Zugriff auf DIS erfolgte weiterhin über Interrupt 0fch.
  • Die DOS 21h-Interrupt-API wurde durch eigene Future Crew-Routinen gepatcht, die Dateien am Ende einer großen Datei öffnen SECOND.EXE.

Diese PROD-Konfiguration hat einen Vorteil in Bezug auf Ladezeit und Schutz gegen Reverse Engineering. Vor allem aber ändert sich beim Wechseln von DEV zu PROD NICHTS aus Sicht der Programmierung oder des Ladens von PART.

Teil 5: Separates TEIL


Jeder der visuellen Effekte von Second Reality ist eine voll funktionsfähige ausführbare DOS-Datei. Sie heißen PART und alle 23. Eine solche Architekturlösung ermöglichte Rapid Prototyping, parallele Entwicklung (da FC höchstwahrscheinlich keine Tools zur Versionskontrolle hatte) und freie Wahl der Sprachen (ASM, C und sogar Pascal sind in der Quelle enthalten).

Separates TEIL


Eine Liste aller PART / EXEs finden Sie im Engine-Quellcode: U2.ASM . Hier ist eine bequemere Kurzbeschreibung aller 23 Teile (mit der Position des Quellcodes, obwohl die Namen sehr verwirrend sein können):

TitelAusführbare DateiCodiererBildschirmfotoQuelle
STARTMUS.EXEMAIN / STARTMUS.C
START.EXEWILDFIRESTART / MAIN.c
Versteckter TeilDDSTARS.EXEWILDFIREDDSTARS / STARS.ASM
Alkutekstit iALKU.EXEWILDFIREALKU / MAIN.C
Alkutekstit IIU2A.EXEPSIVISU / C / CPLAY.C
Alkutekstit IIIPAM.EXETRUG / WILDFIREPAM /
BEGLOGO.EXEBEG / BEG.C.
GlenzGLENZ.EXEPSIGLENZ /
DottitunneliTUNNELI.EXETRUGTUNNELI / TUN10.PAS
TechnoTECHNO.EXEPSITECHNO / KOEA.ASM
PanikfakePANICEND.EXEPSIPanik
Vuori-SchriftrolleMNTSCRL.EXEFOREST / READ2.PAS
WüstentraumsterneDDSTARS.EXETRUG
LinsePSI
RotazoomerLNS & ZOOM.EXEPSILENS /
PlasmaWILDFIRE
PlasmacubePLZPART.EXEWILDFIREPLZPART /
MiniVectorBallsMINVBALL.EXEPSIDOTS /
PeilipalloscrollRAYSCRL.EXETRUGWASSER / DEMO.PAS
3D Sinusfeld3DSINFLD.EXEPSICOMAN / DOLOOP.C
JellypicJPLOGO.EXEPSIJPLOGO / JP.C.
Vektor Teil II 'U2E.EXEPSIVISU / C / CPLAY.C
Bildunterschriften / Danksagungen
ENDLOGO.EXEEND / END.C
CRED.EXEWILDFIRECREDITS / MAIN.C
ENDSCRL.EXEENDSCRL / MAIN.C

Es scheint, dass jeder Entwickler seine eigene Spezialisierung hatte, die in einem Teil geteilt werden konnte. Dies macht sich besonders in der ersten Szene mit Scrollen, Schiffen und Explosionen (Alkutekstit) bemerkbar. Obwohl dies wie ein kontinuierlicher Effekt aussieht, handelt es sich tatsächlich um drei ausführbare Dateien, die von drei verschiedenen Personen geschrieben wurden:

Alkutekstit (Credits) Sequenz
ALKU von WILDFIREU2A von PSIPAM von TRUG / WILDFIRE

Vermögenswerte


Image Assets ( .LBM) werden mit Deluxe Paint generiert , einem in den 90er Jahren äußerst beliebten Bitmap-Editor. Interessanterweise werden sie in ein Array von Bytes konvertiert und in PART kompiliert. Infolgedessen lädt die exe-Datei auch alle Assets herunter. Darüber hinaus erschwert dies das Reverse Engineering.

Zu den coolen Asset-Sets gehören die berühmte CITY und SHIP aus der neuesten 3D-Szene:



Interne Einheit TEIL


Da sie alle in ausführbaren DOS-Dateien kompiliert wurden, konnte in PART jede Sprache verwendet werden:


Was die Speichernutzung angeht, habe ich auf Wikipedia und anderen Websites viel über MMU gelesen ... aber tatsächlich konnte jeder Teil alles verwenden, da er nach der Ausführung vollständig aus dem Speicher entladen wurde.

Bei der Arbeit mit VGA verwendete jeder Teil seine eigenen Tricks und arbeitete an seiner Auflösung. In allen von ihnen wurde nicht Mode 13h und nicht ModeX verwendet, sondern ein modifizierter Modus 13h-Modus mit eigener Auflösung. In der SCRIPT- Datei werden häufig 320 x 200 und 320 x 400 erwähnt.

Leider wird das Lesen des Quellcodes bei der Analyse von PART zu einer entmutigenden Aufgabe: Die Qualität des Codes und der Kommentare nimmt dramatisch ab. Vielleicht geschah dies aufgrund der Eile oder weil jedes PART an seinem eigenen Entwickler arbeitete (dh es gab keinen "echten" Bedarf an Kommentaren oder Code-Verständlichkeit), aber das Ergebnis war etwas völlig Verwirrendes:


Hoch entwickelte Algorithmen sind nicht nur schwierig , auch die Namen von Variablen zu verstehen ( a, b, co[]...). Der Code wäre viel besser lesbar, wenn die Entwickler uns Hinweise in den Versionshinweisen hinterlassen würden. Infolgedessen habe ich nicht viel Zeit darauf verwendet, jeden Teil zu studieren. Die Ausnahme war die 3D-Engine, die für U2A.EXE und U2E.EXE verantwortlich war.

3D-Engine Second Reality




Wie auch immer, ich habe mich entschlossen, die 3D-Engine, die in zwei Teilen verwendet wurde, im Detail zu studieren: U2A.EXEund U2E.EXE.

Der Quellcode ist C mit Assembler-optimierten Prozeduren (insbesondere Gouro-Füllung und -Schattierung):



Die Architektur dieser Komponenten ist bemerkenswert: Die Bibliothek VISUführt alle komplexen Aufgaben aus, z. B. das Laden von Assets: 3DS-Objekte, Materialien und Flüsse (Kamera- und Schiffsbewegungen).

Die Engine sortiert die Objekte, die gezeichnet werden müssen, und rendert sie mithilfe des Algorithmus des Künstlers. Dies führt zu einer großen Anzahl von Neuzeichnungen, aber da Sie mit VGA-Latches 4 Pixel gleichzeitig aufnehmen können, ist dies nicht so schlimm.

Eine interessante Tatsache: Der Motor führt Transformationen auf eine Art und Weise der „alten Schule“ durch: Anstatt übliche homogene 4x4-Matrizen zu verwenden, werden 3 * 3-Rotationsmatrizen und ein Verschiebungsvektor verwendet.

Hier ist eine Zusammenfassung im Pseudocode:

      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);
     }

Ports für moderne Systeme


Nach der Veröffentlichung dieses Artikels begannen viele Entwickler, Second Reality auf moderne Systeme zu portieren. Claudio Matsuoka machte sich daran, sr-port zu erstellen , einen C-Port für Linux und OpenGL ES 2.0, der bisher ziemlich beeindruckend aussieht. Nick Kovacs hat bei PART PLZ großartige Arbeit geleistet und es nach C (jetzt ist es Teil des Quellcodes von sr-port) sowie nach Javascript portiert :


All Articles