Analysis model: gpt-5.5 xhigh

X Mas 93 by Dante - Technical Dissection

Scope

X Mas 93 is Dante's 1993 MS-DOS 80K intro from The Party 1993. The party result file ranks it sixth in the PC intro competition with 144 points, between Aardvark's Copperfaked and Sympton/Symptom's Typhoid.

Release year: 1993.

This page covers the Dante package preserved as xmas93.zip. Scene.org also has xmas93in.zip in the same directory. Despite the name, the examined xmas93in.zip payload and DIZ are byte-identical to the Dante package's payload and DIZ, so I do not treat it as separate evidence for Ilusion's eighth-place X Mas 93 entry.

Technically, this is a much better intro than the original tiny public EXE suggests. After restoration, it is a 109,072-byte real-mode program with an external ProTracker module, a GUS-only sample upload path, a mode 13h visual loop, page-buffered polygon spans, palette loading, and a built-in frame-rate measurement screen.

Public References

The relevant result table excerpt is:

4   332  TimeOut       Epical
5   152  Copperfaked   Aardvark
6   144  X Mas 93      Dante
7   136  Typhoid       Sympton
8   124  X Mas 93      Ilusion
9   124  Instant       Xtacy

Both DIZ files identify the archive as XMAS 93 TRO by DANTE and claim sixth place at The Party 1993.

Examined Packages

Scene.org HTTP metadata observed for this pass:

xmas93.zip    content-length 27996  last-modified Mon, 03 Apr 2000 05:50:27 GMT
xmas93.diz    content-length   120  last-modified Mon, 03 Apr 2000 05:51:42 GMT
xmas93in.zip  content-length 27709  last-modified Mon, 03 Apr 2000 05:48:57 GMT
xmas93in.diz  content-length   120  last-modified Mon, 03 Apr 2000 05:49:10 GMT

The two ZIP files differ as ZIP containers:

7ee38582629d5d515962a73a78de9af9d3bcf65ac760695e16681bf7c393afef  xmas93.zip
a0f1fd1fa271385c8d34d784349d789f1e97366ff0af4a31a3c8a16c935a29f0  xmas93in.zip
619c81a6ef61c7babf6479eb2a3a79d002ca30401b44e25f00dbbe9ec20f5a15  xmas93.diz
619c81a6ef61c7babf6479eb2a3a79d002ca30401b44e25f00dbbe9ec20f5a15  xmas93in.diz

The extracted payloads are identical:

831d744a5efb2c5b0aabdb1823ede9da4303650b5e051b1a861267fa171be0ac  DNT-NONO.EXE
619c81a6ef61c7babf6479eb2a3a79d002ca30401b44e25f00dbbe9ec20f5a15  FILE_ID.DIZ
1a69b1aaf81974a5e7f6a6a547a039319de953693eec183681e6c1b691b92134  GREAT.MOD

cmp -l on the two ZIPs reports only a two-byte trailer difference before EOF on the shorter file, and unzip -l lists the same three payload files. The canonical xmas93.zip carries ZIP comment art; xmas93in.zip does not. That is enough to explain the wrapper-size difference.

Archive Layout

Both ZIPs contain:

DNT-NONO.EXE   15355  1993-12-28 03:56
FILE_ID.DIZ      120  1993-12-30 18:26
GREAT.MOD      28078  1993-12-23 00:11

file(1) reports:

DNT-NONO.EXE  MS-DOS executable, MZ for MS-DOS
GREAT.MOD     4-channel Protracker module sound data Title: "GREAT"
FILE_ID.DIZ   ISO-8859 text, with CRLF line terminators

The intro is split in a very 1993 way: one packed real-mode EXE plus one standard external module. There are no separate picture files, no launcher chain, and no NFO text in the package.

Packed MZ Layout

The public executable is a tiny MZ wrapper:

file size          15355 bytes
declared MZ size   15355 bytes
header size           32 bytes
load image         15323 bytes
overlay bytes          0
relocations            1
minalloc          180dh
maxalloc          ffffh
SS:SP             03c8h:0200h
CS:IP             fff0h:0100h
reloc table       001ch
relocation        0000h:0007h

The CS:IP = fff0h:0100h pair is the usual packed-entry trick. The real stub starts immediately after the 32-byte header. It first checks memory and prints Not enough memory$ via DOS function 09h if the block is too small:

0020  mov  ax,1bcdh
0023  mov  dx,03beh
0026  add  ax,0000h
0029  cmp  ax,[0002h]
002d  jb   004ah
002f  mov  ah,09h
0031  mov  dx,0118h
0034  int  21h
0036  int  20h
0038  "Not enough memory$"

On success, it copies 0x122 words of second-stage unpacker code to a temporary segment and enters it with a far return:

004a  sub  ax,0025h
004d  mov  ss,ax
004f  sub  ax,0025h
0052  mov  es,ax
0054  push ax
0055  mov  cx,0122h
0058  xor  di,di
005a  push di
005b  mov  si,0142h
005e  cld
005f  rep  movsw
0061  retf

The inner depacker is LZ-like. Its literal path is a byte copy guarded by a shifted bit buffer:

00dc  jb   00e6h
00de  movsb
00df  shr  bp,1
00e1  dec  dx
00e2  je   00d8h
00e4  jae  00deh

The match path subtracts a packed offset from the current destination pointer, temporarily makes DS equal to the already-expanded output segment, and copies from previous output:

0153  mov  bh,cs:[022dh+bx]
0158  lodsb
0159  mov  bl,al
015b  push si
015c  mov  si,di
015e  sub  si,bx
0160  push ds
0161  push es
0162  pop  ds
0163  rep  movsb
0165  pop  ds
0166  pop  si

That is why the packed file leaks readable fragments but not the real code body. The useful program appears only after unpacking.

Restored EXE

UNP 4.11 restored DNT-NONO.EXE under DOSBox-X with SDL dummy video/audio. The restored executable hash is:

327b3817c187de8d04f14752798db586876f415ad7b8288f27d0136a5facb8b1  dnt-nono-unpacked.exe

The restored header is normal:

file size          109072 bytes
declared MZ size   109072 bytes
header size           112 bytes
load image         108960 bytes
relocations            18
minalloc           0130h
maxalloc           ffffh
SS:SP              0000h:0400h
CS:IP              0060h:0000h
reloc table        001ch
entry load offset  0600h

The first instruction at load offset 0600h is just a jump:

0600  jmp  3d46h
0603  "great.mod",0
061b  "G R E E T I N G S ..."

The visible data at 0603h matters. great.mod is not a stray string; the loader later opens DS:DX = segment 0060h:0003h, which points directly to this filename.

The relocation table also explains why static linear offsets can be misleading. Several immediates that appear as small segment constants in the restored file are relocated by DOS at load time. For example, the graphics code uses segment constants such as 041dh and the music code uses far calls into segment 17feh; those are relative to the EXE load module, not absolute host file offsets.

Startup And Music Calls

The real entry starts at load offset 3d46h:

3d46  pusha
3d47  call 17fe:2485h     ; music defaults
3d4c  call 17fe:24a8h     ; GUS setup
3d51  mov  ax,4a00h       ; shrink DOS block
3d54  mov  bx,046eh
3d57  int  21h
3d59  mov  dx,0060h
3d5c  mov  ds,dx
3d5e  mov  dx,0003h       ; "great.mod"
3d61  call 17fe:2607h     ; load module
3d66  call 17fe:2385h     ; install timer player
3d6b  popa
3d6c  mov  ax,0013h
3d6f  int  10h

The audio calls point into the high restored block:

17fe:2485  -> load offset 1a465h
17fe:24a8  -> load offset 1a488h
17fe:2607  -> load offset 1a5e7h
17fe:2385  -> load offset 1a365h

At 17fe:0f2eh the default GUS base port is 0220h, and 17fe:0f32h holds the channel count 0004h. The code does not contain a Sound Blaster or AdLib fallback path in the restored body examined here.

Palette Upload

Immediately after mode 13h, the program walks a 768-byte RGB table and writes the VGA DAC one entry at a time:

3d71  push es
3d72  mov  al,00h
3d74  mov  cs:[0243h],al
3d78  mov  ax,041dh
3d7b  mov  es,ax
3d7d  mov  di,fc8ch
3d80  mov  al,es:[di]     ; red
3d83  mov  cs:[0244h],al
3d87  inc  di
3d88  mov  al,es:[di]     ; green
3d8c  mov  cs:[0245h],al
3d90  mov  al,es:[di]     ; blue
3d94  mov  cs:[0246h],al
3d98  call 3d2dh
3d9b  inc  byte cs:[0243h]
3da0  cmp  byte cs:[0243h],00h
3da6  jne  3d80h

The tiny DAC helper is:

3d2d  mov dx,03c8h
3d30  mov al,cs:[0243h]   ; DAC index
3d34  out dx,al
3d35  inc dx              ; 03c9h
3d36  mov al,cs:[0244h]
3d3a  out dx,al
3d3b  mov al,cs:[0245h]
3d3f  out dx,al
3d40  mov al,cs:[0246h]
3d44  out dx,al
3d45  ret

The palette table begins with low 6-bit VGA color values and ramps:

13e5c: 00 00 00 00 00 2a 00 2a 00 00 2a 2a ...
13ebc: 3f 00 00 3f 03 00 3f 07 00 3f 0c 00 ...
13f4c: 34 3f 17 31 3f 10 2d 3f 08 28 3f 00 ...

Opening Fill

After setting ES=A000h, the intro draws a growing white shape directly into mode 13h memory. The center start is 7da0h, which is screen offset 32160, or approximately x=160, y=100:

3dae  mov  cs:[02d5h],7da0h
3db5  mov  ax,ffffh
3dbb  mov  cs:[2178h],0001h
3dc7  xor  dx,dx
3dca  mov  di,cs:[02d5h]
3dcf  mov  bx,cs:[2178h]
3dd4  add  bx,bx
3dd6  cmp  bx,00c8h
3ddf  mov  bx,00c8h       ; cap row count at 200
3de2  mov  cx,cs:[2178h]
3de7  rep  stosw          ; draw one row of white words
3de9  sub  di,cs:[2178h]
3dee  sub  di,cs:[2178h]
3df3  add  di,0140h       ; next scanline, 320 bytes
3df7  inc  dx
3df8  cmp  dx,bx
3dfa  jb   3de2h
3dfc  inc  word cs:[2178h]
3e01  sub  word cs:[02d5h],0141h
3e14  cmp  word cs:[2178h],00a0h
3e1b  jb   3dc7h

The moving start offset and increasing rep stosw count make the fill expand from the center instead of acting like a plain screen clear. The step 0141h means "one scanline plus one byte", so every wider pass shifts the left edge up and left.

Visual Tables

The program then prepares four far pointers used by the renderer:

341b  dx=041dh cx=3f2ch  -> stores segment/offset at 081e/0826
3437  dx=041dh cx=7e4ch  -> stores segment/offset at 0820/0828
3453  dx=1415h cx=000ch  -> stores segment/offset at 0822/082a
346f  dx=041dh cx=bd6ch  -> stores segment/offset at 0824/082c

Those point at large embedded data blocks in the restored image. The first and last are texture/palette-like byte planes; the segment/offset conversion avoids 64K wrap issues when the renderer switches DS to a source plane.

The y-offset table is generated at 33dfh:

33df  xor ax,ax
33e2  mov bx,0140h
33e5  xor si,si
33e8  mov cx,00c8h
33eb  mov cs:[si+0335h],ax
33f0  add si,2
33f3  add ax,bx
33f5  loop 33ebh

That creates 200 words: 0, 320, 640, .... Later code indexes this table with screen y coordinates to get byte offsets into a 320-wide mode 13h page.

Main 3D Loop

The main loop starts after the opening fill:

3e24  call 341bh          ; prepare far source pointers
3e27  call 33dfh          ; build y*320 table
3e2a  mov  ax,cs:[21c2h]
3e2e  add  cs:[21bah],ax
3e33  cmp  word cs:[21bah],0168h
3e3f  sub  word cs:[21bah],0168h
3e46  mov  ax,0002h
3e49  add  cs:[21bch],ax
3e4e  cmp  word cs:[21bch],0168h
3e5a  sub  word cs:[21bch],0168h

0168h is 360 decimal, so 21bah and 21bch are angle indices modulo 360. The code reads two lookup tables at 2250h and 2304h; the pairs are used as rotation sine/cosine-like values:

3e81  mov  bx,cs:[21bch]
3e86  shl  bx,1
3e88  mov  ax,cs:[bx+2250h]
3e8d  mov  cs:[2244h],ax
3e91  mov  ax,cs:[bx+2304h]
3e96  mov  cs:[2248h],ax
3e9a  mov  bx,cs:[21bah]
3e9f  shl  bx,1
3ea1  mov  ax,cs:[bx+2250h]
3ea6  mov  cs:[2242h],ax
3eaa  mov  ax,cs:[bx+2304h]
3eaf  mov  cs:[2246h],ax

The object has ten source vertices. Each vertex is transformed through a pair of 2D rotations plus a perspective divide:

3eb3  mov  cx,000ah
3eb6  mov  word cs:[223eh],0000h
3ebd  mov  bx,cs:[223eh]
3ec2  mov  ax,cs:[bx+2178h]   ; x
3ec7  imul word cs:[2246h]
3ecc  mov  cs:[21b4h],ax
3ed0  mov  ax,cs:[bx+218ch]   ; y
3ed5  imul word cs:[2242h]
3eda  sub  cs:[21b4h],ax
3edf  mov  ax,cs:[bx+2178h]
3ee4  imul word cs:[2242h]
3ee9  mov  cs:[21b6h],ax
3eed  mov  ax,cs:[bx+218ch]
3ef2  imul word cs:[2246h]
3ef7  add  cs:[21b6h],ax

The projection step divides by z + 1400h and adds a screen-center constant:

3f40  xor  dx,dx
3f42  mov  ax,cs:[21b4h]
3f46  imul word cs:[21c0h]
3f4b  mov  bx,cs:[21b8h]
3f50  add  bx,1400h
3f54  idiv bx
3f5b  add  ax,cs:[0814h]
3f60  mov  cs:[bx+21c6h],ax   ; projected x
...
3f67  mov  ax,cs:[21b6h]
3f6b  imul word cs:[21beh]
3f70  mov  bx,cs:[21b8h]
3f75  add  bx,1400h
3f79  idiv bx
3f80  mov  cs:[bx+21dch],ax   ; projected y
3f85  mov  ax,cs:[0816h]
3f89  sub  ax,cs:[bx+21dch]

0814h is set to 00a0h and 0816h to 0064h in the loop, so the screen center is again x=160, y=100.

Face Dispatch

The face list starts at 21f2h. Each record begins with a type word. Type 2 uses the flat span filler at 2ea6h; type 3 uses the textured or sampled quadrilateral path at 348ch:

3fb8  mov  cx,cs:[21f2h]
3fbd  mov  ax,cs:[bx+21f2h]
3fc5  cmp  ax,0002h
3fc8  je   3fd5h
3fcd  cmp  ax,0003h
3fd0  je   4050h

For a type-2 face, it copies four projected vertex pairs into the scratch slots at 02dah..0308h, stores a color byte at 032ah, and calls the filler:

3fd5  mov si,cs:[bx+21f2h]
3fda  mov ax,cs:[si+21dch]  ; y0
3fdf  mov cs:[0302h],ax
3fe3  mov ax,cs:[si+21c6h]  ; x0
3fe8  mov cs:[02dah],ax
...
4031  mov ax,cs:[bx+21fah]
4036  mov cs:[032ah],al
403f  call 2ea6h

For a type-3 face, it fills the 082e..083c scratch area and passes a source selector through 224ch before calling 348ch:

4050  mov si,cs:[bx+21f2h]
405a  mov cs:[0838h],ax
4063  mov cs:[0830h],ax
...
40ac  mov ax,cs:[bx+21fah]
40b1  mov cs:[224ch],ax
40ba  call 348ch

Flat Quad Filler

The flat filler at 2ea6h first finds the vertical bounds, clamps to y=0..199, and sorts vertices so scan conversion can walk two edges at a time. The important inner span write is at 33bah:

33ac  xor  bx,bx
33af  mov  cx,cs:[032fh]       ; span count
33b4  mov  al,cs:[032ah]       ; color
33b8  mov  ah,al
33ba  push cx
33bb  mov  di,cs:[bx+04d5h]   ; left screen offset
33c0  mov  cx,cs:[bx+04d7h]   ; right-left length
33c5  cmp  cx,0000h
33c8  jl   33dbh
33cd  shr  cx,1
33cf  jae  33d5h
33d4  stosb                   ; odd pixel
33d5  inc  cx
33d6  add  bx,0004h
33d9  rep  stosw              ; packed two-pixel fill
33db  pop  cx
33dc  loop 33bah
33de  ret

This is a classic mode 13h span loop: compute all edge intersections into tables, then emit one byte if needed and word-fill the remaining even part.

Textured/Sampled Quad Filler

The type-3 path is more expensive. It picks one of the prepared source planes, builds edge stepping arrays, and copies sampled words into the offscreen page. The source pointer selection is:

348c  push ds
348d  mov  es,cs:[081ch]      ; destination page
3492  mov  bx,cs:[224ch]      ; source selector
3497  mov  ds,cs:[bx+081eh]
349c  mov  ax,cs:[bx+0826h]

The line builder stores destination offsets, texture x, and texture y in three parallel arrays:

354a  mov cs:[si+1bceh],di    ; destination offset for this step
354f  mov cs:[si+0c2eh],ax    ; texture x
3554  mov cs:[si+13feh],bx    ; texture y
3559  add dx,cs:[1fc0h]
3568  add di,cs:[04cdh]
356d  add ax,cs:[04cdh]
3577  add bx,cs:[1fbah]
357c  add di,cs:[1fb8h]       ; next scanline, +/-320
3581  add si,0002h
3584  loop 354ah

The final sample/copy loop has two variants depending on which axis is the major step. Both use movs word from the selected source plane to the destination page, with accumulated carries nudging the source and destination pointers:

3880  movsw
3881  add  ah,al
3883  adc  si,cs:[2176h]
3888  add  dx,bp
388a  adc  di,bx
388c  loop 3880h

The reverse-direction version changes only the destination carry:

3893  movsw
3894  add  ah,al
3896  adc  si,cs:[2176h]
389b  add  dx,bp
389d  sbb  di,bx
389f  loop 3893h

The second half at 39xxh repeats the same logic for the other edge ordering. This is not a general affine mapper in a modern sense; it is a compact span/edge sampler specialized for the intro's small object and embedded source planes.

Page Copy And VBlank

The renderer draws into an offscreen segment stored at 081ch, then copies it to the real VGA segment stored at 02d8h:

3b0c  mov  es,cs:[02d8h]
3b11  xor  di,di
3b14  mov  ds,cs:[081ch]
3b19  xor  si,si
3b1c  mov  cx,7d00h
3b1f  rep  movsw
3b21  ret

7d00h words is 64,000 bytes, exactly one mode 13h frame.

The flip is synchronized to vertical retrace:

40cb  mov dx,03dah
40ce  in  al,dx
40cf  test al,08h
40d1  jne 40ceh       ; wait until not in retrace
40d3  mov dx,03dah
40d6  in  al,dx
40d7  test al,08h
40d9  je  40d6h       ; wait for retrace start
40db  call 3b0ch      ; copy page to A000h

The frame loop exits after 1000 frames or Escape:

4189  inc  word cs:[07fdh]
418e  cmp  word cs:[07fdh],03e8h
419a  jmp  41a9h       ; after 1000 frames
419d  in   al,60h
419f  cmp  al,01h
41a1  je   41a9h       ; Escape
41a6  jmp  3e2ah

That explains the embedded benchmark strings:

Frames done       :
Time              :
Frames per second :
That means        : Piece of shit!

The exit path switches back to text mode, prints those 20 fixed strings with DOS function 09h, waits for Escape, shuts down the GUS player, and exits via int 21h,4c00h.

GUS MOD Loader

The music block is self-contained and high in the restored image. The setup at 1a488h builds a period table and initializes the GUS registers derived from base port 0220h:

1a4be  mov bx,cs:[0f2eh]    ; 0220h
1a4c5  add bx,0103h
1a4c9  add cx,0105h
...
1a548  mov dx,cs:[0f2eh]
1a54d  add dx,0102h
1a551  mov al,cl
1a553  dec al
1a555  out dx,al            ; select voice
1a557  inc dx
1a559  out dx,al
1a55d  mov al,03h
1a55f  out dx,al

The module loader at 1a5e7h opens great.mod, parses it, allocates memory with DOS function 48h, and uploads samples through the GUS DRAM write ports:

1a95e  mov ax,3d00h         ; open file
1a963  int 21h
1a949  mov ax,3f00h         ; read file
1a956  int 21h
1a937  mov ax,4800h         ; allocate memory
1a93b  int 21h
1a93f  mov ax,4900h         ; free memory
1a944  int 21h

The loader recognizes ProTracker signatures and selects channel counts:

1a62b  compare 4-byte signature
1a640  si=03cdh  channels=4
1a68e  set stride 30h, channels=6
1a6c3  set stride 20h, channels=8

For this package, GREAT.MOD is M.K., so the four-channel path is the one that applies.

The sample-upload loop writes one byte at a time to GUS DRAM, updating the hardware memory pointer on every byte:

1a82a  mov dx,cs:[0f2eh]
1a82f  add dx,0103h
1a833  mov al,43h
1a835  out dx,al
1a836  inc dx
1a837  mov ax,bp
1a839  out dx,ax            ; low DRAM pointer
1a83b  dec dx
1a83d  mov al,44h
1a83e  out dx,al
1a841  mov al,bl
1a843  out dx,al            ; high DRAM pointer
1a847  mov al,[di]
1a849  out dx,al            ; sample byte
1a84d  inc di
1a84e  add bp,1
1a851  adc bl,0
1a854  loop 1a833h

The player installs its own timer interrupt at vector 8:

1a389  xor ax,ax
1a38c  mov es,ax
1a38e  mov bx,es:[0020h]
1a393  mov ax,es:[0022h]
1a397  mov cs:[166ch],bx
1a39c  mov cs:[166eh],ax
1a3a2  mov dx,1824h
1a3a5  mov es:[0020h],dx
1a3aa  mov es:[0022h],ds
1a3af  mov al,36h
1a3b1  out 43h,al
1a3b3  mov al,0a9h
1a3b5  out 40h,al
1a3b7  mov al,04h
1a3b9  out 40h,al

The timer ISR steps the module rows, updates GUS voice registers, and chains to the old BIOS timer every 0x37 player ticks:

19804  pusha
1980c  mov al,20h
1980e  out 20h,al
19811  add word ds:[167eh],1
19816  adc word ds:[1680h],0
1981b  inc word ds:[167ch]
...
1a2ca  dec word ds:[1682h]
1a2d3  mov word ds:[1682h],0037h
1a2d9  pop es
1a2da  pop ds
1a2db  popa
1a2dc  jmp dword ptr cs:[166ch]
1a2e4  iret

The effect decoder covers ordinary ProTracker commands by jumping through tables at 2305h and 2325h. Examples visible in the restored code include portamento, volume set/clamp, sample offset, pattern speed/tempo, and volume slide.

External Module

GREAT.MOD is a compact ProTracker module:

title             GREAT
signature         M.K.
module size       28078 bytes
song length       13 orders
patterns          11
pattern bytes     11264
real sample data  15730 bytes

The order list is:

06 06 09 0a 0a 00 02 03 04 01 05 07 08

Only seven sample slots carry real named sample data. The remaining slots use two-byte, zero-volume placeholders that are not present as physical trailing sample bytes:

01 RAGEBD.SMP     len= 3314  vol=64
02 KATTILA.SMP    len= 2146  vol=32
03 CLAP.SMP       len= 1768  vol=64
05 DS15.CFG       len=   10  vol=64  loop=2+4
07 ADPDRUM2.SMP   len= 1492  vol=64
09 BASS9804.SMP   len= 3522  vol=64
11 LOMELOOP.SMP   len= 3478  vol=64

That structure fits the intro: the EXE contains the visual program and player, while the music stays in a normal, small, tool-friendly M.K. file.

Runtime Probe

I did not capture visual timestamps for this pass. The VM shell had no X display, and the useful bounded route was SDL dummy video/audio. That is enough for unpacking and startup checks, but not enough for a trustworthy effect timeline.

The exact runtime environment for the bounded probe:

emulator: DOSBox-X 2026.01.02
video:    SDL dummy driver, surface output fallback
audio:    SDL dummy driver
method:   UNP 4.11 run in a mounted temp directory, then static analysis
timing:   no visual timestamp capture

The unpack run succeeded and expanded the executable from 15,355 bytes to 109,072 bytes. Earlier startup probes also showed DOSBox-X switching display sizing, confirming that the public package starts far enough to enter video setup. I do not treat that as a substitute for a frame-accurate timeline.

What It Does

In runtime order, the intro does this:

  1. The public EXE depacks itself with a small LZ-style MZ stub.
  2. The restored program initializes GUS playback defaults and tries to load great.mod.
  3. It switches to mode 13h.
  4. It uploads a 256-color DAC table from internal image data.
  5. It draws a center-out white opening fill directly into A000h.
  6. It builds screen-offset and source-plane pointer tables.
  7. It enters a 1000-frame rotating object loop.
  8. Each frame clears an offscreen 64,000-byte page, transforms ten vertices, dispatches flat or sampled faces, waits for vertical retrace, and copies the page to VGA memory.
  9. The timer ISR keeps the external MOD playing through GUS registers.
  10. At the end, it prints frame-count, elapsed-time, and FPS text in text mode, waits for Escape, restores the music timer/hardware state, and exits.

Why It Matters

X Mas 93 by Dante looks modest as a 28 KB ZIP, but the restored program is a compact complete intro engine: standard external module, GUS sample upload, mode 13h page buffering, direct DAC writes, scanline polygon filling, sampled source-plane spans, and frame-rate reporting.

It also clarifies the scene.org archive wrinkle. The Party 1993 result file lists two different X Mas 93 intro entries, Dante and Ilusion, but the two current scene.org archives named xmas93.zip and xmas93in.zip do not preserve two different payloads. For this corpus, Dante's sixth-place entry is the one verified by both DIZ text and executable contents.