Analysis model: gpt-5.5 xhigh

StarPort BBS Intro II by Future Crew - Technical Dissection

StarPort BBS Intro II is a 21 November 1993 MS-DOS VGA/AdLib BBStro by Future Crew for the group's StarPort board. It is small enough to be mistaken for a simple advertisement, but the released source shows a dense 2k-style effect: a planar VGA starfield, a rotating 3D dot-letter scroller, palette cycling, a 70 Hz timer hook, and a compact multi-channel AdLib music player.

This page covers the second StarPort intro. Future Crew's own FCINFO12.TXT lists the earlier STARPRT2.EXE as the 13 September 1992 StarPort BBS intro and STARPORT.ZIP as the 21 November 1993 StarPort BBS intro II. The 1993 source package is the useful target here because it preserves SP2.ASM, SP2.COM, and the authors' comments.

StarPort BBS Intro II runtime contact sheet

The contact sheet above is runtime evidence from the original 1993 SP2.COM. It shows the plain starfield, the first large projected dot letters, mid-run rotation/scroll states, and the late title sweep. The source-derived analysis below explains how those frames are made.

Sources

Archive

The examined source archive is FCSP2SRC.ZIP from the Metropoli mirror:

325c928245d90685a71fccecf7312e2c68a50c9f2386e998846ed12dbe1b6954  FCSP2SRC.ZIP

It contains the source, an updated Future Crew information file, and helper material, but not the final SP2.COM binary:

FCINFO12.TXT  66094 bytes
FILE_ID.DIZ      99 bytes
MAKE.BAT         86 bytes
MMZLM.WRZ       751 bytes
README          281 bytes
SLOBBS.EXE     6931 bytes
SP2.ASM       20856 bytes
UNDER.COM      1058 bytes

The SP2.COM used for runtime capture comes from the GitHub source mirror:

f999fcd7aa2e2c4b24a71164b4d3d74ee67441e58d76b353d49726fafa9dda72  SP2.COM
d27511e811f49ce41cf2d418441ad405737f774add6e9e89c451e297caa3c17d  SP2.ASM
bfefdb6fa8c9e2d5040fbe0577bb1c0dc960a39685e60c23149809d04f6d51b4  FCINFO12.TXT

The source header identifies the production and credits directly:

StarPort Intro II V1.0
Copyright (C) 1993 Future Crew
code: Psi
music: Skaven

The comments also explain the size target. The released COM was first built normally, then postprocessed by removing zero data from the end. That is why the source declares large zeroed buffers while the final file is still only 1993 bytes.

Runtime Capture

On 4 July 2026 I ran SP2.COM under DOSBox-X 2026.01.02, Linux SDL2 64-bit, with machine=svga_s3, normal CPU core, cycles=fixed 12000, dummy SDL video/audio, and capture format=mpegts-h264. Timing zero is the start of dx-capture /v SP2.COM. The captured stream is MPEG-TS H.264/AAC at 640x400, duration 24.441233 seconds. The stills below are extracted at 320x200.

Timestamp Frame Notes
00:00.500 StarPort BBS Intro II runtime 00:00.500 starfield only The intro is already in VGA mode and drawing the initial star/dot field before readable dot letters enter the camera.
00:05.000 StarPort BBS Intro II runtime 00:05.000 first projected letters The dot-letter scroller has begun entering from the right. This is the text0 stream being converted into 8x8 font dots and inserted into the rotating point list.
00:08.000 StarPort BBS Intro II runtime 00:08.000 large rotating dot letters Large letters pass close to the viewer, proving the scroller is not a flat bitmap scroll. Size changes come from the projected Z value and the pset2 dot-size branch.
00:11.000 StarPort BBS Intro II runtime 00:11.000 mid-run dot scroller The letter stream is now fragmented by perspective and rotation. The same frame loop also advances palette order and the AdLib row player.
00:18.000 StarPort BBS Intro II runtime 00:18.000 starfield between text passes A gap in readable letters leaves the starfield visible. The background points are normal pset1 dots sharing the same rotation/projection machinery.
00:23.000 StarPort BBS Intro II runtime 00:23.000 late title sweep The late run wraps back to the board-name phrase and shows the looped text source rather than a one-shot splash card.

StarPort BBS Intro II 3D dot scroller motion clip

The GIF covers the 00:05..00:13 window. It is the important evidence because the effect is temporal: the text advances in world coordinates, rotates through two axes, changes dot size with depth, and shares the field with independently seeded background dots. Still frames alone make it look closer to a static bitmap than it really is.

Program Shape

The source is a tiny COM program with code, data, scratch buffers, and zeroed state in one segment. At startup it sets DS=CS, ES=A000h, clears the zero area, probes CPU/VGA capability, builds its font and geometry tables, starts the AdLib player and timer hook, then loops until a key is pressed.

COM entry
  zero declared scratch/state area
  ES = A000h
  reject pre-386 CPUs
  reject non-VGA displays
  copy BIOS font glyphs into private font buffer
  switch to planar VGA mode 0Dh
  build row/column/mask tables
  seed dot list with random background stars
  initialize AdLib instruments
  install INT 8 timer at roughly 70 Hz
  per-frame loop: palette, copy/clear, simulate, project/draw
  restore timer, silence AdLib, print final text line

The capability checks are compact but explicit. The CPU test uses push sp / pop dx / cmp dx,sp to reject 8086/80186 behavior, then sgdt to separate 286 from 386-or-better. The VGA test calls INT 10h AX=1A00h and requires a VGA display-combination result.

VGA Mode And Plane Layout

The runtime picture is not mode 13h. The intro briefly enters mode 13h only to ask BIOS for the A..Z glyph bitmaps, then switches to BIOS mode 0Dh, the 320x200 16-color planar graphics mode.

mov ax,13h
int 10h                 ; temporary glyph-rendering mode
...
mov ax,0dh
int 10h                 ; final 320x200x16 planar VGA mode

Mode 0Dh matters because one byte in video memory represents eight horizontal pixels in one selected plane. The source precomputes tables so the frame loop does not have to recompute planar addresses and bit masks for every projected dot:

rows[screen_y]       byte offset for the scanline
cols[screen_x]       byte offset within the scanline
colb[screen_x]       one-pixel bit mask
colbv/colbw/colbww   wider masks for larger projected dots

The visible buffer is private, not direct-to-VGA. The declared vbuf is 44*200 bytes, surrounded by eight rows of overflow padding on both sides. That 44-byte row pitch is larger than the 40 bytes strictly needed for 320/8 pixels. The padding lets the wide-dot writers touch nearby rows without constant boundary work.

clearcopy copies this private buffer to A000h and clears it in one pass:

mov si,OFFSET vbuf
mov bx,4
mov cx,200
mov di,-4
...
mov eax,ds:[si]
mov ds:[si],edx
mov es:[di],eax

The loop walks four bytes at a time and copies 40 bytes per screen row to VGA. The private buffer keeps 44 bytes per row, so the copy naturally skips the extra guard bytes.

Palette And Plane Cycling

Each visible frame starts by waiting for vertical retrace through port 3DAh, then uploading a palette order through 3C8h/3C9h.

mov dx,3dah
wait display not in retrace
wait display in retrace
call setpal

The source defines five base colors from black through pale blue-white, then four palette-index tables:

col0..col4       background to brightest dot color
index1..index4   palette order plus target write plane

The ninth byte in each index record selects the VGA sequencer map mask for the frame:

mov al,2
mov ah,ds:[si+8]
mov dx,3c4h
out dx,ax

That means the copy/draw rhythm is both palette-animated and plane-aware. The comments beside the index tables name the plane order:

index1 ... ,1   ; 1248
index2 ... ,8   ; 8124
index3 ... ,4   ; 4812
index4 ... ,2   ; 2481

The visible result is subtle in stills but clear in motion: the dot field has depth and shimmer while still using only 16-color planar VGA.

Timer And Simulation

The intro hooks interrupt 8 and reprograms the PIT divisor to 17000, noted in the source as 70hz:

mov eax,fs:[8*4]
mov ds:oldint8,eax
mov ax,cs
shl eax,16
mov ax,OFFSET intti8
mov dx,17000
...
out 43h,al
out 40h,al
out 40h,al

The interrupt handler is intentionally tiny:

inc cs:framecounter
iret

The drawing loop consumes framecounter and calls doit70 once for each elapsed tick. That is why the source says this is done for frame synchronization on slower machines. Rendering can miss time, but the scroller, waves, depth, and music row advancement are still stepped by elapsed 70 Hz ticks rather than by raw host speed.

Dot Field And 3D Scroller

The full dot list has 444 entries:

DOTNUM1 = 256   text dots
DOTNUM  = 444   text dots plus background dots
dots    = x, y, z, draw-routine for each dot

Startup fills the list with random background points using a small linear congruential generator. These use pset1, the small-dot writer. Letter dots use pset2, the wide-dot writer that grows or shrinks vertically depending on the projected Z threshold.

The scroller text is not stored as a bitmap. It is a byte stream with small delay values embedded among characters. doit70 subtracts SCROLLSPEED, reads the next byte when the spacing counter wraps, and calls letter3d for printable characters.

SCROLLSPEED       90
LETTERDOTSPACING 128
text0             delay bytes + message bytes + zero terminator

letter3d uses the copied BIOS font as an 8x8 dot source. For every set font pixel it writes a dot record:

mov ds:dots[di],si        ; x
mov ds:dots[di+2],bp      ; y
mov ds:dots[di+4],ax      ; z sinus offset
mov word ptr ds:dots[di+6],OFFSET pset2

The dot index wraps inside DOTNUM1, so the 3D letters are a ring buffer. New letters overwrite older letter dots while the background stars remain in the rest of the array.

Rotation And Projection

The motion is built from two 2x2 rotations. set3drot creates sine/cosine pairs from the 64-entry sine table, one matrix for Y/Z and another for X/Z. doit70 changes the driving waves every 70 Hz tick:

sinus1 += 70
sinus2 += 177
wwave  += cosine-derived wave
zadder += cosine-derived depth offset + 8888
udwave/lrwave drive the two rotation matrices

The per-dot draw path applies the rotations, adds the global Z offset, rejects near/behind points, then performs perspective projection:

bp = Z + zadder
if bp < 1024: skip

screen_y = 256 * rotated_y / bp + 100
screen_x = 307 * rotated_x / bp + 160

Those constants match the runtime look. Letters swell when they approach the viewer, shrink and fragment when they move away, and curve through the field instead of moving as a flat sine scroller.

Dot Writers

The small writer is one OR operation into the private buffer:

pset1:
  mov al,ds:colb[si]
  or ds:[di],al

The large writer uses precomputed masks across several rows:

pset2:
  or ds:[di+0],ax
  or ds:[di+44],ax
  cmp bp,8300
  jl pset3
  or ds:[di-44],ax
  or ds:[di+88],ax
  or ds:[di-88],ax
  or ds:[di+132],ax

The writer does not compute circles or shaded sprites. It relies on a few horizontal masks, a 44-byte row pitch, and OR-composition. That is exactly the kind of compromise that makes the intro small: dots look chunky and luminous, but the drawing code stays tiny.

AdLib Player

The audio code is a size-optimized AdLib player, not a raw register dump. The comments call it Simplex Adlib Player, and the source says the tune was by Skaven and converted with ST3->SIMPLEXADLIB.

music_channels = 8
music_speed    = 8
instrument data for five instrument definitions
pattern streams with delay/note bytes

The output routine writes to ports 388h and 389h, waiting after both the register select and data writes. a_init initializes the OPL state, loads instruments for nine AdLib melodic channels, and silences notes. a_dorow advances channels from pattern pointers, handles embedded delay bytes, converts note nibbles through a_note_table, and writes key-on/key-off data through a_playnote.

The important structural point is that music is integrated into the same 70 Hz simulation step as the visuals. It is not a separate DOS player and not a sample stream; it is part of the 1993-byte COM.

Exit Path

The main loop polls BIOS keyboard status:

again:
  call doit
  mov ah,1
  int 16h
  jz again
  mov ah,0
  int 16h

On exit the program restores the timer, reinitializes AdLib to silence it, returns to text mode, and writes a one-line final ANSI-style board card. That end line contains historical BBS contact material; the analysis does not transcribe it because the technical evidence is in the source and runtime effect, not in republishing phone numbers.

Runtime-To-Code Concordance

The media set is a direct DOSBox-X run of the original SP2.COM: six timestamped frames, one contact sheet, and one short scroller GIF. This pass does not recapture, crop, pad, resize, regenerate, or alter those assets. The bridge is unusually strong because the released SP2.ASM source names the data structures and routines behind the visible effect.

Visual evidence Source/code evidence What it means
00:00.500 starfield-only frame Startup seeds the 444-entry dots list, with background stars using pset1; private vbuf is copied through clearcopy The intro is already running the same dot projection/copy pipeline before readable text enters.
00:05.000 first projected letters text0 byte stream, SCROLLSPEED, LETTERDOTSPACING, and letter3d converting BIOS 8x8 font bits into dot records The board text is generated from font geometry, not stored as a flat bitmap scroller.
00:08.000 large close letters and the scroller GIF Projection path uses rotated X/Y/Z, rejects near points, then computes screen_y = 256*y/z + 100 and screen_x = 307*x/z + 160; pset2 grows dots by Z threshold The swelling letters in the GIF are perspective-sized 3D point letters sharing the starfield renderer.
00:11.000 fragmented mid-run letters set3drot builds two rotation matrices from sine/cosine pairs; doit70 advances sinus1, sinus2, wwave, zadder, udwave, and lrwave The broken/curving letter shapes are rotation and depth motion, not random corruption or palette-only animation.
00:18.000 field between readable text passes DOTNUM1 = 256 reserves text dots while the remaining entries persist as background stars Text and background points are one list with different writer routines; gaps reveal the persistent starfield.
00:23.000 late board-name sweep text0 wraps via zero terminator and ring-buffer replacement inside the first DOTNUM1 dot records The late title sweep is the looped scroller source coming back through the same dot pipeline.
Contact sheet shimmer across frames setpal uploads palette-index tables index1..index4; the ninth byte selects sequencer map mask plane order The visual shimmer comes from palette order plus VGA plane cycling, not just point coordinates.
Smooth timing and integrated audio INT 8 hook, PIT divisor 17000, framecounter, doit70, and a_dorow AdLib row advancement Simulation and the compact Skaven AdLib tune advance from the same 70 Hz timing model.
1993-byte COM size despite the complete effect BIOS font reuse, 44-byte-pitch private buffer, precomputed row/column/mask tables, small OR-based dot writers, and Simplex AdLib pattern data The small file works because text geometry, rendering, palette, timing, and music are all specialized to one effect.

The runtime GIF is therefore a faithful summary of the source: a planar VGA/AdLib BBS ad where text becomes 3D point geometry, the same dot list holds letters and background stars, palette/index records animate colour and plane order, and a 70 Hz timer drives both simulation and music.

Why It Works

StarPort BBS Intro II is impressive because almost every byte does double duty:

The runtime GIF is therefore not just decoration. It shows the central trick: StarPort II is a tiny BBS ad built like a real demo effect, with text treated as 3D point geometry and then pushed through the same VGA/timer/music loop as the starfield.