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.EXE
und 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.EXE
und 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 WAD
Doom.
Aber nachdem ich es gelesen hatte, stellte MAIN/PACK.C
ich 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.EXE
werden 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:- Stellen Sie den DIS-Interrupt-Server als Interrupt ein
0fch
. - Ersetzen von DOS-Systemaufrufen durch Unterbrechung
021h
(weitere Einzelheiten finden Sie im Abschnitt "Dev- und Prod-Modi" ). - Laden Sie Musik über den EMS-Speicher auf eine Soundkarte herunter.
- Laufende Musik.
- Durchführen jedes Teils der Demo.
- 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.EXE
wenn 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):- Der Timer des 8254-Chips ist so konfiguriert, dass er IRQ0 mit der gewünschten Frequenz auslöst.
- 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
21h
Dateien ö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 021h
aus 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):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: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.EXE
und U2E.EXE
.Der Quellcode ist C mit Assembler-optimierten Prozeduren (insbesondere Gouro-Füllung und -Schattierung):Die Architektur dieser Komponenten ist bemerkenswert: Die Bibliothek VISU
fü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);
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);
}
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 :