Analysis model: sol high, GPT-6 (reasoning level not recorded)
Last edited: 2026-09-17
Revision attribution: GPT-6, called Astra in this session, independently checked and corrected the earlier sol high working analysis.
Mini Intro by Trug - Technical Dissection
Release year: 1991.
Mini Intro combines a diagonal rainbow scroller with a field of single-pixel sine dots. Its small visual vocabulary hides several useful implementation details: two differently scaled sine tables, a thirteen-record controller, selective old-point erasure, a skewed glyph buffer, and a scroller that runs twice per dot update. The text and shape sequences have different periods.
Pouet dates the intro to July 1991 and files it under Future Crew and Image. The submitter explicitly describes it as Trug's individual production. This article therefore uses Trug as the author rather than inferring a collaboration from those catalogue group tags. The preserved EXE member is dated 6 August 1991; that archive timestamp and the catalogue month are distinct evidence.
The examined release is the
Scene.org archive.
Its three members are the 9,328-byte trug.exe, a 19-byte file_id.diz,
and a later 2,714-byte archive notice.
trug.zip SHA-256
f9915d616f1125ec7f1eb7f9e5431fee9e4bed5bc35ab3ed4c32decf04a31b37
trug.exe SHA-256
7302fe78b5d4bb4bf1e0ecc9f418ec2698b421026f2136e16219abb1922062ca
Evidence and address convention
The original packed program runs in DOSBox-X 2024.03.01 with S3 VGA and 10,000 fixed cycles. Host audio is disabled. The direct capture uses a 640x400 guest surface representing VGA mode 13h. Capture timestamps are host recording timestamps; they are not instruction counts.
The 17 September 2026 recording lasts 245.000 seconds and contains 4,899 decoded FFV1 frames with no audio stream. The requested host sampling rate was 20 fps. The recording starts before program launch; it is not aligned to an emulated retrace. A scripted Space key near the end exits the intro; the frame at 04:04.000 shows the DOS prompt. That exit frame and its historical contact text are excluded from the site.
The five-second excerpt below is capture 01:10.000..01:15.000. It shows the dense two-lobed ring formation and the moving diagonal text. The two lobes are part of the rendered point field, visible in consecutive frames. The excerpt preserves the complete 640x400 guest surface.

This full frame at 01:30.000 shows a later multi-lobed field. Its caption does not assign a controller record using wall-clock estimates alone.

For the code study, two executable restoration stages were followed into
the Pascal program. Offsets in the following discussion are relative to
the recovered main code segment. In the controlled memory image that
segment is 1000h, and DS is 113Ah: a data address DS:n corresponds
to file offset 13A0h+n in the 64 KiB memory snapshot.
The 64 KiB snapshot is a working memory dump, not a claimed original executable length. Its SHA-256 is:
4320a28067b958070293e024f0d71167991f8037f76bd612f2c6f2d7402b0428
The loop checks execute recovered x86 instructions in Unicorn. They stub Pascal stack checking, BIOS video setup, retrace polling, palette I/O and keyboard queries. They prove address sequences, state transitions and operation counts under the stated initialization. They do not measure DOSBox frame rate or prove physical VGA timing.
The main loop has two scroller updates
Startup at 02FDh initializes current count to 21, enables the growth
branch, clears the buffer, selects mode 13h, uploads the palette, and
loads controller record 1. The repeating code begins at 03EFh:
wait for next vertical retrace
update and display scroller
erase previous dot addresses
if growth enabled and current_count < target:
current_count += 1
else if growth disabled and current_count > 2:
current_count -= 1
if scroll_command == B5h:
load next controller record; clear command
for i = 0 through current_count-2, inclusive:
colour = i+1 if i+1 <= 254 else 1
draw one dot; save its address; advance its phases
advance the four frame base phases; copy them to current phases
wait for next vertical retrace
update and display scroller
poll keyboard; repeat while no key
Retrace helper 0000h first waits for port 03DAh bit 3 to clear,
then waits for it to set. Thus there are two retrace waits for each dot
update. Nominal refresh-based estimates must still account for whether
the drawing work misses a retrace at the selected CPU speed.
Current count is an exclusive-style state variable: 21 means 20 dot writes, and 257 means 256. The original colour branch does not implement modulo 254: both dots beyond index 253 receive colour 1.
Dot inner loop: two amplitudes, no multiply
0094h..0133h draws one dot. Its first two phase bytes index the
256-byte table at DS:0184; the other two index DS:0084.
Every entry matches the following rounded sine definitions:
small[i] = round(25 + 25*sin(2*pi*i/256)) # 0..50
large[i] = round(70 + 70*sin(2*pi*i/256)) # 0..140
y = 50 + small[p0] + small[p1] # 50..150
x = 20 + large[p2] + large[p3] # 20..300
address = 320*y + x
A000[address] = colour
erase_list[i] = address
p0,p1,p2,p3 = (phase + corresponding_step) modulo 256
There is no coordinate multiply in this routine. XCHG AL,AH converts
y into 256*y, two right shifts obtain 64*y, and the additions form
320*y+x. The bounded table sums keep the byte-swap shortcut valid.
All dots remain within the visible 320x200 page; no clipping branch is
needed for these coordinate bounds.
The original instructions passed 1,280 independent input cases: all 256 equal-phase vectors and 1,024 reproducibly seeded mixed vectors. Each case checked the emitted pixel, saved erase address, and all four phase updates.
The old-frame eraser at 0071h walks current_count-1 saved words and
writes zero to those addresses. It does not clear the page and does not
restore underlying artwork. Repeated or coincident dots still cost a write
and a history entry. At 256 dots, the field needs 256 old-pixel clears and
256 new-pixel stores per main update, plus 512 bytes read and 512 bytes
written in the address history.
Controller records and the growth trap
Each ten-byte record holds a target word, four per-frame phase increments, and four per-dot increments. Displaying increments as signed bytes makes their direction readable; the actual additions wrap modulo 256.
| Record | Target state | Frame phase increments | Per-dot increments |
|---|---|---|---|
| 1 | 20 | 0, 0, 0, 0 | 1, 0, 1, -128 |
| 2 | 20 | 0, 0, 1, 0 | 1, 0, 1, -128 |
| 3 | 257 | 0, 0, 1, 0 | 1, 0, 1, -128 |
| 4 | 257 | 0, 2, 2, 2 | 1, 0, 1, -128 |
| 5 | 257 | 0, 0, 1, 1 | 6, 10, 5, 3 |
| 6 | 257 | 2, 0, 2, 2 | 4, 1, 126, 1 |
| 7 | 257 | 0, 0, 2, 2 | 63, 0, 1, -128 |
| 8 | 257 | 0, 0, 2, 2 | 6, 10, 8, 3 |
| 9 | 257 | 0, 0, 1, 1 | 3, -2, 3, -126 |
| 10 | 257 | 0, 1, 2, 2 | -66, 4, -2, -2 |
| 11 | 257 | 1, 0, 1, 1 | 1, 2, 0, -127 |
| 12 | 257 | 0, 3, 0, 3 | 0, 2, 1, -127 |
| 13 | 157 | 1, 2, 1, 0 | 10, 2, 1, -128 |
0195h loads the selected record and advances the selector; after record
13 it returns to record 4. It does not reset all accumulated phase bases.
The early controller records introduce motion and then grow the field.
The target word is not an unconditional assignment or a bidirectional easing target. With the growth flag set, a target below the current count does nothing. An 8,000-update instruction trace enters record 13 on update 4,816 with count state 257; the next record on update 5,055 still has count 257. Reading the final record's 157 as a visible reduction would incorrectly describe the program.
Scroller: three different address walks
The far routine at main offset 0560h runs twice per dot update.
Every nine calls it fetches another script byte and copies sixteen font
bytes into a temporary glyph. Character slots are 32 bytes apart, while
this renderer reads bytes 1..16 of the selected slot. Bytes outside the
accepted range produce an empty glyph and can still act as commands.
The glyph-column step rotates each byte through carry, choosing either
zero or a colour index. Its sixteen stores begin at DS:3204h and
advance by 319, because STOSB adds one and ADD DI,013Eh adds
318. Relative to buffer 3150h, row r receives a pixel at column
180-r. This is a skewed insertion edge.
There are nine column steps for an eight-bit glyph. On the tested CPU
model, RCL AL,9 has effective rotation count zero; carry decides the
store. For an all-set test glyph, the first eight steps emit sixteen
nonzero samples. Step nine emits zero on rows 1..15, supplying spacing;
row zero can inherit carry from the earlier colour comparison.
Presentation starts from buffer row 2 and reads fifteen rows of 180
bytes each. Each MOVSW advances the destination by two, then
ADD DI,0140h advances it by another 320. After ninety words, the
subtraction 6FF4h makes the next source row start one screen row lower:
source(row, word) = DS:3150h + 320*(row+2) + 2*word
dest(row, word) = (8980h + 320*row + 322*word) modulo 65536
row = 0..14; word = 0..89
Both bytes of each word are stored. The 2,700-byte destination sequence was matched byte for byte against execution: 2,580 addresses are in the visible screen and 120 in its offscreen tail. Ninety of the visible addresses arise after segment wraparound. These counts describe writes, including black pixels; they do not imply ninety visible stray dots.
Finally, the routine shifts 180 bytes left in each of buffer rows 0..14,
using forward overlapping REP MOVSW. Presentation and shifting therefore
cover different row ranges. Together with the sixteen injected bytes and
one explicit clear at DS:350Fh, each call issues 2,717 buffer-byte
writes and 2,700 VRAM-byte writes. The video transfer alone is 5,400
bytes per dot update, much larger than the dot field's 512 pixel writes.
Colour changes are pixel indices
The palette routine at 0014h is called once from 03DDh during startup.
It rearranges its component arrays and writes DAC indices 1..254, with
254 index outputs and 762 component outputs. The steady frame loop does
not call it. Describing a live DAC colour conveyor would misattribute the
rainbow effect.
The scroller advances a colour counter each call, wrapping 240 to 1.
An illuminated glyph sample uses counter+row, so the skewed insertion
edge receives a graded set of colours. Subsequent RAM shifts and diagonal
presentation move those already coloured pixels across the screen.
Text and shape timing do not share one loop
B5h requests the next dot controller record. B4h redirects the text
pointer to offset 0216h (534) and immediately processes that byte.
It adds no extra blank character interval of its own.
The steady text region contains 1,094 consumed bytes and eighteen B5h
markers. At nine calls per byte, one pass takes 9,846 scroller calls or
4,923 main updates. The executed trace returns to script-next=535 on
updates 2,404 and 7,327, while the loaded shape record changes from 4 to
12. Eighteen markers advance a ten-record cycle by eight positions.
Text/controller alignment therefore repeats after five text passes.
That is an alignment period, not a proven framebuffer period. Accumulated phase bases, the 240-step colour counter, glyph state and presentation buffers must also agree before an identical pixel sequence can be claimed.
The command is tested only between the two scroller calls. A marker fetched by the second call is handled in the following main update. This explains why text fetch count and controller-entry count need not identify the same half-frame.
Corrections and limits
The independent revision corrected five errors in the earlier working interpretation: using one sine amplitude for both axes, describing a live palette conveyor, applying the scroller destination increment only once per row, assuming the final target shrinks the dot count, and treating colour overflow as modulo 254. The instruction checks above exercise the corrected interpretations directly.
The complete recording establishes the opening, multiple later formations, and keyboard exit. The controlled instruction trace establishes text-loop and controller state transitions. Matching every recorded shape to an exact controller entry would require a synchronized trace from the same DOSBox run; visual resemblance alone is insufficient.
The two-stage executable restoration supplied readable code and data, but the previous working notes' detailed unpacker instruction totals have not been independently reproduced in this revision and are omitted. No claim of a full framebuffer period or measured native refresh cadence is made.