Analysis model: gpt-5.5 xhigh
Heaven by Barti!/NoooN - Technical Dissection
Scope
This is a binary-level pass over Heaven by Barti!/NoooN, an MS-DOS 4 KB intro released at Assembly 1995. The Assembly 1995 results list it as the second-place entry in the PC 4K intro competition, between Animate by Schwartz and Crashtest by J-P/Rebels.
Useful public references:
- Assembly 1995 results: https://archive.scene.org/pub/parties/1995/assembly95/info/asm95res.txt
- Scene.org archive: https://archive.scene.org/pub/parties/1995/assembly95/in4k/nooon_he.zip
- Scene.org file description: https://archive.scene.org/pub/parties/1995/assembly95/in4k/nooon_he.diz
- Assembly 1995 4K archive directory: https://archive.scene.org/pub/parties/1995/assembly95/in4k/
The shipped text identifies the intro as a one-night 4K, coded by Barti, and says the code is optimized for size rather than speed. It also names the visible parts:
- fractal cloud landscape
- blue/yellow bounded torus with vertical rays
- 64,000-pixel tunnel
- pink/yellow torus
- realtime triple donut space-cut scene
That text is accurate. The executable is a packed 4095-byte COM that expands itself in memory, allocates a large conventional-memory work area, builds sine, height, texture, palette, and object tables at runtime, then runs a frame-counter timeline.
Archive And Variants
The examined party archive:
e1cf4d4d65fceebeb2082830a4ff4ee9f8590fa59601b26c2f123eb059f02e8e nooon_he.zip
Extracted files:
5df1b8015d3dc40a3f438882717c6397e724b38672468be9f8afb6e6d30bcdd4 HEAVEN.COM
137821af5bb349b9afc45a540b728fc02181f10ee47554bb912d77cafc699011 NONAME.COM
3d34925b9d27b9fc837dc2c6cdf45cdcecb2c6c076052e96a332cd888ae64214 FILE_ID.DIZ
5ebc5dfb24d2908814116c99164b91a252cc57c1d414115cdf116314054b18f5 HEAVEN.TXT
Both COM files are exactly 4095 bytes. HEAVEN.COM is the named version used
for this pass. NONAME.COM is very close but changes the visible credit area
and packed bytes near the tail. Because the archive text describes Heaven by
Barti/NoooN, the named executable is the useful one to dissect.
The external text says the intro needs roughly 450 KB of low memory. The code
matches that claim: after unpacking it requests 0x6000 paragraphs, i.e.
393,216 bytes, and then divides that allocation into several 64 KB-ish work
segments.
Runtime Capture
The visual pass used the same HEAVEN.COM hash listed above in DOSBox-X
2026.01.02. Timing zero is the start of dx-capture /v HEAVEN.COM.
Machine: svga_s3
Memory: 32 MB
CPU: normal core, fixed 50000 cycles
DOS memory: XMS on, EMS off
Audio: disabled; this intro has no music path
Capture: DOSBox-X MPEG-TS/H.264
Frame size: 640x400 host capture of 320x200 VGA
Duration: 108.032 s

The contact sheet follows the frame-counter timeline recovered from the code:
cloud landscape, first torus/rays object, tunnel, second torus object,
landscape/tunnel composite, and triple-donut object scene. The on-screen
HEAVEN tag is produced by the intro itself and remains visible through most
sections, which is useful when checking that these are not unrelated captured
frames.




Packing And Recovered Image
HEAVEN.COM does not start as ordinary code. The first live instructions are a
short high-memory relocator:
mov cx, 07dch
mov si, 10deh
mov di, ff7fh
mov bp, 0100h
push bp
pusha
std
push di
rep movsw ; copy most of the packed COM upward, backwards
lea si, [di+3]
cld
mov di, bp
ret ; return into the relocated tail at ff7f
The relocated tail expands the real program over the original COM area. Stopping
at the first DOS interrupt after unpack gives a normal-looking COM image with
the entry at 0x0100:
eaa0a3141697bdcf1d8f0f261d3066bc022138bc635546e5f48be62a65299b32 recovered COM area
a6daf18a6f29ab996d9ba9e30df4f6f4f113e18377a784f3db8ef89ec772f547 recovered full segment
All offsets below are offsets in that recovered COM image, using normal COM
addresses where the file is loaded at CS:0100.
Top-Level Runtime
The unpacked entry begins with DOS memory resizing, stack setup, and runtime table generation:
0x0100 DOS AH=4A, shrink/resize current block to 0x0e6d paragraphs
0x0107 SP = 0xe6c2
0x010a DS = ES = CS
0x010e call 0x06ec allocate work memory and build sine table
0x0111 int 10h mode 13h
0x0116 clear 768-byte RAM palette at 0xb430
0x0120 upload palette
0x0123 wait retrace
0x0126 DOS print title string
0x012d copy the visible text-mode/VGA area into a work buffer
0x013e build several palettes
0x016b build perspective curve
0x016e build fractal/height material
0x01b6 save old timer vector
0x01bf install timer ISR
0x01c2 enter frame-counter timeline
The timeline is driven by word [0xd730], incremented from the timer interrupt.
The main branch compares it against these cut points:
0x0320 800 frames
0x0640 1600 frames
0x0a8c 2700 frames
0x0dac 3500 frames
0x1388 5000 frames
0x1c66 7270 frames
The code does not use a general script format. It has explicit branches for the six sections, and each branch calls the small renderer components in a different combination.
The rough timeline is:
0x0000..0x031f fractal cloud landscape
0x0320..0x063f first torus/rays object
0x0640..0x0a8b tunnel
0x0a8c..0x0dab second torus object
0x0dac..0x1387 landscape plus tunnel composite
0x1388..0x1c65 triple donut / space-cut object scene
Escape is polled directly from keyboard port 0x60. Scan code 1 exits.

The runtime pacing is exactly as compact as the branch table suggests. There are no loader menus or hidden interaction states: the intro enters graphics, runs the six timed sections, restores text mode, prints the credit line, and exits.
Memory Layout
Routine 0x06ec requests 0x6000 paragraphs with DOS AH=48h. The returned
segment is stored at 0xa81a, and then several derived segment bases are built:
[0xa826] = allocation_base
[0xd742] = allocation_base + 0x2000
[0xa82a] = allocation_base + 0x3000
[0xd740] = allocation_base + 0x4000
[0xd744] = allocation_base + 0x5000
The intro treats these as scratch pages:
[0xd744] main 64 KB-ish work framebuffer copied to A000
[0xa82a] generated height / tunnel / cloud data source
[0xd740] lookup or texture work segment
[0xd742] second lookup/height segment
[0xa826] depth/space-cut buffer pair
The same allocation also holds a sine table. 0x06ec seeds a 32-bit recurrence,
then writes a 4096-entry word table at 0xb730:
; after building 4096 32-bit recurrence values
si = 0
cx = 0x1000
di = 0xb730
loop:
eax = [ds:si]
eax >>= 14
stosw
si += 4
This table is used everywhere: camera motion, object rotation, projection offsets, tunnel addressing, and the landscape camera vector.
Timer And Palette
The timer code is minimal. 0x060f saves interrupt vector 8 from physical
0000:0020. 0x05f6 installs CS:0594, then programs PIT channel 0:
cli
write interrupt vector 8 = CS:0594
out 43h, 36h
out 40h, low(04a9h)
out 40h, high(04a9h)
sti
The interrupt handler at 0x0594 accumulates 0x11eb into [0x05cc]. On
carry it advances the frame counter and several phase variables:
[0xd730] += 1 global timeline frame
[0xa816] += [0x137e]
[0xa818] += 2
[0xd73e] += 15
[0xd738] += 5
[0xd73a] += 10
[0xd73c] += 3
Then it far-jumps to the old timer vector. This keeps DOS/BIOS timing alive while giving Heaven its own faster phase counters.
Palette upload is at 0x0626:
si = 0xb430
dx = 03c8h
al = 0
out dx, al ; DAC index 0
inc dl ; 03c9h
cx = 0300h
rep outsb
Routine 0x04b4 is the palette fade/scaler. If BP is between 1 and 128, it
multiplies every channel from the source palette by BP, shifts right by 7,
writes the scaled palette to 0xb430, waits retrace, and uploads it. If the
fade value is outside the range, it simply uploads the current palette.
The palette tables themselves are generated by 0x0649. The routine takes
small control tables at 0x12df, 0x12eb, and 0x1333, and interpolates RGB
triples into the live palette buffer. For 4K, this is more efficient than
storing full 768-byte palettes.
Frame Present And Work Buffer Clear
Routine 0x052b copies the work framebuffer to mode 13h A000 and then prepares
the work buffer for the next frame.
The first half is a fast copy:
push ds
push es
cx = 0x3e30
si = 0x0140
di = 0
es = A000h
ds = [0xd744]
rep movsd ; copy 63,680 bytes to visible screen
The source begins at 0x0140, so it skips one 320-byte line. That lets some
renderers write with y clipping and still present a clean 320x199-ish page.
After the copy, it either clears the beginning of the work buffer or reloads it from another generated segment:
if [0x1351] == 0:
clear BP words at work_buffer:0000
else:
copy 0x3e80 dwords from [0xa82a] into work_buffer
That is a size-coded way to share one present routine between the landscape, tunnel, and object sections. Some parts want a blank work page. Some parts want the generated background restored each frame before objects are drawn.
Fractal Cloud Landscape
The landscape is built from two pieces:
0x0b46generates a fractal/height source.0x08b9renders it as vertical columns.
Fractal Generator
Routine 0x0b46 initializes a 64K buffer to 0xffff, seeds one corner with
100, calls a recursive subdivision routine at 0x0aa8, then converts the
high nibble of each generated value into a byte height map.
The recursive kernel at 0x0aa8 is a diamond/subdivision style function. It
looks at four neighboring samples, averages them, perturbs the result through
the pseudo-random helper at 0x0a7c, then recurses into four smaller squares.
The helper 0x0a7c is the random perturbation:
si = (0x00ab * si + 0x2bcd) mod [0x134b]
dx = si - 0xeaa9
ax = signed(dx * cl) >> 5
ax += current_average
clamp to 0xfe
store byte
The recursion stops when the square size reaches one. That gives the intro a cloud/terrain texture without storing one in the 4095-byte file.
Landscape Column Renderer
Routine 0x08b9 is the landscape draw path. It sets FS and GS to generated
lookup/height segments, interpolates camera endpoints into a small local table
at 0xa80c, and then draws vertical screen columns from back to front.
The hot loop is in 0x0988..0x09e5. Rewritten:
for sample along ray:
advance fixed-point source coordinates
height = -terrain[source] + 15
screen_y = projected_height_lookup[source_phase]
if screen_y < current_horizon:
if screen_y < 9:
screen_y = 9
source_phase = 0xfa00
count = current_horizon - screen_y
current_horizon = screen_y
color = height - (source_phase >> 11)
if color < 0:
color = 0
color <<= 4
repeat count rows:
work_buffer[y][x] = color | color
y -= 1
The actual store is word-sized:
shl al, 4
mov ah, al
mov es:[di], ax
sub di, 0140h
loop row_fill
That means it writes two adjacent pixels at a time while walking upward through the 320-byte screen. This is exactly the classic height-field/voxel-column optimization: one ray gives a stack of filled pixels, and already-covered screen rows are skipped by the moving horizon variable.
The part text's "32000 pixels" claim lines up with the code style. The renderer draws a half-resolution set of vertical columns and uses word stores to cover two pixels per step.
The cloud clip above makes the recovered landscape model visible. It does not scroll a finished picture; the same generated height/material data is sampled from a moving camera path and repainted into vertical exposure spans.
Tunnel
The tunnel uses precomputed lookup buffers, not per-pixel polar math in the frame loop.
Routine 0x0813 clears a 64K buffer, then repeatedly calls 0x0764 while
incrementing a scale value from 1 up to 0x176. The generator uses the 4096
sine table and writes a pair of map buffers through FS and GS.
The visible tunnel renderer is 0x0853, with its core pixel loop at 0x0893:
mov ebx, gs:[si] ; packed lookup pair
add ebx, ebp ; animated phase
mov al, [bx] ; sample one texture component
shr ebx, 16
mov ah, [bx] ; sample another component
add bx, dx ; scroll/phase offset
shl ax, 4
and bl, 0feh
add ax, [bx] ; color/lighting contribution
add es:[di], ax ; write two pixels additively
si += 4
di += 2
The caller runs this loop twice with CX=0x3e80:
first 0x3e80 words = 32,000 pixels
second 0x3e80 words = 32,000 pixels
total = 64,000 pixels
That is the "64000 pixels" tunnel from HEAVEN.TXT. It is not doing expensive
division or trigonometry in the hot loop. All geometry is in the lookup maps;
the frame loop is lookup, combine, word add, advance.
The use of add es:[di], ax rather than mov is intentional. The tunnel can
be composited over other generated material, and the two bytes in AX carry
two neighboring pixels.

The tunnel clip is the best example of the 64,000-pixel path. Most of the visible motion comes from phase movement through generated lookup and texture tables; the renderer itself stays small enough for a 4K budget.
Object Generator And Torus Scenes
The torus/donut sections are built by routine 0x0b88. It takes a compact
source shape at 0x13db, writes an expanded object table at 0x14b6 or
0x4abe, and varies the construction according to a mode byte at 0x80c6.
The setup values in the main timeline show the intended variants:
mode 1 first torus/rays object
mode 4 second torus object
mode 3 first object in triple scene
mode 2 second object in triple scene
0x0b88 is compact but dense. It loops over twelve source slices and repeatedly
builds pairs of transformed records. The rotation helpers at 0x0cd9 and
0x0d20 use sine-table values and scale constants at 0x147f and 0x1481.
The result is a list of vertices/edges/faces suitable for the triangle renderer.
The triple section starting at 0x035f expands two objects:
object A at 0x14b6, center-ish values 0xb4 and 0x37
object B at 0x4abe, center-ish values 0x91 and 0x55
Then routine 0x0422 draws three passes per frame by changing position, depth,
and rotation phase variables before calling the object renderer.
Triangle Setup
Routine 0x0d57 is the object draw entry. It reads object records, transforms
and projects vertices with 0x0e58, then builds triangle/edge data and calls
the rasterizer 0x0ee2.
The projection step is straightforward:
z = vertex_z + current_z_offset
if z is behind the near plane:
screen_x = 160 + (x * scale) / z
screen_y = 100 + (y * scale) / z
else:
keep unprojected or clipped coordinate
The screen-space setup also writes small per-vertex intensity/depth helpers at
0xa008. Those are later used to shade spans and decide which parts of an
object should be visible.
0x0ee2 sorts or classifies the three vertices by y, rejects triangles outside
the 200-line screen, then calls:
0x111cto compute edge deltas0x1099to compute cross-edge interpolation state0x116bto draw spans
This is where the text file's "spacecut" remark shows up in code: the renderer maintains depth/coverage buffers and tests spans at pixel level rather than just overpainting triangles in submission order.
Depth-Tested Span Inner Loop
The core span loop is at 0x1284..0x12a9, inside 0x116b.
Before it reaches the loop, 0x116b has:
DI = x position inside current row
DX = span length
SI = interpolated depth value
CX = color/intensity accumulator
BP = per-pixel depth increment
ES = work framebuffer segment
FS = depth/space-cut buffer segment
If DI crosses the half-buffer boundary at 0x7d00, the code switches FS
and ES upward to the second buffer half:
cmp di, 7d00h
jb same_half
sub di, 7d00h
fs += 0x0fa0
es += 0x07d0
Then the pixel loop:
cmp fs:[edi*2], si
jbe skip_pixel
mov fs:[edi*2], si
xor al, al
add bl, cl
adc al, ch
mov es:[di], al
skip_pixel:
inc di
add si, 1111h
add cx, bp
dec dx
jne pixel_loop
The depth buffer is word-sized, indexed as edi * 2. A pixel is drawn only if
the new depth is closer than the stored depth. On success, the color is derived
from a compact fixed-point accumulator: the carry from adding CL into BL
becomes part of the final color, while CH carries the coarse shade.
This is the most important 4K trick in Heaven. The object renderer is not a
simple painter's algorithm. It pays the cost of a per-pixel depth test, which
lets the torus/donut scenes self-occlude correctly. The code is size-oriented,
so the depth step is crude (SI += 0x1111 in the inner loop), but it is enough
to make overlapping rings read as solid shapes.

The first torus clip shows the renderer using the same depth-tested span core for a large close object. The shading bands are coarse, but the occlusion reads correctly because pixels only win when their word depth beats the stored value.

The final triple-donut section is the most convincing runtime proof of the object pipeline. Multiple interlocking rings overlap and rotate without collapsing into painter-order artifacts, which matches the per-pixel depth buffer described above.
Texture And Background Generator
Routine 0x1396 sits directly after the visible credit string and is called
before torus sections. It clears a generated segment to zero, then runs a long
byte-generation loop from about 0x13a4:
es = [0xa82a]
clear 0x8000 words
di = 0xef98
ecx = 0x10e50
loop:
transform neighbor bytes around di
build small bias from high bits of the loop counter
add/average previous pixels
stosb
ecx--
The exact byte recipe is packed hard for size, but the structure is clear: it
uses previous pixels and nearby rows (di-0x280, di-0x140, di-0x141) to
grow a soft texture/background. The shipped text thanks earlier fire-background
work; this is the runtime generator that replaces a stored bitmap.
Runtime-To-Code Concordance
The four GIF clips now have an explicit code map. This is mostly a cross-index, because the article already has the relevant inner loops.
The cloud-landscape GIF maps to the first timeline window,
[0xd730] < 0x0320. The code-side anchors are the midpoint/fractal generator
at 0x0b46, the recursive subdivision kernel at 0x0aa8, the pseudo-random
perturbation helper at 0x0a7c, and the vertical-column renderer at 0x08b9.
The clip proves moving camera resampling of generated terrain/cloud material,
not a stored background pan.
The tunnel/vortex GIF maps to the 0x0640..0x0a8b timeline window and the
64,000-pixel tunnel path. The lookup generator at 0x0813 and helper 0x0764
build the tunnel maps; the hot loop at 0x0893 samples packed lookup pairs,
adds phase offsets, combines two texture bytes and a lighting contribution, and
adds two pixels at a time into the work buffer. The GIF is the temporal proof
that this is phase motion through generated maps, not per-frame polar math.
The first-torus GIF maps to the object generator/rasterizer chain:
0x0b88 expands the compact source shape, 0x0d57 transforms and projects
object records, and 0x0ee2/0x116b set up and draw triangle spans. The
important visible property is self-occlusion, which matches the word depth
buffer test in the span loop at 0x1284..0x12a9.
The triple-donut GIF maps to the final timeline window,
0x1388..0x1c65. That section reuses the same object generator and
depth-tested span core but draws multiple passes from 0x0422 with different
position, depth, and rotation phase values. The clip is the strongest visual
proof that Heaven spends bytes on a real space-cut/depth path rather than a
painter-order object hack.
All four clips share the same infrastructure: the self-relocating COM unpacker,
the 0x6000-paragraph allocation, generated sine/palette/texture tables, the
timer ISR at 0x0594 that advances [0xd730] and phase variables, and the
present routine at 0x052b that copies the work framebuffer to A000h.
That is the 4K design pattern: generators and shared kernels, not stored
visual assets.
Exit Path
When the frame counter reaches the end threshold or Escape is pressed, the code runs:
0x05ce restore old timer vector and reset PIT divisor
unmask IRQ2 path through port 0x21
int 10h mode 3
DOS free allocated block
DOS print the title and credit strings
DOS AH=4C exit
The final DOS print strings are small title and credit lines. The recovered image stores them with marker bytes around the title, so the useful point is only that the exit path restores text mode, frees the allocated block, prints the Heaven credit, and returns to DOS.
What The Code Is Doing
Heaven is impressive because the visible variety is not stored as assets. In 4095 bytes it contains:
- a self-relocating unpacker
- a memory allocator and segment layout
- a 4096-entry sine table generator
- palette interpolation and palette scaling
- a midpoint-style fractal/height generator
- a height-field landscape renderer
- a tunnel lookup generator and 64,000-pixel tunnel renderer
- an object/torus generator
- a projected triangle renderer with a depth-tested span loop
- a timer-driven timeline
The inner loops are deliberately simple once the generated tables exist. The landscape loop fills vertical runs and skips already-covered rows. The tunnel loop is two table reads plus a two-pixel additive write. The torus loop is a classic y-sorted triangle span routine with a word depth buffer. That is the right 4K design: spend bytes on generators and shared render kernels, then reuse them with different constants and palettes to make several distinct scenes.