Analysis model: gpt-5.5 xhigh
Cycling - Abaddon
Subject
Cycling is a 1992 MS-DOS VGA demo by Abaddon. The Hornet 1992 index lists it
as:
cycling.zip 238171 *** Cycling by Abaddon : ,,SB,
Release metadata:
- Release year: 1992
- Group: Abaddon
- Package examined:
cycling.zipfrom the Hornet 1992 mirror - Executable:
CYCLING.COM - Required display: VGA
- Audio choices exposed by the program: PC speaker, LPT1 DAC, Sound Blaster, or no sound
- Music file:
STUDIO.STM, a Scream Tracker module titledPetimix
Sources:
The archive's decoded credits asset contains old contact text. That frame is deliberately omitted from the public screenshot/contact-sheet set.
Runtime Capture

The runtime contact sheet above is from a local DOSBox-X capture:
Emulator: DOSBox-X 2026.01.02 SDL2
Capture date: 2026-06-19
Machine: svga_s3
CPU core/cycles: normal, 40000 cycles
Sound: disabled for capture
Menu input: 4 = no sound, then 4 = highest frequency menu option
Command: CYCLING.COM
Capture stream: MPEG-TS/H.264, 118.370677 seconds
Timing zero: beginning of captured stream, including text-mode menus
The published runtime images start after the menu because the menu frame shows local scratch paths from the DOSBox shell.
The sequence GIF below is assembled from the same public-safe runtime frames. It skips the text-mode menus and contact-bearing credit asset, then compresses the visible demo arc into one scan: Abaddon logos, aperture wipe, Cycling title page, pink text strips, VH logo, and the late blue banded texture states.

Representative observed timestamps:
| Timestamp | Screen state |
|---|---|
00:04.000 |
Red Abaddon logo and the "cat has broken up" intro text. |
00:08.000 |
Blue Abaddon swirl/logo page. |
00:16.000 |
Four-way black aperture over the blue Abaddon page. |
00:28.000 |
CYCLING logo over the pink faceted background. |
00:36.000 |
Large pink scroll/zoom text strips over black. |
00:48.000 |
Red VH logo page. |
01:28.000 |
Blue texture bands over the persistent lower/side logo frame. |
01:36.000 |
Wider blue banded texture state. |
The capture was stopped by the emulator time limit, not by a natural demo exit.
Runtime-To-Code Concordance
The GIF and timestamped frames above are direct DOSBox-X runtime frames. The code analysis below explains why those frames look the way they do; no additional viewport correction, padding, or regenerated staging is needed.
| Runtime evidence | Code path / data path | What it proves |
|---|---|---|
| Public-safe runtime sequence GIF | The GIF intentionally skips text-mode menus and the contact-bearing CRED.ABA page. |
The visual sequence is a bounded runtime sample, not a complete capture of every shipped screen. |
00:04.000 and 00:08.000 Abaddon logo pages |
Mode 13h setup at 0x0345, .ABA loader/decompressor at 0x22e4/0x22e7, palette at the first 768 bytes of each asset, default destination A000h. |
The logo pages are compressed VGA bitmap assets decoded into display memory, not generated vector scenes. |
| Horizontal striping/doubled-line look in the logo and title screens | The .ABA row check at 0x2368 advances DI by another 0x0140 bytes after each 320 decoded bytes. |
The striped layout is a deliberate one-line-write/one-line-skip memory layout produced by the asset decoder. |
00:16.000 four-way aperture over the blue Abaddon page |
Retrace helper 0x1cdf, transition loops around 0x0526/0x0577, and line helper 0x2390; the main loop also writes CRTC state around 0x04cb. |
The wipe is a real-time transition over an already decoded screen, paced to vertical blank and CRTC/palette updates. |
00:28.000 CYCLING title over pink faceted background |
CYCLING.ABA is loaded at 0x060d; BACKRND.ABA is loaded at 0x0619 to a later segment (AX=A104h) with row offset 0x0140. |
The title page combines decoded asset layers with the same line-skipping layout rather than a single flat screenshot. |
00:36.000 pink scroll/zoom text strips |
Text/glyph routine 0x1eed, glyph mask table at 0x23fe, palette/color window around 0x1fa8, and DAC upload from index 04h. |
The pink text is assembled from a bit-mask renderer plus palette animation, not just a prerendered full-screen bitmap. |
00:48.000 red VH logo page |
VHLOGO.ABA is named in the embedded file table and uses the shared .ABA decode path. |
The late logo page is another compressed asset stage in the show chain. |
01:28.000 and 01:36.000 blue banded texture states |
Palette shifter 0x09b5 shifts 207 colors from DAC index 48 onward; moving speck helper 0x0b4c updates 24 small entries. |
The late motion is mostly palette rotation and tiny dirty-plot updates over static decoded image data. |
Capture menu choice 4 = no sound |
Menu code at 0x0153 jumps over music setup for no sound; active sound choices write the output port at 0x13db. |
The visual capture intentionally has no audio, but the executable still contains a complete selectable PCM output path. |
STUDIO.STM / Petimix |
INT 08h handler at 0x0c72 mixes four channels, converts to unsigned output, and branches to LPT1, PC speaker table, or Sound Blaster DSP write. |
The sound system is a timer-driven 4-channel software mixer even though this public capture was silent. |
This is the main technical character of Cycling: compact custom assets,
line-skipping decode, retrace-paced palette/CRTC motion, a small masked text
renderer, and a capable timer PCM mixer.
Archive Contents
Examined archive:
bc6195d62c0d21a4dbba916379f54297f1113f1f2e6d7e49f68760e4dafc1f27 cycling.zip
Extracted files:
ABI.ABA 59270 bytes
BACKRND.ABA 37152 bytes
CAT.ABA 14888 bytes
CRED.ABA 12664 bytes
CYCLING.ABA 14894 bytes
CYCLING.COM 10247 bytes
CYCS1.ABA 6372 bytes
CYCS2.ABA 4790 bytes
CYCS3.ABA 8788 bytes
CYCS4.ABA 6742 bytes
LOADING.ABA 10588 bytes
STUDIO.STM 144167 bytes
THEEND.ABA 66744 bytes
VHLOGO.ABA 14670 bytes
File hashes:
33ddaeaa144fbb7aa26b23e24b6bb73bb08e3cf117fa39800c9f255434411419 ABI.ABA
8c669872c94172da020e3cb9fa88617e330006abe95598558058e55c02ae8c19 BACKRND.ABA
2adf909d4c85d464e5880f79bc2ed5fa29aa7371103c5395a89195d973bfb03e CAT.ABA
5430fa0740e62979fc0babdfcc134017168619a1034c53ee9c359a45c984708a CRED.ABA
e3aa4e0fbeb72012c909dab8416526f5a4599cbc833c2ccf4d33ea0e94243bab CYCLING.ABA
7e9ff9408830e0466591a02d189fcd1de7becfaa0845180fc7829d7e69e5920b CYCLING.COM
3d4745e46b71682ac5e4c20994d23f3790e610863240f6350eb7afbae4b92e25 CYCS1.ABA
6a648794e3399156acaa590b22893d23e10026f5437af947f30c635ee97c160f CYCS2.ABA
46619f8368e46ecc37586ef32ba5832d6d8e6dd76dabe3b98e284f169fa18e53 CYCS3.ABA
00eaf64fd26df308cb6023cbef44abc80d2dcd483761c0c8579e292596b20b21 CYCS4.ABA
7c125ff70612f735b7d956b8090a587bbbae6a0c65c32f17adcb510583b04960 LOADING.ABA
fae0cc630a399f3caa14eef5ccc82670f59fb74bbb6ca923d314fb32753f3922 STUDIO.STM
deaf0c7c38d070bbea321e0877175b561929e23c00dc37265f0a5fa03841a84e THEEND.ABA
73edb2eead7e6ff9f99545bceddfc62be2200fdf950e1d7f9ca71233b1602db2 VHLOGO.ABA
CYCLING.COM embeds every external filename:
STUDIO.STM
CRED.ABA
CAT.ABA
ABI.ABA
LOADING.ABA
CYCLING.ABA
THEEND.ABA
CYCS1.ABA
CYCS2.ABA
CYCS3.ABA
CYCS4.ABA
BACKRND.ABA
VHLOGO.ABA
COM Startup And Menus
The program is a plain .COM loaded at CS:0100. It begins with a short jump
over an inline copyright string:
0100: eb 35 jmp 0137h
0102: 0d 43 6f ... ; "Copyright (C) Abaddon 1992..."
The startup path masks/unmasks the PIC, prints menu strings with DOS
INT 21h/AH=09h, and reads raw key choices with BIOS INT 16h/AH=00h:
013c: mov al,0ffh
013e: out 21h,al ; mask IRQs during menu setup
0141: mov sp,0141h
0144: mov dx,01abh
0147: mov ah,09h
0149: int 21h ; "Select sound device"
014b: mov al,0fdh
014d: out 21h,al
014f: xor ah,ah
0151: int 16h ; read device key
0153: cmp al,34h ; '4' = no sound
0155: je 0194h
Device selection writes the output port at 0x13db:
- PC speaker keeps the default path.
- Sound Blaster sets
0x13db = 022ch. - LPT1 sets
0x13db = 0378h. - No sound jumps over the music setup.
If sound is enabled, it asks for a playback-frequency class:
0170: mov dx,0201h
0173: mov ah,09h
0175: int 21h ; "Select playing frequency"
0177: xor ah,ah
0179: int 16h
017b: sub al,31h ; key '1' -> 0
017d: cmp al,04h
017f: jb 0183h
0181: mov al,02h ; out-of-range defaults to option 3
0183: cbw
0184: shl ax,2
0187: mov si,019bh
018a: add si,ax
018c: lodsw
018d: mov [13ddh],ax
0190: lodsw
0191: mov [13dfh],ax
The frequency table at 0x019b is four numerator/denominator pairs:
option 1: 0002, 0003
option 2: 0001, 0001
option 3: 0004, 0003
option 4: 0005, 0003
The following code scales timing tables and calculates a PIT divisor. That is why the prompt is not a sound-card probe; it is a CPU/load tuning knob for the software mixer.
VGA And Interrupt Setup
After the menu, the demo installs handlers and enters mode 13h:
0306: xor ax,ax
0308: mov es,ax
030a: mov al,es:[0449h] ; save BIOS current video mode
0311: mov ax,es:[0020h] ; save INT 08h offset
0315: mov [0c2ah],ax
031f: mov ax,es:[0024h] ; save INT 09h offset
0323: mov [0c1ch],ax
032d: mov ax,0c72h
0330: mov es:[0020h],ax ; INT 08h -> audio timer
0334: mov ax,0c65h
0337: mov es:[0024h],ax ; INT 09h -> Escape handler
0345: mov ax,0013h
0348: int 10h
034a: cmp byte es:[0449h],13h
0350: je 039fh
The keyboard handler at 0x0c65 reads port 60h and exits on Escape:
0c65: push ax
0c66: mov al,20h
0c68: out 20h,al ; EOI
0c6a: in al,60h
0c6c: cmp al,01h ; Escape make code
0c6e: je 0c10h ; cleanup/exit path
0c70: pop ax
0c71: iret
The retrace wait helper is the classic 03DAh two-phase poll:
1cdf: mov dx,03dah
1ce2: in al,dx
1ce3: and al,08h
1ce5: jne 1ce2h ; wait until not in retrace
1ce7: in al,dx
1ce8: and al,08h
1cea: je 1ce7h ; wait until retrace starts
1cec: ret
That helper is called before palette uploads and many visible transition steps, so the effects are deliberately synced to vertical blank.
.ABA Image Format

The .ABA files are custom VGA image assets. The first 768 bytes are a VGA DAC
palette:
256 colors * 3 bytes = 768 bytes
channel range: 0..63
The bytes after the palette are a compact RLE stream. The public asset contact
sheet above omits CRED.ABA, because that decoded page contains old contact
text.
The decompressor is at 0x22e4 / 0x22e7. The two entry points differ only in
the destination segment:
22e4: mov ax,0a000h ; default destination
22e7: mov es,ax ; caller may enter here with AX already set
22e9: mov ax,3d00h
22ed: int 21h ; open filename in DS:DX
...
230d: mov cx,0fa00h
2310: mov ah,3fh
2313: int 21h ; read chunks into DS:2907h area
...
2333: mov si,2c07h ; 2907h + 768-byte palette
2336: xor di,di
2338: mov cx,di
233a: mov bx,di ; row base
The RLE control loop:
2350: lodsb
2351: test al,80h
2353: je literal
2355: cmp al,80h
2357: je done
2359: neg al
235b: mov cl,al
235d: inc cx
235e: lodsb
235f: rep stosb ; repeat next byte 257-control times
2361: jmp row_check
2363: mov cl,al
2365: inc cx
2366: rep movsb ; copy control+1 literal bytes
Equivalent decoder:
src = file + 768;
di = 0;
row_base = 0;
for (;;) {
control = *src++;
if (control == 0x80)
break;
if (control & 0x80) {
count = 257 - control;
value = *src++;
repeat(value, count);
} else {
count = control + 1;
copy_literals(src, count);
src += count;
}
if (di - row_base >= 320) {
di += 320;
row_base = di;
}
}
The row check is unusual and important:
2368: mov ax,di
236a: sub ax,bx
236c: cmp ax,0140h
236f: jb 2377h
2371: add di,0140h
2375: mov bx,di
After every 320 output bytes, the decoder skips another 320 bytes. In a normal mode-13h linear interpretation, that writes one line, leaves one line for background/transition content, and then writes the next line. This matches the horizontal striping visible in many captured screens and the doubled-line look in the decoded assets.
The decoder also normalizes ES:DI when DI approaches the 64 KiB boundary:
2377: cmp di,0fdc0h
237b: jb 233ch
237d: mov ax,di
237f: shr ax,4
2382: mov dx,es
2384: add ax,dx
2386: mov es,ax
2388: and di,000fh
238b: jmp 233ah
That lets larger assets such as THEEND.ABA decode through a moving segment
window instead of being limited to one 64 KiB address range.
Palette Operations
The palette-clear routine at 0x22cc waits for retrace, toggles the Attribute
Controller, sets DAC index zero, and outputs 768 zeros:
22cc: call 1cdfh
22cf: mov dx,03c0h
22d2: mov al,31h
22d4: out dx,al
22d5: xor al,al
22d7: out dx,al
22d8: mov dx,03c8h
22db: out dx,al
22dc: inc dx
22dd: mov cx,0300h
22e0: out dx,al
22e1: loop 22e0h
Several effect parts animate by rotating or shifting palette ranges rather than
rewriting the whole frame. One large palette shifter is at 0x09b5:
09b5: push cs
09b6: pop es
09b7: std
09b8: mov si,2c03h
09bb: mov di,2c06h
09bf: mov cx,026dh
09c2: rep movsb ; shift palette bytes upward by 3
09c4: inc si
09c5: mov ax,[si+026dh] ; wrap old tail to front
09c9: mov [si],ax
09cb: mov al,[si+026fh]
09cf: mov [si+2],al
09d3: mov al,30h
09d5: mov dx,03c8h
09d8: out dx,al ; update DAC from index 48
09d9: mov cx,026dh
09dc: inc dx
09dd: rep outsb
That routine uploads 621 DAC bytes, or 207 colors, from color index 48 onward. It is responsible for the animated blue/pink texture feeling in the captured backgrounds: the image data can stay static while the palette slides.
Timer Audio Inner Loop
Even though the capture used no sound, the audio path is one of the cleanest
inner loops in the program. STUDIO.STM begins with:
title: Petimix
tracker: !Scream!
The setup path reads the module, builds sample pointers, installs INT 08h,
and programs the PIT. For PC speaker/noise timing it writes:
02bc: mov al,34h
02be: out 43h,al ; PIT channel 0, mode 2/3 style timer setup
02c0: mov al,[13e1h]
02c3: out 40h,al
02c5: xor al,al
02c7: out 40h,al
02c9: mov al,90h
02cb: out 43h,al
02cd: in al,61h
02cf: or al,03h
02d1: out 61h,al
The INT 08h handler at 0x0c72 mixes four sample channels into one byte.
Each channel has a current sample pointer, an end pointer, loop information,
a step value, and a volume byte. The repeated pattern is:
0c87: mov bx,[19cch] ; channel 0 current offset
0c8b: cmp bx,[19c5h] ; end?
0c91: cmp word [19c9h],ffffh
0c98: mov bx,[19c7h] ; loop restart if needed
0ca0: mov ax,[19ceh] ; fractional step
0ca3: add [19d2h],ax
0ca7: adc bx,[19d0h] ; integer step
0cab: mov [19cch],bx
0caf: mov es,[19c3h] ; sample segment
0cb3: mov al,es:[bx]
0cb6: imul byte [19cbh] ; sample * volume
0cbe: add dx,ax ; accumulate mix
The same structure repeats for channel 1, channel 2, and channel 3. After the four channels, it converts the high mixed byte to unsigned output:
0d6b: mov bx,1b27h
0d6e: mov al,dh
0d70: add al,80h
0d72: mov dx,[13dbh] ; selected device port
The output branch handles the device:
0d76: cmp dx,0378h
0d7a: je direct_lpt
0d7c: cmp dx,0042h
0d7f: je pc_speaker_table
0d81: mov cl,al ; Sound Blaster/DSP path
0d83: in al,dx
0d84: rol al,1
0d86: jb 0d83h ; wait until DSP can accept byte
0d88: mov al,cl
0d8d: out dx,al
For the PC speaker path, XLAT maps the mixed byte through a table at 0x1b27
before the OUT. For LPT1, the mixed byte goes directly to 0378h.
The result is a small 4-channel unsigned 8-bit software mixer, built for the same kind of timer-driven PCM output used by many 1991-1992 PC demos.
Text/Glyph Mask Path
The large pink text fragments in the runtime are not just pre-rendered full
screens. The routine at 0x1eed builds a masked glyph/color window from a bit
table.
Key steps:
1eed: push cs
1eee: pop es
1eef: mov di,1fa8h
1ef2: mov si,1fbah
1ef5: mov cx,0171h
1ef8: rep movsw ; copy/prepare palette window
1efa: mov bl,[bp+0]
1efd: xor bl,7bh
1f02: shl bx,3
1f05: add bx,23feh ; 8-byte glyph/mask row
1f09: mov di,228ah
1f0c: mov si,229ch
Then it unrolls a six-bit row decision. For each mask byte it rotates left; if the carry is clear it writes three zero bytes, otherwise it copies three bytes from the source color pattern:
1f11: rol byte [bx],1
1f13: jb visible
1f15: stosw ; zero, zero
1f16: stosb ; zero
1f17: add si,3
...
visible:
1f1c: movsw
1f1d: movsb
That explains the captured pink block-letter pieces: the renderer is building small three-byte RGB/DAC-like cells through a mask, then uploading a palette window:
1f7a: mov si,1fa8h
1f7d: mov al,04h
1f7f: mov dx,03c8h
1f82: out dx,al
1f83: mov cx,02f4h
1f86: inc dx
1f87: rep outsb
The routine also periodically subtracts or adds to bytes at 0x229c, making
the color window drift. So the text effect combines a mask lookup with palette
animation, instead of redrawing huge shaded letters as full bitmap frames.
Moving Speck/Background Helper
The helper at 0x0b4c updates 24 tiny moving entries. The table at 0x0b98
uses five bytes per entry:
0b50: mov si,0b98h
0b53: mov dx,1680h
0b56: mov cx,18h ; 24 entries
0b59: mov al,[si] ; speed/color-like byte
0b5b: cbw
0b5c: add [si+1],ax ; advance position
0b5f: cmp dx,[si+1]
0b64: sub word [si+1],0140h ; wrap by one screen row
...
0b72: mov bx,[si+3] ; old pixel position
0b75: cmp byte es:[bx],04h
0b7b: mov byte es:[bx],00h ; erase if low color
0b7f: mov bx,[si+1]
0b82: cmp byte es:[bx],00h
0b88: mov es:[bx],al ; plot if empty
0b8e: add si,5
0b91: add dx,0a00h
0b95: loop 0b59h
This is a small dirty-plot particle helper. It advances position, erases the old point under a color threshold, plots the new one only over zero pixels, and stores the new position back into the entry.
Main Visual Structure
The main part sequence is a series of asset loads, palette shifts, CRTC tweaks, and small per-frame helpers. Examples:
0x04besets DAC index zero and uploads 144 bytes from0x2907; this is a partial palette reveal before the first effect.0x04cbwaits for retrace, writes CRTC register0x0d, calls the moving palette helper, and loops, creating a horizontal pan/reveal.0x0526and0x0577repeatedly call the line helper at0x2390with changingx/yvalues, drawing wipe lines in from the screen edges.0x05d7loadsLOADING.ABA, then repeatedly calls a small palette/update helper to animate it.0x060dloadsCYCLING.ABA;0x0619loadsBACKRND.ABAto a later segment (AX=A104h) and sets a row-offset word to0x0140.
The demo's visual character comes from using those ingredients together: compressed line-skipping bitmaps, CRTC start/pan changes, palette rotation, and small generated overlays. It is not a 3D polygon demo. The best inner loops are the asset RLE decoder, palette shifters, text-mask builder, and timer PCM mixer.