Analysis model: gpt-5.5 xhigh
Trek, the Last Generation by Official Version - Technical Dissection
Trek, the Last Generation is a July 1991 MS-DOS demo by Official Version.
Pouet and Demozoo list it under that title, platform, release month, and group.
The Hornet 1991 index instead lists the same ov_trek.zip archive as
Official Version by The Last Generation, so older archive metadata appears to
have blurred the group/title fields.
Release year: 1991
This is one of the stranger 1991 pieces in the current early-PC pass. The
presentation wraps itself in a fake Amiga-emulator shell, but the program is
plain real-mode DOS code with a large restored MZ image. The interesting parts
are not one single core loop: it uses a mode 13h text/palette/scroller
frontend, mode 0Dh and 0Eh planar picture loaders, retrace-timed block
transitions, a timer interrupt that pokes the VGA attribute controller, and a
separate planar pixel/line library used by later drawing routines.

The image above is a recovered static-analysis map, not a runtime frame. A bounded DOSBox-X capture attempt against the original packed executable did not produce usable video, so this article does not claim exact visual timestamps. When OV-TREK gets a clean capture pass, the missing effect-time annotations should be added here.
Front-page media note: the card uses the generated recovered code-path map as a static preview. I did not make a GIF for this pass because the current local evidence contains no usable runtime frames; a clean DOSBox-X capture remains a follow-up item for this demo.
Sources
- Pouet production page: https://www.pouet.net/prod.php?which=86069
- Demozoo production page: https://demozoo.org/productions/285748/
- Hornet 1991 demo index: https://ftp.scene.org/mirrors/hornet/demos/1991/00_index.txt
- Scene.org archive: https://ftp.scene.org/mirrors/hornet/demos/1991/ov_trek.zip
- Scene.org file page: https://files.scene.org/view/mirrors/hornet/demos/1991/ov_trek.zip
Archive
The examined archive is ov_trek.zip from the Hornet mirror:
ov_trek.zip 263143 bytes
ov-trek.exe 269634 bytes 1991-07-08 01:00
Hashes:
5c473be5845ddbea80720879047bf4bdfd4986f71ab5b558fb36f9cce850f90b ov_trek.zip
eb102b54acd8a02a168838cb48c290572b5e49fd46581910c8a3808e44b19138 ov-trek.exe
13145163ac54e0a11cb37334db2beb9dbd44e9010fa2f823347c1755c1816a12 OV-TREK.EXE after UNP
The restored binary contains its own public release text saying the demo was released on Monday, 8 July 1991. It also contains private postal contact text; that private block is deliberately omitted from this public note.
Capture Status
The current visual status is:
DOSBox-X execution starts but did not yield a clean captured frame here
usable runtime video no
exact observed timestamps no
image in this article static recovered code-path map
The timing comments below therefore describe loop pacing and delay counts from the code, not stopwatch-verified visual timestamps.
MZ Layout And Address Convention
The distributed executable is packed:
packed file size 269634 bytes
MZ header 112 bytes
MZ reported size 269634 bytes
load image 269522 bytes
relocations 0
min alloc 3F43h paragraphs
max alloc 3F43h paragraphs
entry FFF0:0100
stack 41D5:0400
relocation table 0050h
UNP restores a much larger MZ:
unpacked file size 515520 bytes
MZ header 992 bytes = 03E0h
MZ reported size 515520 bytes
load image 514528 bytes
relocations 241
min alloc 0373h paragraphs
max alloc 0373h paragraphs
entry 0000:0490
entry raw 0870h
stack 7F1A:1F70
relocation table 001Ch
Offsets below are raw offsets in the restored executable. Runtime memory offset is therefore:
runtime offset = raw offset - 03E0h
For example, raw 1604h is runtime offset 1224h, and the restored entry raw
0870h is runtime 0490h.
Overall Structure
The useful visible code is split into four families:
03E0h..080Fh mode 13h frontend, palette upload, text/scroller loop
10B0h..11CBh retrace-paced 20x20 block copy/clear transitions
1503h..1A50h higher-level show sequence, planar picture loaders, text ending
1A60h..1D0Fh planar pixel read/write and line drawing primitives
That matters because a shallow reading makes the demo look like a generic
chunky mode-13 program. It is not. The first text/scroller section is mode
13h, but the later images and primitive drawing use EGA/VGA planar modes and
the sequencer/graphics-controller registers directly.
Mode Set Helpers
Raw 03E0h and 03E6h are trivial BIOS helpers:
03E0: mov ax,0013h
03E3: int 10h
03E5: ret
03E6: mov ax,0003h
03E9: int 10h
03EB: ret
The more important helper starts at raw 03ECh. It is a retrace-synchronized
DAC uploader:
input count [bp+4] -> CX
input start [bp+6] -> BX, also DAC start index
input far ptr [bp+8] -> DS:SI
The loop does this:
- Adds the start index to the far source pointer.
- Waits for vertical retrace to be clear, then set, using port
03DAhbit 3. - Disables interrupts.
- Writes the start color index to
03C8h. - For each byte in the source range, emits that byte three times to
03C9h. - Re-enables interrupts and returns with
ret 8.
So 03ECh treats a one-byte intensity stream as grayscale RGB triplets. It is
not a full 768-byte palette copy. This is why later fade code can adjust a
compact byte table and still upload RGB colors.
Palette Fades At 041Eh And 0579h
Raw 041Eh is a palette transition routine built around the 03ECh uploader.
It uses two main tables:
1A66h working intensity table
09DCh target intensity table
The first part optionally calls the surrounding TLG runtime through far calls
around segment 7B37h/7949h, then clears the fade counter at 1B6Ch. The
body repeatedly fills or mutates 1A66h, calls 03ECh for palette indices
starting at 14h, and walks bytes in 1A66h toward the target bytes in
09DCh.
The important inner behavior is byte convergence rather than arithmetic color ramps:
for each affected palette byte:
if work_byte < target_byte: work_byte++
if work_byte > target_byte: work_byte--
upload changed range through 03ECh
Raw 0579h is related but starts with a screen-data pass. It sets ES=A000h
and applies:
CX = 1900h
for DI from 0000h:
ES:[DI] &= 7Fh
That clears the high bit across 6400 bytes of display memory before continuing
with the same 1A66h/09DCh palette-table machinery. It is a visual cleanup
plus fade path, not only a palette routine.
The 0615h Text, Palette, And Strip Loop
Raw 0615h is the first really dense display kernel. It takes a font pointer
table and a text/control stream as far arguments, then loops until its stream
state says the part is done.
Initial state:
[0002h] = 0000h text-stream cursor
[0004h] = 0000h current glyph cursor
[0006h] = 00h glyph word countdown
[0007h] = 01h reload delay/state
[07F5h] = 00h termination flag
[000Ah] = 14h cadence counter
Stream Reader
When [0007h] reaches zero, the code reads a byte from the far text stream:
AL = stream[[0002h]]
if AL == 40h:
[07F5h] = 1
[0007h] = 1
[0002h]++
jump to the frame tail
else:
[0002h]++
AL -= 20h
AX = AL * 2
SI = font_pointer_table[AX]
AX = first word from glyph stream
[0004h] = SI + 2
[0006h] = AL
40h is the @ terminator/control byte. Normal characters are shifted down by
space (20h) and used as an index into a word-pointer table. That is a compact
proportional glyph system, not a BIOS-font print.
If the current glyph is already active, the loop reads the next glyph word from
the saved cursor at [0004h], decrements [0006h], and when that counter
expires sets [0007h] to 03h so a small gap elapses before the next stream
character.
320-Byte Buffer Shift
The scroller buffer begins at 1B70h. Each frame performs sixteen
single-column shifts:
SI = 1B70h
DI = 1B70h
SI++
DX = 0010h
repeat 16 times:
CX = 013Fh
rep movsb ; shift 319 bytes left by one
AL = 00h
glyph_word <<= 1
if carry:
AX = 007Ah + DX
stosb ; append generated byte
SI++
DX--
013Fh + 1 gives a 320-byte logical row. Sixteen rows are advanced in one
outer frame, with the appended byte taken from the current glyph word. The
nonzero byte value is not just FFh; it is 7Ah + row_number, giving the
bottom strip a palette-index gradient.
Palette Shuffle
After the buffer shift, raw 06CAh..06DFh runs backward over the 1A66h
palette work table:
SI = 1A66h + 77h
DI = SI
DX = 0005h
repeat 5 chunks:
CX = 0013h
AL = [SI--]
rep movsb backward
stosb AL
That rotates five 20-byte-ish chunks. It is preparing the palette/intensity table used immediately afterward.
Retrace And DAC Output
The loop then waits on port 03DAh bit 3. Once inside retrace, it writes DAC
index 14h to 03C8h and streams 100 palette entries from 1A66h + 14h.
The code uses [07F4h] as a bit-controlled channel selector:
CX = 0064h
SI = 1A66h + 14h
BL = [07F4h]
repeat CX times:
AL = *SI++
AH = 00h
for R,G,B:
rotate/test BH
sometimes swap AL/AH
out 03C9h, AL
The point is that the source is still one intensity byte per logical palette entry, but the output can put that byte into selected RGB channels while zeroing the others. This gives colored palette motion from a compact intensity table.
Immediately after that, the code writes DAC index 7Bh and emits 30h bytes
from a table at 0ADCh + [0008h] * 3. The [0008h] index increments and wraps
at 00BDh, so this is a second cyclic color strip.
Bottom-Strip Copy
The full text buffer is copied to video memory here:
ES = A000h
DI = E380h
SI = 1B70h
CX = 0A00h
rep movsw
0A00h words equals 5120 bytes, enough for a 320-byte by 16-line strip. In
mode 13h, A000:E380h is near the bottom of a 64000-byte page:
E380h decimal 58240
58240 / 320 = line 182
So this is a bottom-of-screen strip copy.
Optional Sprite Strip
Every other frame, [000Bh] is rotated and a sprite/animation strip is copied:
DS:SI = far pointer [2F9Ah] + [000Ch]
DI = CB84h
BX = 0010h rows
repeat 16 rows:
CX = 001Eh
rep movsw ; 30 words = 60 bytes
DI += 0104h ; 60 + 260 = 320-byte row stride
[000Ch] += 003Ch
if [000Ch] >= 1518h:
[000Ch] = 0
That is a 60-pixel-wide, 16-line animation copied into the same 320-byte row
geometry, with source frames spaced by 3Ch bytes per row slice.
Tiny Font Overlay
There is also a countdown at [0010h]. When it reaches zero, the code reads a
byte from 1544h + [000Eh], maps it through an 8-byte glyph table at 0D44h,
and draws an 8x8 character into the bottom area. The draw loop clears bit 7 of
each destination byte and ORs in the shifted glyph bit:
8 glyph rows:
AL = glyph_row
8 pixels:
ES:[DI] &= 7Fh
AL <<= 1
AH = carry ? 80h : 00h
ES:[DI] |= AH
DI++
DI += 0138h
The row stride is again 320 bytes: eight written pixels plus 0138h equals
0140h.
20x20 Block Copy And Clear
Raw 10B0h is a compact block copier. It treats DI=FFFFh as a null
destination; otherwise it copies a 20x20 rectangle:
if DI == FFFFh:
return
BP = 0014h ; 20 rows
repeat:
CX = 000Ah
rep movsw ; 10 words = 20 bytes
DI += 012Ch ; 20 + 300 = 320-byte stride
BP--
until BP == 0
Raw 10C6h is the same geometry but clears with zero words:
if DI == FFFFh:
return
AX = 0000h
BP = 0014h
repeat:
CX = 000Ah
rep stosw
DI += 012Ch
BP--
until BP == 0
Raw 10DEh is a vertical-retrace wait using 03DAh bit 3: wait while retrace
is set, then wait until it becomes set again. The transition routines call it
three times per step, so their block motion is deliberately slowed to a visible
cadence.
Four transition walkers reuse the same geometry:
10ECh table 0652h, count 006Eh, source 01A2h, clears oldest slot
1124h table 0734h, count 0028h, source 01A2h, clears oldest slot
115Ch table 078Ah, count 0032h, source 0012h, copies four table slots
1194h table 078Ah, count 0032h, source 01A2h, clears oldest slot
The tables contain destination offsets. Each iteration reads four consecutive
word positions through BX, copies the same 20x20 source block into several
positions, clears one old position, advances the table pointer by two bytes, and
loops. This is a table-driven tile transition rather than a freeform sprite
engine.
Keyboard/Configuration Gate At 1503h
Raw 1503h is not a graphics loop, but it controls part sequencing. It allocates
0382h bytes of stack workspace, uses TLG runtime calls to read or transform a
configuration/string buffer, and loops until either a runtime check reports a
key/event or a requested count has been reached.
The user-facing error path switches to text mode 03h and prints strings
through runtime calls if the comparison against table bytes at 07F9h fails.
In normal flow it acts as a paced gate between image parts. The top-level
sequence calls it with arguments 14h and 1Eh, after delays of 1000 and
300 runtime ticks respectively.
Mode 0Dh And 0Eh Planar Image Loaders
Raw 1604h loads an image into BIOS mode 0Dh:
1604: mov ax,000Dh
1607: int 10h
1609: push ds
160A: lds si,[2F92h]
160E: mov ax,A000h
1611: mov es,ax
It then configures the VGA graphics controller and sequencer:
GC port 03CEh:
AX = 0805h
AX = 0007h
SEQ port 03C4h:
start AX = 0102h ; map mask plane 0
The copy loop runs four plane passes:
BL = 04h
AH = 01h
repeat 4 planes:
out 03C4h, AX ; sequencer map mask
DI = 0000h
CX = 0FA0h
rep movsw ; 8000 bytes into this plane
AH <<= 1 ; 1,2,4,8
BL--
0FA0h words is 8000 bytes per plane, matching 320x200 4-plane graphics at
one bit per pixel per plane.
Raw 1646h is the same loader for BIOS mode 0Eh:
mode 0Eh
source pointer [2F96h]
copy count 1F40h words = 16000 bytes per plane
plane masks 1,2,4,8
Mode 0Eh is the 640x200 16-color mode, so each plane is 16000 bytes. Both
loaders restore the sequencer map mask to 0Fh and reset graphics-controller
registers after the copy.
Timer ISR And Attribute Controller
Raw 1688h is an interrupt handler. It is short but hardware-specific:
push AX, DX
AH = [2F78h]
read 03DAh ; reset attribute-controller flip-flop
out 03C0h, 09h ; attribute register index 09h
out 03C0h, AH ; value from [2F78h]
out 03C0h, 29h ; index 09h with display-enable bit set
[2F7A:2F7C]-- ; dword countdown
out 20h, 20h ; PIC EOI
pop DX, AX
iret
Raw 16B0h installs that handler on interrupt vector 08h:
ES = 0000h
old vector 08h saved at [2F7Eh]/[2F80h]
vector 08h offset set to 01A8h
vector 08h segment set to CS
PIT channel 0 programmed through ports 43h and 40h
Because the handler writes the attribute controller instead of the DAC, it is
not the same kind of color engine as the mode-13 0615h loop. It changes an
attribute register at timer rate while the rest of the program draws or waits.
Top-Level Show Sequence
Raw 1A18h is the clearest high-level script:
clear/runtime init through far call 7B37:027C
call 1604h ; mode 0Dh image
delay 1000 runtime ticks
call 1503h with 14h
call 1646h ; mode 0Eh image
delay 300 runtime ticks
call 1503h with 1Eh
call 1702h
call 17A4h
call 19CEh
far return
This confirms the multipart nature: picture load, delay/gate, picture load, delay/gate, then a later drawing/text path.
VRAM Scroll And Runtime Drawing At 17A4h
Raw 17A4h begins by clearing or preparing runtime state, then performs a
direct video-memory backward copy:
ES = A000h
DS = A000h
CX = 3480h
SI = 347Fh
DI = 3E7Fh
STD
GC register 05h = 01h
rep movsb
GC register 05h = 00h
The copy is backward because the source and destination overlap:
source A000:0000h..347Fh
destination A000:0A00h..3E7Fh
So this shifts/copies a large screen area down by 0A00h bytes while avoiding
overwrite. In a 320-byte row layout, 0A00h is eight lines. The graphics
controller write mode setting around the copy means this is not just a normal
RAM move; it is being done with planar VGA write behavior active.
After that, 17A4h enters a runtime-assisted drawing loop. It calls far
drawing helpers in segment 0168h, uses delays of 00FAh, polls keyboard
state through TLG runtime calls around 7AD5h, and exits on enough iterations,
space, or Esc. The visible effect is driven by the surrounding runtime library,
but the first scroll/copy block is local and hardware-facing.
Text-Mode Intermission
Raw 1932h is a centered typed-text helper. It copies a string into a local
buffer, computes a centered column from the first byte/length, positions the
cursor through the runtime, then prints one character at a time with a delay of
46h ticks between characters. At the end it waits 01F4h ticks.
Raw 19CEh uses it:
clear/runtime call
BIOS mode 03h
print string at 082Eh on row 0Ah
print string at 0880h on row 0Ch
print string at 08D2h on row 0Eh
print string at 0924h on row 10h
delay 1000 runtime ticks
return
That is the text ending/intermission path after the planar graphics sequence.
Planar Pixel Set At 1A60h
Raw 1A60h is a far-callable planar pixel writer. It takes color plus y and
x style arguments from the stack. The address math is the important part:
AX = y
DX = y
AX <<= 2 ; y * 4
AX += DX ; y * 5
AX <<= 4 ; y * 80
BX = x
CL = low 8 bits of x
BX >>= 3 ; x / 8
BX += AX ; byte offset = y*80 + x/8
Then it builds the bit mask:
CL &= 7
CL ^= 7
AH = 1 << CL
out 03CEh, AX with AL=08h ; GC bit mask
The write uses graphics-controller write mode and data-rotate setup:
out 03CEh, 0205h
out 03CEh, 0003h
dummy read ES:[BX]
AL = color byte from [BP+6]
ES:[BX] = AL
reset bit mask to FFh
reset GC register 05h and 03h
lret 6
This is the classic EGA/VGA single-pixel write pattern: select a bit inside the
byte through GC register 08h, arm the controller, do a latch read, then write
the color.
Planar Pixel Read At 1ABCh
Raw 1ABCh uses the same y*80 + x/8 and 1 << (7 - (x&7)) math, then reads
the selected pixel across four planes:
SI = byte offset
BL = 00h
GC read map select starts at plane 3
for planes 3 down to 0:
out 03CEh, AX with AL=04h
BH = ES:[SI]
BH &= bit_mask
BH = -BH ; carry/sign-like collapse to 0/FF
rol BX,1 ; collect bit into BL
AH--
It returns the four-plane pixel value in AL. This is exactly the inverse of
the planar set-pixel helper: select a plane, read the byte, test the bit, pack
the result into a nibble.
Line Drawing At 1B12h And 1D06h
Raw 1B12h is the main line primitive. It starts by programming the graphics
controller:
DX = 03CEh
AH = color from [BP+6]
AL = 00h
out DX, AX ; set/reset value
out DX, 0F01h ; enable set/reset on all planes
out DX, 0003h ; data rotate/function setup
Then it computes deltas from the endpoint arguments:
dy = y1 - y2
if dy < 0:
dy = -dy
swap endpoints
dx = x1 - x2
if dx < 0:
dx = -dx
row_step = -80
else:
row_step = +80
The function stores working values at 2F82h..2F88h, chooses one of two loop
entries depending on whether the line is shallow or steep, and uses an indirect
jump through [2F88h].
The address setup is shared with the pixel helpers:
byte_offset = y*80 + x/8
bit_mask = 1 << (7 - (x & 7))
ES = A000h
The vertical/general line loops repeatedly write GC bit masks to register 08h
and OR a byte at ES:[DI] to trigger the latched VGA write. Error terms decide
when to step to the next byte/row. The horizontal special path at
1C0Fh..1C8Bh builds start/end masks and uses rep movsb for middle byte
runs, which is much faster than plotting every pixel separately.
Raw 1D06h is a second entry into the same machinery, but it initializes the
color/control state differently before jumping into the shared body. It is best
read as an alternate line mode, not a separate renderer.
What This Demo Is Doing
OV-TREK is a hybrid:
- The fake PCAmiga text establishes the joke shell and likely hides some runtime setup.
- The early mode
13hsection uses a bottom strip, proportional glyph streams, cyclic palette tables, and optional sprite slices. - The transition routines are table-driven 20x20 block movers paced by retrace.
- The middle picture parts are real planar mode
0Dhand0Ehuploads, not chunky VGA pages. - The later drawing code has a proper planar pixel/line library with byte/bit addressing, GC bit masks, set/reset, read-map selection, and Bresenham-style stepping.
The lack of a clean capture means the current article is stronger on code identity than visual chronology. The next improvement should be a reliable video capture, then exact timestamp annotations for where the text strip, block transitions, planar stills, scroll/copy pass, and text intermission appear.