Analysis model: gpt-5.5 xhigh
Evolution Graffiti by Spartacus - Technical Dissection
Evolution Graffiti is a 1986 MS-DOS cracktro / graffiti screen by Spartacus.
Demozoo lists it as released on 1 March 1986 for MS-DOS, with Spartacus
credited for the crack code.
Release year: 1986
This is a source-limited entry. I have the public Demozoo production page, the three public 320x200 screenshots referenced by that page, and the external Defacto2 record, but I do not have a verified executable for this pass. The analysis therefore does not claim exact offsets, labels, bytes, or a recovered timeline. It describes what the frames prove, then gives bounded 8088/CGA loop models that fit those frames.
The later public screenshots contain old contact-style text. The article uses one safe title frame and one redacted later frame; unredacted contact-bearing frames were kept out of the public article assets.
Sources
- Demozoo production page: https://demozoo.org/productions/368774/
- Demozoo public title screenshot: https://media.demozoo.org/screens/o/01/11/40d9.358015.png
- Defacto2 external record: https://defacto2.net/f/b726e8e
Two later Demozoo carousel frames were reviewed for the redacted derivative image used below. They are not linked directly here because they contain old contact-style text.
Demozoo records:
Title: Evolution Graffiti
Group: Spartacus
Type: Cracktro
Platform: MS-Dos
Released: 1 March 1986
Credit: Spartacus - Code (crack)
Screenshot hashes used while preparing the public-safe images:
b422944cbd23169400ec4a6725d3a03b2eae995bc07bd55e3cdffe705236e83b images/evolution-graffiti-demozoo-01.png
158478f37daf4653ff2b07e1cec1ea79b54acbe1b875f45c4c151bc0030053e1 images/evolution-graffiti-demozoo-03-redacted.png
0cb336a6204d3dab29ef00b6d8cf7b675405e5a063338f257b71ced16ea7e811 images/evolution-graffiti-layout-map.png
Visual References
| Time | Image | Notes |
|---|---|---|
00:00.000 public still |
![]() |
First public Demozoo frame. Black background, orange SPARTACUS / PRESENTS text, and a green/orange oversized EVOLUT... logo region. |
00:00.000 later public still |
![]() |
Later public Demozoo frame with old contact-style text masked. The same composition has shifted to a grey/cyan palette and the full EVOLUTION word is visible. |

What The Frames Prove
The screenshots are 320 by 200 pixels. That is the native CGA low-resolution graphics footprint, and the composition fits a direct 320x200 bitmap rather than an 80x25 text page:
- the huge
EVOLUTIONletters occupy irregular pixel widths and do not align to an 8x8 or 9x16 text grid; - the small
SPARTACUSandPRESENTSlines are centered as bitmap text or graphics-mode font output; - the background is a true full-screen black field;
- the visible colours match a small indexed palette rather than per-cell text attributes.
The important consequence is that the hot loops are almost certainly CGA graphics memory loops, not B800h character/attribute word loops.
The three public frames also prove a state change:
early frame: orange/green palette, large word not fully revealed
later frame: grey/cyan palette, full word visible, bottom line present
That can be produced in two plausible ways:
- Draw or copy different prebuilt 320x200 pages.
- Keep the same 2-bit pixel indices and change the CGA palette latch while a reveal loop fills more of the large logo.
The second model is more code-economical for 1986 and explains why the same layout survives the colour change.
Visual-To-Code Concordance
This concordance is bounded to the public-safe images and the CGA memory model. No executable was acquired for this pass, so the links below describe the visible renderer classes, not recovered function addresses.
The first Demozoo still proves a graphics-mode page, not a text-mode splash:
visible image: evolution-graffiti-demozoo-01.png
frame size: 320x200
display model: CGA mode 04h-style 2bpp graphics
memory target: B800h split odd/even scanline banks
proved behavior: black clear, centered bitmap headings, partial logo state
bounded inference: full-page copy, span table, or clipped logo reveal
The oversized EVOLUT... region is the important clue. It has broad irregular
letter strokes and pixel-level edges, so the hot path cannot be a
character/attribute writer. The useful primitive is a CGA byte/span writer:
build a row base for the current scanline, align to four-pixel byte groups,
write 00h/55h/AAh/FFh runs for solid colour, and patch the unaligned ends
with 2-bit masks.
The later redacted frame ties the same layout to a state update:
visible image: evolution-graffiti-demozoo-03-redacted.png
same geometry: heading region + giant logo + lower note region
changed evidence: full word visible, different apparent palette,
additional bottom text region
proved behavior: either page/state switch or reveal completion
bounded inference: CGA palette latch write plus bottom-line draw is cheaper
than repainting a separate full-colour page
Because the geometry remains stable while the apparent colours change, the most economical 1986 reading is stable 2-bit pixel indices plus a CGA colour select update. The palette step is technically tiny:
out 03D9h, first_colour_select
draw/reveal logo
out 03D9h, later_colour_select
draw lower line or copy lower text band
That does not prove the exact register values, but it explains why the visible composition can change colour without any large bitmap rewrite.
The generated layout map is therefore a renderer map, not a recovered call graph:
visible image: evolution-graffiti-layout-map.png
screen regions: headings, giant logo, lower note/contact area
core loops: clear/copy, CGA row-base calculation, span writer,
optional tile/strip reveal, palette latch, delay/key gate
not proved here: archive layout, exact reveal order, exact timing, sound
The strongest source-limited bridge is:
public title still
-> mode set + black clear + bitmap-font headings + partial CGA logo reveal
later redacted still
-> same page geometry + completed/replaced logo state + palette remap
+ lower text-region writer/copy
layout map
-> split-bank B800h addressing and span/tile loops needed by both frames
This keeps the analysis appropriately small for an early graffiti screen while still identifying the real technical content: packed 2bpp CGA addressing, wide-span writes, palette-state changes, and a simple reveal or page-state transition.
CGA Memory Model
The most likely display mode is 320x200 4-colour CGA graphics. In that mode, one byte contains four horizontal pixels, two bits per pixel:
bit pair 7..6 -> pixel x + 0
bit pair 5..4 -> pixel x + 1
bit pair 3..2 -> pixel x + 2
bit pair 1..0 -> pixel x + 3
Rows are split between two 8 KiB banks:
even scanlines: B800:0000 + (y / 2) * 80 + (x / 4)
odd scanlines: B800:2000 + (y / 2) * 80 + (x / 4)
The address formula is:
offset = ((y & 1) * 0x2000) + ((y >> 1) * 80) + (x >> 2)
shift = 6 - ((x & 3) * 2)
mask = 0x03 << shift
value = (colour & 3) << shift
That odd/even layout shapes every useful inner loop. Any full-screen operation has to either visit all even rows and then all odd rows, or recompute the bank for each scanline.
Screen Parts
The screen can be divided into four functional regions:
y 18.. 55 small centered "SPARTACUS" heading
y 57.. 78 small centered "PRESENTS" heading
y 82..166 oversized "EVOLUTION" logo
y 172..194 optional bottom note / contact-style line, redacted here
The top headings and the giant logo are probably not separate "effects" in the modern demo sense. They are display-list items on a static CGA page:
- clear background;
- draw small heading string;
- draw second small string;
- reveal or copy the large logo;
- optionally draw the bottom line;
- wait, exit, or continue to the cracked program.
The word "effect" here is mostly the reveal and palette state, not a per-frame 3D or procedural renderer.
Mode Setup
A minimal direct-CGA setup is:
mov ax,0004h ; CGA 320x200 graphics, 4 colours
int 10h
mov dx,03D9h ; CGA colour select register
mov al,palette
out dx,al
Many 1986 programs use BIOS for the mode switch and direct port writes for the colour latch. A pure BIOS path is also possible:
mov ax,0004h
int 10h
mov ah,0Bh ; set background / palette
mov bh,00h
mov bl,palette_id
int 10h
The direct port path is the better fit for a cracktro because changing the palette during a reveal is a one-byte output:
mov dx,03D9h
mov al,next_cga_colour_select
out dx,al
The visible orange/green and grey/cyan states do not require repainting the logo if the 2-bit pixels are stable. Changing the CGA palette changes how those indices are interpreted.
Full-Screen Clear Loop
The displayed CGA area is 16000 bytes: 8000 bytes for even rows and 8000 bytes for odd rows. The physical banks are 8192 bytes apart, so each displayed bank also leaves a small unused tail.
The direct clear is two REP STOSW runs:
mov ax,0B800h
mov es,ax
xor ax,ax
cld
xor di,di ; even-row bank
mov cx,4000 ; 8000 bytes
rep stosw
mov di,2000h ; odd-row bank
mov cx,4000
rep stosw
Expanded inner loop:
for i in 0..3999:
B800:0000 + i*2 = 0000h
for i in 0..3999:
B800:2000 + i*2 = 0000h
That writes colour index 0 to every pixel. The frames have a black background with no texture or dither, so a zero fill is sufficient.
Whole-Page Copy Loop
If the art was stored as a complete packed page in system memory, the display copy would be similarly simple:
mov ax,0B800h
mov es,ax
mov ds,art_segment
cld
mov si,even_page
xor di,di
mov cx,4000
rep movsw
mov si,odd_page
mov di,2000h
mov cx,4000
rep movsw
The real cost is 16000 bytes copied. On an 8088 that is not free, but for a static cracktro title it is still acceptable. If the program only shows one or two still states, storing full pages is simpler than building a complicated shape renderer.
The drawback is size. Three full CGA pages would be about 48 KiB before compression. A cracktro of this age is more likely to store logo masks, spans, or simple RLE blocks unless the host file already has room.
Packed Pixel Plot
The smallest general pixel writer for CGA 2bpp graphics looks like this in logic:
offset = ((y & 1) << 13) + ((y >> 1) * 80) + (x >> 2)
shift = 6 - 2 * (x & 3)
mask = 3 << shift
old = B800[offset]
new = (old & ~mask) | ((colour & 3) << shift)
B800[offset] = new
In assembly, the multiplication by 80 is usually avoided inside the deepest loop. The caller prepares a row pointer:
; BX = row base, x advances horizontally
mov di,bx
mov cl,x_mod_shift
mov al,es:[di]
and al,not mask
or al,colour_shifted
mov es:[di],al
This is too slow for large filled areas if called per pixel. It is useful for
edges and small glyph dots, but not for the body of the EVOLUTION logo.
Horizontal Span Inner Loop
The giant logo is made from large filled letter strokes. A span renderer is a better fit than per-pixel plotting.
For a solid horizontal run from x0 to x1 at scanline y:
colour_byte = colour * 0x55
while x <= x1 and (x & 3) != 0:
write one masked 2-bit pixel
x += 1
while x + 3 <= x1:
B800[row_base + x/4] = colour_byte
x += 4
while x <= x1:
write one masked 2-bit pixel
x += 1
The hot center loop is one byte store per four pixels:
; DI points to first fully aligned byte
; AL is 00h, 55h, AAh, or FFh for colour 0,1,2,3
; CX is aligned byte count
rep stosb
That is the loop that matters for the heavy logo area. Most of the scanline is
handled by REP STOSB; only the two unaligned ends need read/modify/write.
The required tables are tiny:
pair_mask[4] = C0h, 30h, 0Ch, 03h
pair_shift[4] = 6, 4, 2, 0
solid[4] = 00h, 55h, AAh, FFh
Logo Stroke Representation
The source data for the large logo could be stored as per-scanline spans:
record:
y
count
repeated count times:
x0
x1
colour
The renderer is then:
for record in logo_records:
row_base = cga_row_base(record.y)
for each span in record:
draw_solid_span(row_base, span.x0, span.x1, span.colour)
For the EVOLUTION logo this representation is efficient because the letters
are broad and mostly horizontal on each scanline. It also makes a reveal easy:
clip every span against a moving reveal column.
visible_x1 = reveal_column
clipped_x1 = min(span.x1, visible_x1)
if clipped_x1 >= span.x0:
draw_solid_span(row_base, span.x0, clipped_x1, span.colour)
That exactly fits the difference between a partial EVOLUT... public still and
a later full EVOLUTION still, without requiring separate art for every
intermediate state.
Left-To-Right Reveal Loop
A likely reveal loop is:
reveal_column = 0
while reveal_column < 320:
wait_for_tick_or_retrace()
draw_logo_clipped_to(reveal_column)
reveal_column += step
A more efficient version does not redraw the whole clipped logo every time. It only draws the newly exposed vertical strip:
old_column = reveal_column
reveal_column += step
for each logo span:
x0 = max(span.x0, old_column + 1)
x1 = min(span.x1, reveal_column)
if x0 <= x1:
draw_solid_span(row_base(span.y), x0, x1, span.colour)
This turns each frame into "draw the next band" rather than "redraw everything seen so far". In 1986 terms, this matters because the 8088 bus is slow and CGA video memory writes are expensive.
If step is 4 pixels, most writes are byte-aligned. If step is 8 or 16
pixels, the inner loop becomes almost entirely REP STOSB.
Tile Reveal Variant
The public frames do not prove that the reveal is column-perfect. It could also reveal 8x8 or 8x4 tiles. A tile representation would look like:
for tile_index in reveal_order:
source = logo_bitmap + tile_index * tile_size
target = cga_tile_address(tile_x, tile_y)
copy_tile(source, target)
For an 8x8 tile in 320x200 CGA, each row is 2 bytes wide:
8 pixels / 4 pixels per byte = 2 bytes per row
8 rows = 16 bytes per tile
The tile copy must still hop between CGA banks for odd and even scanlines:
for row in 0..7:
y = tile_y + row
dst = ((y & 1) << 13) + ((y >> 1) * 80) + (tile_x >> 2)
copy 2 bytes
This is more pointer-heavy than a span reveal, but it allows a small masked bitmap logo and a custom reveal order.
Small Heading Text
The SPARTACUS and PRESENTS headings can be drawn in three ranked ways:
- Prebaked as part of a full-page or logo bitmap.
- Drawn with a small custom bitmap font.
- Drawn by BIOS graphics text after setting CGA mode.
The custom-font loop is the most typical cracktro compromise. A one-bit 8x8 font is expanded into 2bpp CGA pixels:
for each character in string:
glyph = font[character]
for gy in 0..7:
bits = glyph[gy]
y = base_y + gy
row_base = cga_row_base(y)
for gx in 0..7:
if bits & (0x80 >> gx):
plot_or_span_scaled_pixel(base_x + gx, row_base, colour)
base_x += glyph_advance
If the font is unscaled, the inner loop can expand one glyph byte through a lookup table:
expanded[256][2] -> two CGA bytes for eight monochrome pixels
Then each glyph row becomes:
lodsb ; AL = one 8-pixel font row
xlat ; or table lookup to two bytes
mov es:[di],ax ; store two CGA bytes
With a two-byte expansion table, an eight-character word costs only 16 bytes of video writes per scanline.
Centering Text
The headings are centered, so the draw routine probably computes:
pixel_width = len(text) * glyph_advance
x = (320 - pixel_width) / 2
The 8088-friendly version avoids division by using a right shift:
mov ax,320
sub ax,pixel_width
shr ax,1
For a fixed title string, the x coordinate may simply be a constant in the display list:
draw_string x=112, y=24, colour=2, "SPARTACUS"
draw_string x=124, y=57, colour=2, "PRESENTS"
The visible spacing does not prove dynamic centering. Constant coordinates are more likely if the intro only draws this one page.
Palette Remap
CGA 320x200 graphics has only 2-bit pixel indices. The colour select register chooses how those indices appear. A palette change can be done without touching video memory:
mov dx,03D9h
mov al,orange_green_state
out dx,al
; later
mov al,cyan_white_state
out dx,al
The practical inner loop is not a loop at all; it is a single I/O write. This is why palette remapping was attractive on CGA even before VGA-style DAC cycling existed.
If the frames are from separate program states, the order could be:
draw logo using stable indices
wait
change palette latch
draw bottom line
wait
Because the public stills show the same composition with different colours, palette state is a stronger explanation than repainting the entire screen in different literal colour indices.
Retrace Wait
To keep the reveal stable, the program can wait for vertical retrace through the CGA status port:
mov dx,03DAh
wait_out:
in al,dx
test al,08h
jnz wait_out
wait_in:
in al,dx
test al,08h
jz wait_in
That waits for a clean frame boundary:
leave any current retrace
wait until the next retrace begins
perform one reveal or palette step
The screenshots cannot prove retrace pacing, but it is the natural way to make a CGA reveal look deliberate instead of tearing through a batch of writes as fast as the CPU allows.
Keyboard Or Delay Gate
A cracktro page usually does one of three things after the title:
- wait for a key;
- delay for a fixed number of ticks;
- wait briefly, then chain to the cracked program.
The keyboard-poll loop is:
mov ah,01h
int 16h
jz no_key
mov ah,00h
int 16h
jmp continue_or_exit
The BIOS tick delay is:
mov ah,00h
int 1Ah ; CX:DX = timer ticks since midnight
add dx,delay_ticks
wait_tick:
mov ah,00h
int 1Ah
cmp dx,target_dx
jb wait_tick
For 1986, BIOS timing is adequate. A custom PIT interrupt would be unnecessary for a single static graffiti page.
Probable Runtime Order
A compact 1986 implementation could be:
1. Save original video mode if the author cared about clean exit.
2. Set CGA 320x200 4-colour mode.
3. Select the first palette state.
4. Clear B800h graphics memory.
5. Draw the small heading strings.
6. Reveal the large `EVOLUTION` logo with clipped spans or tiles.
7. Change palette state for the later frame.
8. Draw the bottom note line.
9. Wait for key or timeout.
10. Exit, return to DOS, or continue to the cracked program.
The three public stills support steps 2 through 8. Steps 1, 9, and 10 are standard cracktro control flow but not directly visible in the screenshots.
What Is Not Proven
The public evidence does not prove:
- the file format of the distributed executable;
- whether the frames are generated live, copied from packed pages, or restored from a compressed image;
- the exact timing of the reveal;
- whether there is music or sound;
- whether the screen chains into a cracked game loader;
- exact machine target beyond MS-DOS-era CGA compatibility.
The safe conclusion is narrower: Evolution Graffiti is an early MS-DOS
CGA-bitmap cracktro page whose visible complexity is in packed 2bpp screen
memory, palette state, and probably a simple reveal loop rather than in later
demo-style 3D, plasma, or sound-synchronized part scheduling.

