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
- Scene.org canonical archive: https://archive.scene.org/pub/parties/1993/theparty93/in80/xmas93.zip
- Scene.org canonical DIZ: https://archive.scene.org/pub/parties/1993/theparty93/in80/xmas93.diz
- Scene.org duplicate/archive-name variant: https://archive.scene.org/pub/parties/1993/theparty93/in80/xmas93in.zip
- Scene.org duplicate/archive-name variant DIZ: https://archive.scene.org/pub/parties/1993/theparty93/in80/xmas93in.diz
- Scene.org The Party 1993 PC intro directory: https://archive.scene.org/pub/parties/1993/theparty93/in80/
- Scene.org The Party 1993 results: https://archive.scene.org/pub/parties/1993/theparty93/results.txt
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:
- The public EXE depacks itself with a small LZ-style MZ stub.
- The restored program initializes GUS playback defaults and tries to load
great.mod. - It switches to mode 13h.
- It uploads a 256-color DAC table from internal image data.
- It draws a center-out white opening fill directly into A000h.
- It builds screen-offset and source-plane pointer tables.
- It enters a 1000-frame rotating object loop.
- 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.
- The timer ISR keeps the external MOD playing through GUS registers.
- 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.