Analysis model: gpt-5.5 xhigh
4K iNTRO by MLJ/Xenogenesis - Technical Dissection
Scope
4KR.EXE is the Xenogenesis/XNG 4 KB intro from The Gathering 1995. The TG95
results file lists the PC 4K third-place entry as Majakovskij/XNG, entry
number 8. The TG95 in4k archive's Xenogenesis entry is 4kr.zip, and its DIZ
identifies the intro as:
4K iNTRO by MLJ of Xenogenesis
From The Gathering 1995
This analysis uses the public scene.org archive, PKLITE expansion with UNP, and static disassembly of the expanded real-mode executable. The program is a compact no-sound visual intro: it generates a Mandelbrot-like table, uses it as a texture source, then runs a tweaked-VGA heightfield/voxel-style renderer with an animated plasma height map.
Public references:
- Scene.org TG95
in4karchive,4kr.zip: https://files.scene.org/get/parties/1995/thegathering95/in4k/4kr.zip - Scene.org TG95
in4kDIZ,4kr.diz: https://files.scene.org/get/parties/1995/thegathering95/in4k/4kr.diz - Scene.org TG95 results file: https://files.scene.org/get/parties/1995/thegathering95/results.txt
- Scene.org TG95 party archive index: https://archive.scene.org/pub/parties/1995/thegathering95/
- Demozoo The Gathering 1995 page: https://demozoo.org/parties/41/
Offset notation:
packed+0x...means a file offset in the original 4089-byte PKLITE executable.expanded+0x...means a file offset in the UNP-expanded executable.runtime+0x...means an offset inside the loaded MZ image, after the 512-byte MZ header. Thusexpanded+0x0200isruntime+0x0000.
This is not a source reconstruction. Names such as "height map", "texture map", and "horizon table" are inferred from memory use and renderer behavior.
Archive Identity
The archive contains the executable and DIZ:
334e5714cd509f65d85e90543c2fd583743408d06f119da93b34d9ac8b2851b7 4kr.zip
e1f8f22c96b26142ac0a4c0dc21e49f26b207f5628694a84ec6e3f28719b3792 4KR.EXE
3071fdcc0ee40773e924819714ea419cb2998133c24846043d43f3c031595cec FILE_ID.DIZ
The packed executable is a 4089-byte MZ file, just under the 4096-byte 4K limit:
packed file size: 4,089 bytes
MZ header size: 128 bytes
packed load image: 3,961 bytes
PKLITE marker: packed+0x001e
entry point: CS:IP fff0:0100
UNP expands it to:
ac5165ecf6697e0f41f94cac6f4eaa05f8b0889fe0f5a50e07bc565ee3e7047e UNP-expanded 4KR.EXE
expanded file size: 6,430 bytes
MZ header size: 512 bytes
load image size: 5,918 bytes
entry point: CS:IP 0000:0000
entry file offset: expanded+0x0200
relocations: 5
The expanded image still needs a large allocation. Its e_minalloc is
0x3001 paragraphs, because the intro uses several 64 KB work segments beyond
the loaded code image.
The visible ending text in the expanded image is:
A contribution to the 4kB intro competition of The Gathering 1995
Coded by MLJ of Xenogenesis.
Runtime Segments
The executable stores four important segment constants at runtime+0x0f66:
runtime+0x0f66 A000h VGA segment, not relocated
runtime+0x0f68 CS + 0172h main work/backbuffer segment
runtime+0x0f6a CS + 1172h height-map segment
runtime+0x0f6c CS + 2172h texture/fractal segment
runtime+0x0f6e CS + 2172h same texture segment, later loaded into GS
The last four are MZ-relocated words. In the file they appear as 0172h,
1172h, and 2172h; DOS adds the program's load segment.
The work areas are used like this:
CS+0172h row/column temporary data, horizon arrays, planar source buffer
CS+1172h live height map sampled by the voxel renderer
CS+2172h generated Mandelbrot/escape table, also used as color/texture map
A000h VGA output
The main renderer clears CS+0172h each frame, renders into it, then copies it
to VGA plane by plane.
Startup Flow
The expanded entry begins in text mode by printing the final/identity string:
expanded+0x0200 / runtime+0x0000:
mov ax,cs
mov ds,ax
mov dx,16beh
mov ah,09h
int 21h
Then it initializes memory:
es = [cs:0f6a] ; height map
eax = 80808080h
call clear64k
es = [cs:0f6c] ; texture/fractal map
eax = 01010101h
call clear64k
ds = es = cs
call 0cf3h ; build sine/cosine-like table
call 0d33h ; build per-column offset table
call 0bb6h ; generate Mandelbrot-like escape table
clear64k is:
runtime+0x02b9:
xor di,di
mov cx,4000h
rep stosd
The program then copies every other row from the generated 256x256 fractal table into the work segment:
es = [cs:0f68]
ds = [cs:0f6c]
si = 0100h
di = 0
bp = 80h
repeat 128 times:
cx = 40h
rep movsd ; 256 bytes copied
si += 0100h ; skip the next 256-byte row
So the first visual source is a vertically downsampled version of the generated fractal/escape map.
Generated Tables
Wave Table Builder
runtime+0x0cf3 builds a full signed wave table from a short embedded quarter
wave at runtime+0x163e.
The high-level shape is:
si = 163eh
di = 0fa8h
cx = 40h
rep movsw ; copy quarter-wave samples
store 7fffh
walk backwards and mirror
store negative mirrored samples
finish with copied tail
The table is later read at runtime+0x0fa8 and runtime+0x1028. The startup
texture prelude uses it to produce changing sampling increments.
Offset Table
runtime+0x0d33 writes 128 words at runtime+0x1228:
di = 1228h
ax = 1450h
cx = 40h
repeat:
[di++] = ax
ax -= 50h
cx = 40h
repeat:
[di++] = ax
ax += 50h
This gives a down-and-up sequence of row/offset values. The prelude uses it as
the BP parameter for 256 calls to the texture warp routine.
Span Jump Table
The file also contains a table of 200 runtime code addresses from roughly
runtime+0x1434 through runtime+0x15c2:
062d, 0633, 0639, 063f, 0645, ...
Those point into the unrolled vertical span writer. The heightfield renderer
indexes this table with a negative span height to jump directly into the
correct number of store pixel; advance color instructions.
The table immediately after it, at runtime+0x15c4, begins:
0000, 0001, 0002, 0003, ...
That second table is read as distance/step data by the same renderer. The data region is intentionally overloaded.
Mandelbrot-Like Table Generator
runtime+0x0bb6 generates a 256x256 escape-style table in the segment at
CS+2172h. It uses 32-bit fixed-point math in real mode.
Initial state:
es = [cs:0f6c]
[0f94] = 0
[0f8c] = fc000000h ; y / imaginary coordinate
[0f96] = 000000ffh ; row counter
[0f90] = 0 ; run start
[0f88] = fc000000h ; x / real coordinate
[0f9a] = 0 ; column counter
For each pixel it performs up to 127 iterations:
esi = x
edi = y
bp = 127
repeat:
ebx = (esi * esi) >> 25
ecx = (edi * edi) >> 25
edi = ((esi * edi) >> 24) + y0
esi = ebx - ecx + x0
if ebx + ecx >= 08000000h:
break
bp--
if bp != 0:
repeat
value = 128 - bp
The stores are run-length compressed while generating:
if value changed:
edi = row * 256 + run_start
ecx = current_x - run_start
al = old_value
rep stosb
old_value = value
run_start = current_x
This is a good 4K trade: the escape algorithm produces a rich 64 KB texture from a few dozen instructions and writes long runs cheaply.
Twisted Texture Prelude
After mode 13h is set, the intro runs a 256-step prelude before the main heightfield.
Setup:
es = A000h
ds = CS+0172h ; downsampled fractal source
[0fa6] = 0100h
cx = 0
repeat 256 times:
di = cx
bp = table_1228[(cx & 7fh) * 2]
call 0d4eh
cl++
[0fa6]--
runtime+0x0d4e is a texture warp. It uses the wave table to derive two
source-space increments and fills almost a full 320x200 mode-13h screen:
for y in 0..198:
for group in 0..79:
sample four bytes from DS using DH/high-byte address components
pack them into EAX
bswap eax
es:[di] = eax
di += 4
The inner loop uses only additions and byte fetches after setup. Four pixels
are emitted per iteration with a 386 mov dword.
The source address pattern is intentionally odd:
bx = bp
bl = dh
bx &= 7fffh
al = [bx]
...
Only pieces of the evolving coordinate are used as the texture address. That keeps the code short and gives a rotating/twisting fractal texture rather than a strict affine mapper.
VGA Mode Setup
The intro starts from BIOS mode 13h, then turns it into a planar/tweaked 256-wide working mode:
mov ax,0013h
int 10h
; wait one retrace edge
in al,03dah ...
; CRTC display start = 0
out 03d4h, 0c00h / 0d00h
; Sequencer memory mode: chain-4 off style setup
out 03c4h, 0604h
; CRTC offset / byte-panning-related setup
out 03d4h, 0014h
out 03d4h, e317h
out 03d4h, 0009h
; enable all planes for clears
out 03c4h, 0f02h
The later copy to A000h writes each VGA plane separately with sequencer map
masks 1, 2, 4, and 8.
Main Timeline
The main loop uses runtime+0x0fa6 as a frame counter:
[0fa6] = 0
[132c] = 0 ; scene flag
frame:
[0fa6]++
if [132c] == 0:
call 04b6h ; update camera/heightfield path
if [0fa6] == 00a0h:
call 0e44h ; convert Mandelbrot texture into height map
if [0fa6] == 019ah:
fill height map with 80808080h
[0f70] = 0020h ; change camera delta
if [0fa6] == 0226h:
[132c] = 1 ; switch to animated plasma height source
if [132c] == 1:
call 0e6bh ; update animated height map
if [0fa6] == 02b7h:
exit_scroll
call clear_work
call render_heightfield
call copy_planes_to_vga
goto frame
So there are three broad visual states:
- Flat/initial height map with moving camera.
- A height map derived from the Mandelbrot table.
- A height map regenerated every frame by the plasma routine.
Camera Update
runtime+0x04b6 updates the main view parameters:
ax = 0
bx = [0f70]
[0f72] -= bx ; horizontal/position term
[0f74] += 10h ; vertical/depth phase
cx = [0f72]
dx = [0f74]
[0f82] = cx
[0f84] = dx
call 0b55h ; derives another view scalar
ah += 19h
if carry:
ax = ffffh
[0f86] = ax
[0f70] starts as zero and is later changed to 0x20, which makes the camera
begin drifting. [0f82], [0f84], and [0f86] are read by the heightfield
renderer as camera/phase/projection values.
Heightfield Renderer
runtime+0x04f8 is the main renderer. It renders into the CS+0172h work
segment, not directly to VGA.
It first initializes two 256-entry arrays in the work segment:
fs = [cs:0f6a] ; height map
gs = [cs:0f6e] ; color/texture map
word horizon[256] = 7d00h
word color_cache[256] = 0000h
The horizon array lives at work offset 03aa, and the color cache at 05aa.
The exaggerated initial horizon value ensures the first visible projected
terrain samples produce spans.
The distance loop starts at runtime+0x0778 / table offset 0x78 and steps
backwards by two:
[0f78] = 0078h
while [0f78] != 0:
distance = word [15c4 + [0f78]]
...
[0f78] -= 2
For each distance layer, it renders 256 columns. The column loop starts with:
si = 01feh ; horizon/color-cache index, counts down by 2
[0f9e] = 07aah ; base offset into work framebuffer
For each column, it computes a texture coordinate from the camera, distance, and fractional phase, then samples the height map with linear interpolation:
al = fs:[bx+1] - fs:[bx]
ax = al * fractional_x
ax = (ax,dx) >> 7
ah += fs:[bx]
projected_y = [0f7a] - ((height * [0f7c]) >> 16)
clamp projected_y to 0..199
The projected y is compared to the current horizon:
old_y = horizon[x]
horizon[x] = projected_y
span = old_y - projected_y
if span >= 0:
only update color cache
else:
draw vertical span
This is classic voxel/Comanche-style hidden-surface rendering: nearer distance layers overwrite only the parts that rise above the previous horizon for that screen column.
Color Interpolation
When a span is visible, the renderer samples GS similarly to the height map:
al = gs:[bx+1] - gs:[bx]
imul ch
shl ax,1
ah += gs:[bx]
dx = ax ; new color endpoint
Then it exchanges the new color endpoint with the cached one:
xchg word [si+05aa],ax
ax = old_color - new_color
idiv span
The division gives a per-pixel color delta. The span writer then walks color in
DX, storing DH as the byte color for each vertical pixel.
Unrolled Vertical Span Writer
The hot path does not loop once per pixel. It jumps into a block of unrolled stores:
bp = span_height
bp <<= 1
jmp word cs:[bp + 15c4h]
For visible spans, span_height is negative before the shift, so this indexes
backward from runtime+0x15c4 into the table at runtime+0x1434. That table
contains addresses into the unrolled writer starting around runtime+0x062d.
Each unrolled pixel step is six bytes:
mov [di + y*256], dh
add dx, ax
The disassembly appears as:
runtime+0x062d:
mov [di+3900h],dh
add dx,ax
mov [di+3a00h],dh
add dx,ax
mov [di+3b00h],dh
add dx,ax
...
Because the working screen is 256 bytes wide, adding 0x0100 to the address
moves exactly one scanline down. Starting at the correct jump-table entry makes
the code draw the exact span height without a branch inside the pixel writer.
After the unrolled block falls through, it advances to the next column:
inc word [0f9e] ; next screen x base
si -= 2 ; previous horizon/color entry
if si >= 0:
continue column loop
else:
next distance layer
This is the most important inner loop in the intro.
Height-Map Conversion
At frame 0x00a0, runtime+0x0e44 converts the generated Mandelbrot table
into the live height map:
es = [cs:0f6a] ; destination height map
ds = [cs:0f6c] ; source fractal map
si = 0
ecx = 10000h
for each byte:
al = [si]
if al == 80h:
al = 40h
else:
al &= 1fh
es:[si] = al
si++
That compresses the rich escape table into low terrain heights, with the
special 0x80 value mapped to a higher plateau.
Animated Plasma Height Map
At frame 0x0226, scene flag [132c] becomes 1 and runtime+0x0e6b starts
regenerating the height map every frame.
The routine writes 1024 groups of 256 bytes into the height-map segment:
es = [cs:0f6a]
si = 142eh
di = 0
outer = 0400h
for outer rows:
bp = 0100h
dl,dh,cl,ch = moving phase bytes
bx = [1432]
for 256 bytes:
ax = bx + bp
al += cs:[si + dl]
al += cs:[si + dh]
al += cs:[si + cl]
al += cs:[si + ch]
ax >>= 1
es:[di++] = al
dl += 1
dh += 3
The table at runtime+0x142e is another overloaded data area. It includes
bytes from the jump-address table, which are perfectly usable as a compact
quasi-random waveform source.
After filling the map, it updates the phase velocities at 1328..132b and the
phase bytes at 142e..1431:
[1432]--
bl ^= bh
bl ^= last_output_byte
bl ^= cl
bl ^= dl
derive two small velocity pairs
sometimes increment/decrement one component
[142e] += [1329]
[142f] -= [1328]
[1430] += [132a]
[1431] -= [132b]
This gives a moving synthetic height map without storing any frame data.
Planar Copy To VGA
The renderer's work buffer is 256 bytes wide, but VGA output is planar. The copy routine writes one plane at a time by changing the Sequencer map mask:
source si = 07aah
dest di = 1f48h
rows = 200
for each row:
plane 0: map mask 01h, copy bytes [si+0] and [si+4] pairs
plane 1: map mask 02h, copy bytes [si+1] and [si+5] pairs
plane 2: map mask 04h, copy bytes [si+2] and [si+6] pairs
plane 3: map mask 08h, copy bytes [si+3] and [si+7] pairs
di += 50h
si += 0100h
Each plane copy emits 32 words per row:
cx = 20h
repeat:
al = [si + plane]
ah = [si + plane + 4]
es:[di] = ax
di += 2
si += 8
That deinterleaves the 256-byte chunky work row into four VGA planes. The visual output is 256 pixels wide in the tweaked mode, centered/positioned by the chosen CRTC setup.
Exit Scroll
At frame 0x02b7, the program leaves the main loop and scrolls the display
start through video memory:
bx = 0
repeat:
write CRTC start address high/low from BX
wait retrace
bx += 50h
until bx == 7d00h
mode 3
int 21h AH=4Ch
The repeated CRTC start-address writes produce a hardware scroll/fade-out style ending before returning to text mode.
What Each Part Does
- PKLITE wrapper: keeps a real-mode MZ program with large generated tables under the 4 KB competition limit.
- Mandelbrot generator: produces a 64 KB texture/height source from fixed point iteration.
- Wave/offset tables: supply short sine-like and address sequences used by the texture prelude and renderer.
- Texture prelude: twists the generated fractal source directly into mode 13h for 256 steps.
- Heightfield renderer: projects a 256-column terrain front-to-back, maintains per-column horizons, and draws only newly visible vertical spans.
- Unrolled span writer: removes the per-pixel branch from the hot vertical drawing path by jumping into prewritten store instructions.
- Plasma height-map generator: replaces the static Mandelbrot-derived map with a moving generated height field late in the intro.
- Planar copy: converts the 256-wide chunky work buffer into tweaked VGA planes.
The intro's strength is that its assets are mostly algorithms: one escape-time table, one overloaded waveform/jump-table area, a heightfield renderer, and a few timeline switches. That is enough to create several distinct visual states inside a 4089-byte packed executable.
Confidence Notes
High-confidence findings:
- Archive identity, hashes, PKLITE packing, UNP-expanded MZ layout, segment map, and TG95 result mapping to the Xenogenesis entry.
- The 32-bit fixed-point Mandelbrot-like generator and the 64 KB generated table.
- The tweaked VGA setup, plane-by-plane copy, and CRTC exit scroll.
- The heightfield horizon arrays, color cache, and unrolled vertical span writer.
- The frame-timeline thresholds
0x00a0,0x019a,0x0226, and0x02b7.
Lower-confidence or caveated findings:
- I describe the first visual as a "twisted texture prelude" because its memory access pattern is a compact coordinate warp over the generated fractal source; the binary does not label the effect.
- The result table uses
Majakovskij/XNGwhile the DIZ saysMLJ of Xenogenesis. The archive mapping is based on the TG95 entry number and the only Xenogenesis/XNG 4K archive in the party directory.