Demo de análisis de código Second Reality


El 23 de julio de 2013, se publicó el código fuente de la demo de Second Reality (1993). Como muchos, estaba ansioso por ver el interior de la demostración que nos ha inspirado tanto a lo largo de los años.

Esperaba ver el caos monolítico del ensamblador, pero en lugar de eso, para mi sorpresa, descubrí una arquitectura compleja que combina elegantemente varios idiomas. Nunca había visto un código así, que representa perfectamente dos aspectos integrales del desarrollo de una demostración:

  • Trabajo en equipo.
  • Ofuscación.

Como de costumbre, formé un artículo para mis notas: espero que esto ahorre a alguien unas horas y tal vez inspire a otros a leer más código fuente y convertirse en ingenieros más experimentados.

Parte 1: Introducción


Manifestación


Antes de embarcarme en el código, les daré un enlace para capturar la legendaria demo en video HD (Michael Hut). Hoy, esta es la única forma de evaluar completamente la demostración sin fallas gráficas (incluso DOSBox no puede iniciarla correctamente).


Primer contacto con código


El código fuente se publica en GitHub. Simplemente ingrese un comando git:

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

Al principio, el contenido es confuso: 32 carpetas y una misteriosa U2.EXEque no se inicia en DosBox.


La demo tenía el título de trabajo "Unreal 2" (la primera "Unreal" fue la demo anterior de Future Crew, lanzada para la primera Asamblea en 1992). Y solo durante el proceso de desarrollo el nombre fue cambiado a "Segunda Realidad". Esto explica el nombre del archivo "U2.EXE", pero no por qué el archivo no funciona ...

Si ejecuta CLOC , obtendremos métricas interesantes:

    -------------------------------------------------------------------------------
                                                   
    -------------------------------------------------------------------------------
    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.
  • Tiene diecisiete makefiles. ¿Por qué no solo uno?

Lanzar demo


Es difícil resolverlo, pero la demostración lanzada se puede iniciar en DosBox: debe cambiarle el nombre U2.EXEy ejecutarlo desde el lugar correcto.

Cuando me enteré del funcionamiento interno del código, comenzó a parecer muy lógico:

        CD PRINCIPAL
        MOVER U2.EXE DATA / SECOND.EXE
        CD DATA
        SEGUNDO.EXE

¡Y voilá!


Arquitectura


En los años 90, las demos se distribuían principalmente en disquetes. Después de desempacar, fue necesario instalar dos archivos grandes: SECOND.EXEy REALITY.FC:

    . <DIR> 08/08/2013 16:40
    .. <DIR> 01/08/2013 16:40
    FCINFO10 TXT 48,462 04-10-1993 11:48
    FILE_ID DIZ 378 10/04/1993 11:30
    README 1ST 4.222 10/04/1993 12:59
    REALITY FC 992.188 07-10-1993 12:59 
    SEGUNDO EXE 1,451,093 10/07-1993 13:35
        5 Archivos 2.496.343 Bytes.
        2 Dir (s) 262,111,744 Bytes gratis.

Según mi experiencia en el desarrollo de juegos, siempre espero que toda la imagen se vea así:

  • SECOND.EXE: motor con todos los efectos en un archivo ejecutable.
  • REALITY.FC: Activos (música, efectos de sonido, imágenes) en un formato propietario / encriptado a la WADDoom.

Pero después de leerlo, MAIN/PACK.Cdescubrí que estaba muy equivocado: el motor de Second Reality es solo un cargador y un servidor de interrupción (llamado DIS). Cada demo de escena (también llamada "PARTE") es un ejecutable de DOS completamente funcional. Cada parte es cargada por el cargador Loader y se lanza una tras otra. Las partes se almacenan en forma cifrada al final SECOND.EXE:


  • REALITY.FC contiene dos composiciones musicales que se reproducen durante la demostración (para rellenar, ofuscar, relleno y marcador al principio).
  • SECOND.EXE contiene bootloader y Demo Interrupt Server (DIS).
  • Después del final SECOND.EXE, se agregan 32 partes (PARTE) de la demostración como archivos ejecutables de DOS (encriptados).

Dicha arquitectura ofrece muchas ventajas:

  • : PART , _start (450 ).
  • EXE SECOND.EXE -.
  • : Loader DIS 20 . DOS .
  • : PART PART .
  • / : , PART ( ), : EXE , .
  • Se puede usar cualquier lenguaje para la programación de PARTES: en el código encontramos C, Assembly ... y Pascal.

Lectura recomendada


Los tres pilares para comprender el código fuente de Second Reality son VGA, ensamblador y arquitectura de PC (programación PIC y PIT). Aquí hay algunos enlaces increíblemente útiles:


Parte 2: Segundo motor de realidad


Como se discutió en la Parte 1, la base de la Segunda Realidad consiste en:

  • El gestor de arranque como un ejecutable de DOS.
  • Administrador de memoria (grupo de pila simple)
  • Servidor de interrupción de demostración (DIS).

En esta parte, daré recomendaciones a los programadores que quieran leer el motor y el gestor de arranque (DIS se discutirá en la siguiente parte).

Código del motor


El código del motor es 100% ASM, pero está muy bien escrito y bastante bien documentado:


En pseudocódigo se puede escribir así:

    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

Todos los pasos son bastante fáciles de leer:

  1. Establezca el servidor de interrupción DIS como interrupción 0fch.
  2. Reemplazo de llamadas del sistema DOS por interrupción 021h(para más detalles, consulte la sección "Modos de desarrollo y producción" ).
  3. Descargue música a una tarjeta de sonido a través de la memoria EMS.
  4. Ejecutando música.
  5. Realizar cada parte de la demostración.
  6. ¡Hecho!

Detalles de los procedimientos 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.

Administrador de memoria


Hubo muchas leyendas de que Second Reality usa un administrador de memoria complejo a través de MMU; no había rastros en el motor. La gestión de la memoria se transfiere realmente a DOS: el motor comienza liberando toda la RAM y luego distribuyéndola a pedido . El único truco complicado es la capacidad de asignar RAM desde el final del montón: se hace utilizando el valor de retorno de malloc DOS cuando se solicita demasiada RAM .

Parte 3: DIS


El servidor de interrupción de demostración (DIS) proporciona una amplia gama de servicios para cada PARTE: desde el intercambio de datos entre diferentes PARTES hasta la sincronización con VGA.

Servicios de DIS


En tiempo de ejecución, PART, el servidor DIS le proporciona servicios. Puede encontrar una lista de características en DIS/DIS.H.

Los servicios más importantes:

  • Intercambio entre diferentes PART ( dis_msgarea): DIS proporciona tres buffers de 64 bytes cada uno para que PART pueda recibir parámetros del cargador de la PART anterior.
  • Emulation Copper ( dis_setcopper): simulador de Amiga Copper que le permite realizar operaciones que cambian según el estado de VGA.
  • Modo Dev / Prod ( dis_indemo): permite que la PARTE sepa que se está ejecutando en modo DEV (lo que significa que debe inicializar el video) o que se inicia desde el gestor de arranque en modo PROD.
  • Recuento de tramas VGA ( _dis_getmframe)
  • Esperando el respaldo VGA ( dis_waitb).

Código de demostración del servidor de interrupción


El código fuente DIS también es 100% ASM ... y bastante bien comentado:

  • DIS/DIS.ASM(controlador de interrupción establecido en int 0fch).
  • DIS/DISINT.ASM (Procedimientos DIS en sí mismos).
  • Dado que Second Reality también está parcialmente escrito en C, el código tiene una interfaz para C: DIS/DIS.Hy DIS/DISC.ASM.

Cómo funciona


DIS se establece como un controlador de interrupciones para programático int 0fch. Lo mejor de esto es que puede ejecutarse internamente SECOND.EXEcuando se ejecuta la demostración o como un programa residente ( TSR ) en modo Dev. Esta flexibilidad le permite probar individualmente diferentes demos PARTE durante el desarrollo:

                          // Supongamos que somos un desarrollador de FC y queremos comenzar la parte de STAR directamente.
  C: \> CD DDSTARS            
  C: \ DDSTARS> K

  ERROR: DIS no cargado. 

                          // Vaya, la PARTE no pudo encontrar el DIS en int 0fch.
  C: \ DDSTARS> CD .. \ DIS
  C: \ DIS> DIS

  Demo Int Server (DIS) V1.0 Copyright (C) 1993 The Future Crew
  VERSIÓN BETA - Compilado: 26/07/93 03:15:53 
  Instalado (int fc).
  NOTA: ¡Este servidor DIS no admite sincronización de cobre o música!
                          // DIS está instalado, intentemos de nuevo.

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

¡Y voilá!


Cobre



"Copper" es el coprocesador que los desarrolladores de la demo de Amiga adoraron. Formaba parte del conjunto de chips original y le permitía ejecutar una secuencia programable de comandos sincronizados con equipos de video. No había tal coprocesador en la PC, y Future Crew tuvo que escribir un simulador de cobre que se ejecuta dentro de DIS.

El equipo de FC utilizó los conjuntos de chips de hardware para PC 8254-PIT y 8259-PIC para simular Copper. Creó un sistema sincronizado con la frecuencia VGA , capaz de iniciar procedimientos en tres lugares del haz vertical hacia atrás :

  • Lugar 0: después de encender la pantalla (aproximadamente en la línea de escaneo 25)
  • Lugar 1: inmediatamente después del barrido del haz inverso (ES POSIBLE EVITARLO)
  • Lugar 2: en el haz inverso del haz de exploración

Se puede leer cómo se hace esto MAIN/COPPER.ASM(y ver en el diagrama a continuación):

  1. El temporizador del chip 8254 está configurado para activar IRQ0 con la frecuencia deseada.
  2. El controlador de interrupción 8h (que se llama por el 8259 PIC después de recibir IRQ0) se reemplaza con un procedimiento aquí intti8.

Nota: El servicio de conteo de tramas DIS es realmente proporcionado por el simulador de cobre.

Parte 4: modos de desarrollo y producción


Al leer el código fuente de Second Reality, le sorprende la cantidad de atención que el equipo prestó al cambio continuo de DEV a PROD.

Modo de desarrollo



En el modo Desarrollo, cada componente de la demostración era un archivo ejecutable separado.

  • DIS se cargó en el TSR residente y se accedió a través de una interrupción 0cfh.
  • El gestor de arranque provocó una interrupción de DOS 21hpara abrir, leer, buscar y cerrar archivos.

Esta configuración DEV tiene las siguientes ventajas:

  • Cada codificador y artista podría trabajar en el archivo ejecutable y probarlo por separado, sin afectar al resto del equipo.
  • La demostración completa en cualquier momento podría probarse utilizando una pequeña SECOND.EXE(sin agregar todos los EXE al final). El ejecutable de cada PARTE se cargó usando una interrupción de DOS 021hdesde un archivo separado.

Producción (modo demo)



En el modo de producción, el pequeño SECOND.EXE(que contiene el gestor de arranque), DIS y partes de la demostración como EXE separados se combinaron en uno grueso SECOND.EXE.

  • El acceso a DIS todavía se hizo por interrupción 0fch.
  • La API de interrupción DOS 21h fue parcheada por sus propias rutinas Future Crew, que abren archivos desde el final de un archivo grande SECOND.EXE.

Esta configuración de PROD tiene una ventaja en términos de tiempo de carga y protección contra ingeniería inversa ... pero lo más importante, desde el punto de vista de la programación o carga de PART, nada cambia al cambiar de DEV a PROD.

Parte 5: PARTE separada


Cada uno de los efectos visuales de Second Reality es un ejecutable de DOS completamente funcional. Se llaman PART y todos ellos 23. Dicha solución arquitectónica permitió la creación rápida de prototipos, el desarrollo paralelo (ya que FC probablemente no tenía herramientas de control de versiones) y la libre elección de lenguajes (ASM, C e incluso Pascal se encuentran en la fuente).

Parte separada


Se puede encontrar una lista de todas las PART / EXEs en el código fuente del motor: U2.ASM . Aquí hay una breve descripción más conveniente de las 23 partes (con la ubicación del código fuente, aunque los nombres pueden ser muy confusos):

TítuloArchivo ejecutableDescifradorCaptura de pantallaFuente
STARTMUS.EXEPRINCIPAL / STARTMUS.C
START.EXEFUEGO FATUOSTART / MAIN.c
Parte ocultaDDSTARS.EXEFUEGO FATUODDSTARS / STARS.ASM
Alkutekstit iALKU.EXEFUEGO FATUOALKU / MAIN.C
Alkutekstit IIU2A.EXEPSIVISU / C / CPLAY.C
Alkutekstit IIIPAM.EXETRUG / FUEGO SALVAJEPAM /
BEGLOGO.EXEBEG / BEG.C
GlenzGLENZ.EXEPSIGLENZ /
DottitunneliTUNNELI.EXETRUGTUNNELI / TUN10.PAS
TechnoTECHNO.EXEPSITECHNO / KOEA.ASM
PanicfakePANICEND.EXEPSIPánico
Vuori-scrollMNTSCRL.EXEFOREST / READ2.PAS
Desert Dream StarsDDSTARS.EXETRUG
LentePSI
RotazoomerLNS & ZOOM.EXEPSILENTE /
PlasmaFUEGO FATUO
PlasmacubePLZPART.EXEFUEGO FATUOPLZPART /
MiniVectorBallsMINVBALL.EXEPSIPUNTOS /
PeilipalloscrollRAYSCRL.EXETRUGAGUA / DEMO.PAS
Campo sinusal 3D3DSINFLD.EXEPSICOMAN / DOLOOP.C
JellypicJPLOGO.EXEPSIJPLOGO / JP.C
Vector parte II 'U2E.EXEPSIVISU / C / CPLAY.C
Subtítulos / Agradecimientos
ENDLOGO.EXEEND / END.C
CRED.EXEFUEGO FATUOCRÉDITOS / PRINCIPAL. C
ENDSCRL.EXEENDSCRL / MAIN.C

Parece que cada desarrollador tenía su propia especialización, que podría compartirse en una parte. Esto es especialmente notable en la primera escena con desplazamiento, barcos y explosiones (Alkutekstit). Aunque esto parece un efecto continuo, de hecho son tres archivos ejecutables escritos por tres personas diferentes:

Secuencia Alkutekstit (Créditos)
ALKU por WILDFIREU2A por PSIPAM por TRUG / WILDFIRE

Bienes


Los activos de imagen ( .LBM) se generan con Deluxe Paint , un editor de mapas de bits extremadamente popular en los años 90. Curiosamente, se convierten en una matriz de bytes y se compilan dentro de PART. Como resultado de esto, el archivo exe también descarga todos los activos. Además, esto complica la ingeniería inversa.

Entre los geniales conjuntos de activos se encuentran los famosos CITY y SHIP de la última escena 3D:



Unidad interna PARTE


Como todos fueron compilados en ejecutables de DOS, en PARTE, cualquier lenguaje podría usarse:


En cuanto al uso de la memoria, leí mucho sobre MMU en Wikipedia y otros sitios web ... pero, de hecho, cada parte podría usar cualquier cosa, porque después de la ejecución se descargó completamente de la memoria.

Cuando trabajaba con VGA, cada parte usaba su propio conjunto de trucos y trabajaba en su resolución. En todos ellos, no se utilizaron Mode 13h ni ModeX, sino un modo 13h modificado con su propia resolución. El archivo SCRIPT a menudo menciona 320x200 y 320x400.

Desafortunadamente, al analizar PART, leer el código fuente se convierte en una tarea desalentadora: la calidad del código y los comentarios disminuye drásticamente. Quizás esto sucedió por apuro o porque cada PARTE trabajó en su propio desarrollador (es decir, no hubo una necesidad "real" de comentarios o comprensión del código), pero el resultado fue algo completamente confuso:


Sofisticados algoritmos no sólo son difíciles de entender incluso los nombres de las variables ( a, b, co[]...). El código sería mucho más legible si los desarrolladores nos dejaran pistas en las notas de la versión. Como resultado, no dediqué mucho tiempo a estudiar cada parte; La excepción fue el motor 3D responsable de U2A.EXE y U2E.EXE.

Motor 3D Segunda Realidad




De todos modos, decidí estudiar en detalle el motor 3D, que se utilizó en dos partes: U2A.EXEy U2E.EXE.

El código fuente es C con procedimientos optimizados por ensamblador (especialmente relleno y sombreado Gouro):

  • CITY.C (código principal).
  • VISU.C (biblioteca visu.lib).
  • AVID.ASM (video de ensamblador optimizado (limpieza, copia de pantalla, etc.)).
  • ADRAW.ASM (dibujar objetos y truncar).
  • ACALC.ASM (matrices y cálculos rápidos de sin / cos).


La arquitectura de estos componentes es bastante notable: la biblioteca VISUrealiza todas las tareas complejas, por ejemplo, carga de activos: objetos 3DS, materiales y flujos (movimientos de cámara y barco).

El motor clasifica los objetos que deben dibujarse y los procesa utilizando el algoritmo del artista. Esto lleva a una gran cantidad de redibujos, pero dado que los pestillos VGA le permiten grabar 4 píxeles al mismo tiempo, no es tan malo.

Un hecho interesante: el motor realiza transformaciones de una manera "de la vieja escuela": en lugar de utilizar matrices homogéneas comunes de 4x4, utiliza matrices de rotación 3 * 3 y un vector de desplazamiento.

Aquí hay un resumen en pseudocódigo:

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

Puertos para sistemas modernos


Después del lanzamiento de este artículo, muchos desarrolladores comenzaron a portar Second Reality a los sistemas modernos. Claudio Matsuoka se dedicó a crear sr-port , un puerto C para Linux y OpenGL ES 2.0, que hasta ahora parece bastante impresionante. Nick Kovacs hizo un gran trabajo en PART PLZ, portándolo a C (ahora es parte del código fuente sr-port), así como a javascript :


All Articles