Analysis model: gpt-5.5 xhigh, sol high

Last edited: 2026-08-26

# TU Player V1.0 by The Untouchables - Technical Dissection

Release date: 25 December 1991

`TU Player V1.0` is a small MS-DOS VGA intro by The Untouchables. Its one
screen is economical rather than static: a 33-row Gothic text strip enters one
four-pixel column per frame, a three-page planar ring lets the CRTC scroll that
strip without moving the whole screen, and four 17-colour ramps animate the
skull, eyes, bars, logo, and lettering. A custom packed-image decoder supplies
both the font atlas and the skull/`TU` base picture. The final `TU.DAT` region
is a complete four-channel ProTracker module, with direct-DAC Sound Blaster and
Covox playback code in the restored executable.

![Continuous direct TU Player runtime excerpt](images/tu-player-v1-runtime.gif)

This is a direct, continuous, silent DOSBox-X excerpt from graphics-stream
`00:04.000..00:10.000`, reduced from the emulator's exact 2x output to the
logical 320x200 frame. It shows the top strip moving independently of the
lower skull picture, the next Gothic columns entering at the right edge, the
horizontal bars crossing the split, and the four offset palette pulses. It is
not a slideshow or a public-screen reconstruction.

## Identity and source chain

- [Demozoo production page](https://demozoo.org/productions/303472/)
- [Pouet production page](https://www.pouet.net/prod.php?which=90511)
- [Scene.org archive page](https://files.scene.org/view/demos/compilations/lost_found_and_more/tu_player_v1_0_intro.zip)
- [Scene.org release archive](https://files.scene.org/get/demos/compilations/lost_found_and_more/tu_player_v1_0_intro.zip)

Demozoo records the release as an MS-DOS intro by The Untouchables on 25
December 1991. The archive's two payload timestamps are also Christmas Day
1991. This is not a shortened copy of the group's earlier *Anti-svd*: the
executables, `TU.DAT` sizes, decoded pictures, scroll text, and module formats
all differ.

```text
tu_player_v1_0_intro.zip   131,975 bytes
INTRO.EXE                   11,513 bytes   1991-12-25 15:37:34
TU.DAT                     174,035 bytes   1991-12-25 15:37:24
```

```text
9226a10f98eeae8f989dd5452aec0a80298e79790a626ad25c3971e9ec07b11f  archive
e303a84308dc15344653486808e0f6816e2d2b741130775564f52563e30d1957  INTRO.EXE
5518426aa66e81dfcbadb4c2ab0738c1cd1006d18b35452b869d2b0456bc6b6a  TU.DAT
```

![Unmodified public Pouet screenshot](images/tu-player-v1-pouet.png)

The unmodified 320x200 Pouet still catches the scroll phrase `The
Untouchables` above the same skull and logo. The direct run reproduces that
composition and shows what the still cannot: the words and bars belong to a
moving CRTC strip, while the skull and emblem below it come from one decoded
raster resource.

## Runtime capture and normal exit

The release was run through its explicit fastest/no-sound command-line route:

```text
emulator             DOSBox-X 2024.03.01, commit 02f36ba
machine / CPU        svga_s3, normal core, fixed 12,000 cycles
guest command        INTRO.EXE /speed:7 /sound:4
host audio           SDL dummy; guest no-sound branch
capture              DOSBox-X dx-capture /v /-a /-m
graphics stream      ZMBV 640x400, 70.0863 fps, 16.879189 seconds
exit input           one X11 Space event at host-run +15 seconds
timing zero          first frame of the 640x400 graphics stream
```

The first 13 seconds cover the running strip and palette phases. The injected
Space request becomes visible as the final palette fade by about stream
`00:14`; the image is almost black at `00:16`, BIOS mode 3 follows, and the
capture closes normally. The host mixer was dummy and sound option 4 skips the
module loader and all sample output. No soundtrack was monitored or auditioned.

DOSBox-X startup, SDL, XKB, and code-page diagnostics were redirected to an
ignored log. The installed build has `-silent`, but that option also selects
dummy video, so it is unsuitable for a visual capture; ordinary launch plus
stdout/stderr redirection is the quiet capture path used here.

## Two unpacking boundaries

The distributed 11,513-byte MZ contains a `PKLITE Copr. 1990-91` signature.
One UNP pass expands it to a 23,109-byte MZ that still contains the inner
backward expander. A second pass restores the actual application:

```text
first UNP result       23,109 bytes
SHA-256                b5203fa1524e9fde227480d90486fc610b9ea6cd02617a5ee6a3c9fe911f2bec

final restored MZ     252,672 bytes
SHA-256                09715545035d8a18c7b0a9c9ef96850e824e9ede274feab5a53c8a300a3cfc73
MZ header                  272 bytes (0011h paragraphs)
load image             252,400 bytes
relocations                 60
entry                    144A:0010
entry file offset          145C0h
stack                    3D9F:46B0
```

Addresses below are written as linked `segment:offset` values from this final
image. Its principal file bases are `0589 -> 059A0h`, `144A -> 145B0h`, and
`1581 -> 15920h`; DOS adds the process load segment through the relocation
table at runtime.

The MZ entry at `144A:0010` gets the PSP with `INT 21h/AH=62h`, saves it,
checks four marker words (`3412h,7856h,BC9Ah,F0DEh`) at the load-image start,
calls the application body at `144A:10AF`, and exits through
`INT 21h/AX=4C00h`. This explains why the application can use raw segment zero
as shorthand for the relocated load segment: several zero immediates are
relocation words, not references to physical low memory.

## Startup dispatcher

`144A:10AF` first checks `F000:FFFE`; machine-model byte `FCh` enables
`REP OUTSB` for DAC transfers, while other machines use an explicit
`LODSB/OUT` loop. It then uppercases the PSP command tail and accepts:

```text
/SPEED:1..7
/SOUND:1..4
```

The speed words are:

```text
2000 26D7 2E9C 3E25 4295 4DAE 5D37
```

The selected word is reduced by `0500h`. Sound values become zero-based:
Sound Blaster, Covox LPT1, Covox LPT2, and no sound. Missing switches lead to
the corresponding BIOS-keyboard menus.

The graphics path is then:

```text
144A:00D9  install private INT 9 handler
144A:11B6  BIOS mode 13h
144A:0870  clear all 768 DAC components
144A:0954  unchain mode 13h
144A:0939  clear all four 64 KiB VGA planes
144A:0BD7  program CRTC split/start state
0589:014C  decode font atlas to segment 2DA1h
0589:014C  decode base image directly to planar A000h
144A:08EC  copy 768-byte release palette
144A:07D4  64-level palette fade-in
144A:12AC  enter the frame loop
```

The INT 9 handler at `144A:00B2` reads port `60h`, recognizes only Space scan
code `39h`, writes exit state 1 unless shutdown has already reached state 6,
pulses port `61h` bit 7, acknowledges the master PIC, and returns without
chaining the previous keyboard handler. Escape therefore does not end this
intro.

## Exact `TU.DAT` directory

The resource-loader segment starts with three little-endian dword offsets:

```text
0589:0000  00000000h
0589:0004  00002BDDh
0589:0008  000054F1h
```

They divide `TU.DAT` exactly:

| File range | Bytes | Proven role |
|---|---:|---|
| `00000h..02BDCh` | 11,229 | packed 320x200 Gothic font atlas |
| `02BDDh..054F0h` | 10,516 | packed 320x200 skull/`TU` base picture |
| `054F1h..2A7D2h` | 152,290 | complete four-channel `M.K.` module |

The first word of each picture is its compressed payload length:

```text
block 0: 2BDBh + 2-byte length = 2BDDh bytes
block 1: 2912h + 2-byte length = 2914h bytes
```

The graphics loader at `0589:014C` opens `tu.DAT`, seeks to the selected dword,
requests `EA60h` (60,000) bytes into `0589:01AC`, closes the handle, and calls
one of two decoders. Those deliberately oversized reads overlap later regions,
but each bitstream terminates through its exact final payload byte. The loader
checks open failure, not a short read; open failure exits immediately with DOS
status 1 instead of unwinding video and interrupt state.

## The packed-image inner loop

Linear decoder `0589:0050` and planar decoder `0589:009F` share the same token
loop. Both start at packed offset 2 and stop when logical destination index
`DI` reaches `FA00h`, exactly 64,000 pixels. Every token uses two overlapping
big-endian byte windows even though the stream-length header is little-endian:

```text
window = (source[si] << 8) | source[si+1]
window = RCL16(window, bit_position)
window = RCL16(window, 9)

carry clear: emit low_byte(window) once
carry set:   retain that byte as colour,
             extract another window with RCL16(...,10),
             emit colour (low_byte(second_window)+1) times

bit_position++
at bit_position 8: reset it and advance source one extra byte
```

The first stream consists of 557 literals and 4,988 repeated-value tokens;
the second has 3,879 literals and 2,894 runs. A run can emit 1..256 pixels.
Decoded indexed hashes are:

```text
bb851194d3e850f22e214ae3da19c7440ff6ca95499488d6378b514d37a759de  font
aa310556183ca439d8305a94181e1a7cfe7c7f967a0ae3337cf8a336d32888af  base picture
```

The linear path writes ordinary bytes with `STOSB`. The planar store at
`0589:002B` turns logical pixel index `p` into:

```text
SEQ map mask = 1 << (p & 3)
VGA byte address = (p >> 2) + plane_base
```

It then increments the original logical index. Recombining the four planes
therefore recreates the normal 320x200 chunky order exactly.

![Exact decoded Gothic font atlas](images/tu-player-v1-decoded-font.png)

This is the first `TU.DAT` stream decoded by that loop and coloured with the
exact 768-byte, 6-bit DAC palette at restored load offsets `0071h..0370h`.
It is not a font approximation: the renderer reads its columns directly from
the 64,000-byte result in segment `2DA1h`.

![Exact decoded skull and TU background](images/tu-player-v1-decoded-background.png)

This is the second stream under the same release palette. It owns the skull,
eyes, logo, and tiny lower credits in both runtime images. The bars and top
message are absent because those are the live strip inserted after this image
has been decoded into planar VGA memory.

## Scroll-message codec and glyph records

The restored load image contains 661 encoded message bytes at
`0375h..0609h`, followed by count `0295h`. The frame code decodes one source
byte at a time with feedback from the previous encoded byte:

```text
x          = encoded[i] XOR encoded[i-1]       // encoded[-1] = 0
plain      = ROL8(x,4) XOR 3Eh
glyph_code = plain - 20h
```

`plain` is ordinary text. It begins:

```text
Well Dudes.... You're watching at a new masterpiece from The Untouchables
Released On Christmas-Day 1991 (25 December 1991) ...
```

and closes with Christmas/New Year greetings and thanks to the composers of
the included MOD material. The old board-name interval was inspected to close
the stream but is not reproduced here.

For glyph code `g`, the supporting tables are:

```text
width byte       load[0616h + g]
source offset    word load[06D8h + 2*g]
source x/y       offset % 320, offset / 320
glyph height     33 source rows
```

Only codes 0..89 occur. The width table is stored in pixels and the renderer
uses `width >> 2` as the number of four-pixel columns. Each source pointer
lands directly in the decoded atlas shown above.

## One complete scroller frame

The steady-state renderer begins at `144A:0A62` (restored file `15012h`). Its
active constants are:

```text
logical source pitch       320 bytes
VGA pitch per plane         80 bytes
strip height                33 rows
one strip page         33*80 = 0A50h bytes
three-page ring        3*0A50h = 1EF0h bytes
ring base                  7D00h
new-column x byte             79
```

The visible top region is 32 logical rows; row 33 is the guard/source row used
by the ring. Mode 13h scan doubling turns the split into about 64 physical
scanlines. CRTC line compare is exactly 63.

Before drawing, the renderer advances the current glyph only after source
phase reaches `width[g] >> 2`. It then feedback-decodes the next scroll byte,
looks up that glyph's atlas pointer, and computes:

```text
source = glyph_pointer[g] + 4*source_phase
dest   = 7D00h + scroll_offset + 79
```

The first nested loop writes the entering four-pixel column into a selected
display page:

```text
for plane = 0..3:
    SEQ.map_mask = 1 << plane
    SI = source + plane
    DI = dest + page_select

    repeat 33 rows:
        MOVSB                    // one source pixel into this VGA plane
        SI += 013Fh              // net source stride: 1+319 = 320
        DI += 004Fh              // net VGA stride:    1+79  = 80
```

This is 4 planes x 33 rows = 132 byte writes. `144A:0A10` then writes CRTC
start address `7D00h + scroll_offset + page_select`; `page_select` toggles
between 0 and `0A50h`.

After the retrace/palette handoff, the same four-plane, 33-row column is copied
again at a two-page displacement. Each row compares `DI` with
`7D00h + 3*0A50h`; crossing it subtracts `0A50h` three times. That second 132
writes keeps all aliases of the circular backing strip coherent. Finally:

```text
scroll_offset++
if scroll_offset == 0A50h: scroll_offset = 0
source_phase++
```

One frame therefore performs only 264 planar video-byte writes. It does not
copy or clear 64,000 pixels. The CRTC advances one plane byte—four logical
pixels—through the ring, while the fixed lower picture restarts at line
compare 63. This is the exact ownership split visible in the GIF.

## VGA setup and presentation

`144A:0954` derives unchained byte-addressed mode from BIOS mode 13h:

```text
Sequencer 04h: clear bit 3, set bit 2      disable chain-4 / odd-even
Graphics  05h: clear bit 4
Graphics  06h: clear bit 1
CRTC      14h: clear bit 6
CRTC      17h: set bit 6
```

The initial presentation setup writes:

```text
CRTC 18h = 3Fh       line compare 63
CRTC 09h = 80h       scan doubling retained
CRTC 07h = 01h
CRTC start = 7D00h
```

There is no mode probe or fallback. The all-plane clear selects sequencer map
mask `0Fh` and writes 32,768 words to `A000:0000`, covering every 64 KiB plane.

## Palette baseline, dirty range, and four pulses

The direct picture is animated chiefly through palette memory. At
`144A:0D38`, each frame restores `01A4h` (420) baseline bytes into the working
palette and preserves the previous dirty endpoints. Four calls to
`144A:0F87` then overlay 51 bytes—17 RGB entries—at staggered phase starts:

```text
frame >= 0000h  ramp at load 0DEDh, phase byte 0F86h
frame >= 00C8h  ramp at load 0E20h, phase byte 0F85h
frame >= 01A4h  ramp at load 0E53h, phase byte 0F84h
frame >= 0280h  ramp at load 0E86h, phase byte 0F83h
```

Each phase advances through 0..198 and indexes the 200-byte triangular table
at `144A:0F81`. The destination is:

```text
palette[010Bh + 3*phase_table_value : +51]
```

The routine records the affected palette-index interval. `144A:0D7C` writes
only the merged current/previous dirty span through DAC ports `03C8h/03C9h`;
when the computed upper endpoint is below the lower one, it emits nothing.
Every fourth frame, `144A:0FE8` also oscillates another palette interval by
one component step. This is why the skull eyes, logo, bars, and letters pulse
without any corresponding pixel rewrite.

The main phase counter starts at zero, increments once per loop, and at
`2000h` subtracts `1000h`. It therefore traverses 1..8191 once and subsequently
cycles through 4096..8191. The delayed ramp starts at frames 200, 420, and 640
occur only during that first traversal.

## Retrace and IRQ pacing

The paced wait at `144A:011E` first waits until `03DAh` bit 3 is clear. It
compares player IRQ count `1581:75AC` with `floor(selected_rate/60)`. If the
count is not already above that budget, it waits for the next retrace start;
if the renderer is late, it skips the extra wait. It then clears the count.

No-sound option 4 skips module loading, PIT reprogramming, and sample mixing.
Its IRQ0 handler at `1581:6A19` performs only the stop handshake and PIC EOI,
so the sample counter remains zero and normal VGA retrace controls the frame
rate. The measured graphics stream is 70.0863 fps, matching the mode-13h-class
refresh rather than a fixed 60 fps.

## Static module and mixer audit

The soundtrack was not played, but the binary and module path are fully
addressable. `TU.DAT` offset `54F1h` begins a complete 152,290-byte ProTracker
module and ends exactly at EOF:

```text
title               SHAKIN
signature           M.K. at module +0438h / file 05929h
song length         17 orders
orders              1,1,0,2,3,4,5,5,6,6,6,7,8,8,3,0,0
patterns            9 x 1024 bytes
nonempty samples    13 of 31
sample bytes        141,990
```

Music loader `1581:79C5` opens the same `tu.DAT`, seeks to `54F1h`, probes both
the `!Scream!` and `M.K.` positions, and selects its 31-sample MOD loader. It
reads 31 headers, the 128-position order table, 9 patterns, and 31 sample
slots. Inactive branches support Scream Tracker and 15-sample MOD material;
neither owns this release data.

For sound options 1..3, the selected speed word minus `0500h` is a requested
sample IRQ rate. At speed 7 that is 22,583 Hz; the player chooses PIT divisor
`floor(1193180/22583)=52`, yielding about 22,945.8 IRQ/s. Tracker setup uses six
ticks per row and a countdown near `rate/49`.

The mixer has four channel records with 16.16 nearest-neighbour sample
stepping, sample end/loop pointers, volume, and a 65x256 signed-sample/volume
lookup table at `1581:1D45`. The IRQ alternates channel pairs `(0,1)` and
`(2,3)`, pipelines adjacent sums, adds `80h`, and performs no separate clipping
pass.

Sound Blaster output is deliberately primitive:

```text
base ports      fixed 220h; BLASTER is not parsed
setup           reset through 226h, then send D1h speaker-on
each IRQ        poll 22Ch, send direct-DAC command 10h,
                mix one byte, poll 22Ch, send sample
DMA             none
SB IRQ          none; timing is PIT IRQ0
```

Covox output uses the LPT1/LPT2 data-port words at BIOS data addresses
`0040:0008` and `0040:000A`, with one `OUT DX,AL` and no control-port strobe.
All playback modes replace INT 8 and acknowledge the master PIC without
chaining the BIOS clock handler.

## Space handshake and cleanup

When the keyboard ISR changes state to 1, main changes it to 2 and waits. The
active IRQ0 handler notices state 2, calls the common stop helper at
`1581:69DE`, changes state to 6, and sends PIC EOI. Main then:

```text
144A:0750  read the palette and perform 64 decrement passes
port 21h   force PIC mask 00h
port 40h   write FFh,FFh as PIT channel-0 divisor
1581:7566  restore saved INT 8 vector
144A:00FA  restore saved INT 9 vector
INT 10h    BIOS mode 3
RET        return to MZ exit
```

The fade reads all 768 DAC components in four 192-byte chunks and performs
`64*4` paced writes, 261 paced waits including setup. Cleanup is functional but
not a byte-for-byte hardware restore: the previous PIC mask and PIT mode/count
are not saved, lost BIOS ticks are not compensated, the Sound Blaster never
receives speaker-off `D3h`, and a Covox latch is not centred.

The common IRQ stop helper also runs a backwards XOR transform over part of the
loaded image using the physical INT 2 vector offset as both starting/count
material. Its instructions are verified; whether the original intent was
anti-ripping, self-scrubbing, or another cleanup convention remains uncertain.

## What the screen actually is

This production contains no vector-object, polygon, or generated-circle
renderer. Its skull and `TU` emblem are packed indexed pixels. Its text is a
packed raster font whose four-pixel columns are injected into a 33-row planar
ring. Its apparent moving light is four palette overlays, not redrawn shading.
The CRTC split and start-address updates make that narrow strip move while the
lower raster remains fixed. The public still, exact resource decodes, direct
runtime GIF, and restored inner loops all describe the same screen.
