Analysis model: gpt-5.5 xhigh

Montezuma's Revenge Graffiti by Software Pirates Inc. - Technical Dissection

Montezumas Revenge Graffiti is a source-limited MS-DOS cracktro / graffiti screen credited by Demozoo to Software Pirates Inc. Demozoo lists the release date as 15 May 1986 and classifies it as an MS-Dos Cracktro. The visible public frame itself says Software Pirates, INC., MONTEZUMA'S REVENGE, pirated 1991, and Press <return> for Menu Screen.

Release year: 1986 in Demozoo metadata; the visible screen carries a conflicting 1991 self-date.

This pass is source-limited. Demozoo provides one public screenshot, but the original executable/archive could not be downloaded from this environment. Defacto2's group page and JSON API endpoints returned HTTP 403 during this pass, so the code sections below are bounded implementation models from the public frame, not recovered disassembly.

The public screenshot contains no phone number, BBS number, street address, or other private contact text.

Sources Checked

Defacto2 probe results:

https://defacto2.net/g/software-pirates-inc                                      403 text/html; charset=UTF-8 5319 bytes
https://defacto2.net/api/v1/artifacts?search=Montezumas%20Revenge%20Graffiti    403 text/html; charset=UTF-8 5464 bytes
https://defacto2.net/api/v1/artifacts?filename=MONTEZUMA                        403 text/html; charset=UTF-8 5395 bytes

Local evidence hashes:

dc228b9e422bc6972bcdf346698e31e1dcbefad5a50c7d0b457db064e907a68b  montezumas-revenge-graffiti-demozoo.png
16c776ccb8246a29ba7063136bbd99109f6c12289210b68666ae297348ee98d3  montezumas-revenge-graffiti-logical-320x200.png
aee57d062269e26b76b5a517425483a1a8c477454ec7f929e584853bdcf0fc7a  montezumas-revenge-graffiti-layout-map.png

Public Frame

Montezuma's Revenge Graffiti public Demozoo screenshot

The public screenshot is 640x400 and contains exactly four RGB colours:

colour   pixels   public bbox
#000000 249340    x=0..639,   y=0..399
#ffff55   3044    x=80..555,  y=192..381
#ff5555   2052    x=160..461, y=160..173
#55ff55   1564    x=144..487, y=80..95

Every public 2x2 block is internally constant, so the screenshot reduces exactly to a logical 320x200 frame:

public size:  640x400
logical size: 320x200
2x2 mismatches: 0 of 64000

Montezuma's Revenge Graffiti reduced 320x200 logical frame

The logical frame has the same four colours:

colour   pixels   logical bbox
#000000  62335    x=0..319,   y=0..199
#ffff55    761    x=40..277,  y=96..190
#ff5555    513    x=80..230,  y=80..86
#55ff55    391    x=72..243,  y=40..47

The colour set is the classic high-intensity CGA palette-0 shape:

pixel code  likely colour
00          black
01          bright green
10          bright red
11          yellow

That makes the likely display family a 320x200, two-bit-per-pixel CGA mode. The screenshot contains no antialiasing and no intermediate capture colours, which is why the exact 2x reduction works cleanly.

Date Discrepancy

The metadata and the frame disagree:

Demozoo:       Released 15 May 1986
Visible frame: pirated 1991

Without the binary, this cannot be resolved honestly. The useful possibilities are:

The technical analysis below therefore treats the visible 1991 as real frame evidence, while keeping the release-year field tied to the available Demozoo metadata.

Layout Map

Montezuma's Revenge Graffiti layout map

Front-page media note: the card uses the public Demozoo screenshot, the exact logical reduction, and the generated layout map directly. I did not make a GIF for this pass because the available evidence is one static public frame, not a timed playback sequence.

The logical screen is almost entirely black, with four horizontal text bands:

logical x/y        colour   region
 72..243,  40..47  green    Software Pirates, INC.
 80..230,  80..86  red      MONTEZUMA'S REVENGE
104..209,  96..103 yellow   pirated 1991
 40..277, 184..190 yellow   Press <return> for Menu Screen

The coordinates are not arbitrary. All left edges are multiples of 8, and the text rows are aligned to 8-pixel font rows:

green line x=72  -> column 9
red line   x=80  -> column 10
yellow mid x=104 -> column 13
bottom     x=40  -> column 5

text baselines start at y=40, 80, 96, and 184

This points to a graphics-mode text blitter using an 8x8 bitmap font rather than a hand-drawn title page. It could be a BIOS graphics text call, but the small amount of text and the clean colour changes are more naturally explained by a direct ROM-font or bundled-font glyph loop.

CGA Memory Model

For a 320x200 CGA graphics page:

segment:     B800h
page size:   16,384 bytes
stride:      80 bytes per logical scanline
pixel size:  2 bits
byte shape:  four pixels per byte
banks:       even scanlines at B800:0000h
             odd  scanlines at B800:2000h

The byte address for a pixel group is:

offset = (y & 1) * 0x2000 + (y >> 1) * 80 + (x >> 2)
shift  = (3 - (x & 3)) * 2
mask   = 3 << shift

For this frame, all text starts on x coordinates divisible by 8, so each glyph row begins on a byte boundary. One 8x8 character row consumes exactly two CGA bytes:

8 pixels / 4 pixels per byte = 2 bytes

That is why the glyph writer can avoid read/modify/write at the left and right edges of every character. If it paints on a black page, it can store two bytes per glyph row directly.

Useful text band addresses:

band                    x/y       first CGA byte offset
green header            72,40     (40 >> 1) * 80 + (72 >> 2) = 0652h
red title               80,80     (80 >> 1) * 80 + (80 >> 2) = 0C94h
yellow self-date       104,96     (96 >> 1) * 80 + (104 >> 2) = 0F1Ah
yellow return prompt    40,184    (184 >> 1) * 80 + (40 >> 2) = 1CCAh

All four starts are on even scanlines, so the first row of each text band lives in the even bank. The next visual row of the glyph jumps to the odd bank at +2000h.

Likely Runtime Shape

Variant A: Clear Screen, Then Draw Four Strings

The smallest structured implementation is:

start:
    mov  ax,0004h            ; CGA 320x200 graphics mode
    int  10h

    ; Select high-intensity palette 0. Exact register path varies by code base.
    ; The visible colours are black, bright green, bright red, yellow.

    mov  ax,0b800h
    mov  es,ax
    xor  di,di
    xor  ax,ax
    mov  cx,2000h            ; 16384 bytes / 2
    rep  stosw               ; black page

    ; draw_string(x=72,  y=40,  colour=1, "Software Pirates, INC.")
    ; draw_string(x=80,  y=80,  colour=2, "MONTEZUMA'S REVENGE")
    ; draw_string(x=104, y=96,  colour=3, "pirated   1991")
    ; draw_string(x=40,  y=184, colour=3, "Press <return> for Menu Screen")

The entire visible page can be produced by about sixty glyphs, far smaller than a raw 16 KiB page image. A raw page copy is possible, but it is an unattractive encoding for this artifact.

Variant B: BIOS Graphics Text

BIOS int 10h can draw text in graphics modes using the ROM font. A compact program might position the cursor and print strings through teletype or string write calls.

That path is plausible because:

It is less attractive if the cracktro wants predictable exact colour/palette behaviour on early clones. A direct B800h glyph loop is more common in size-conscious loaders because it avoids BIOS variation and gives exact control over CGA byte layout.

Inner Loop: 8x8 Font To CGA Bytes

Assume an 8x8 one-bit font where each row is one byte. For each set font bit, the output pixel gets a two-bit CGA colour code:

font row bits: b7 b6 b5 b4 b3 b2 b1 b0
CGA byte 0:    b7 b6 b5 b4 -> pixels x+0..x+3
CGA byte 1:    b3 b2 b1 b0 -> pixels x+4..x+7

The expansion for one nibble is:

expand4(bits, colour):
    out = 0
    if bits & 8: out |= colour << 6
    if bits & 4: out |= colour << 4
    if bits & 2: out |= colour << 2
    if bits & 1: out |= colour
    return out

The direct version of the string routine is:

for each character ch in string:
    fontp = font + ch * 8
    dst0  = cga_offset(x, y)

    for row in 0..7:
        bits = fontp[row]
        dst  = dst0 + cga_row_delta(row)

        dst[0] = expand4(bits >> 4, colour)
        dst[1] = expand4(bits & 15, colour)

    x += 8

In 8088-style assembly, the hot row loop can be implemented with a tiny lookup table instead of testing eight bits for every row:

; Input:
;   DS:SI -> 8 font bytes for one character
;   ES:DI -> destination byte for character's row 0
;   BX    -> expansion table for current colour, 512 bytes:
;            table[bits*2+0] = high four pixels as CGA byte
;            table[bits*2+1] = low  four pixels as CGA byte

draw_char:
    mov  bp,8

.row:
    lodsb
    xor  ah,ah
    shl  ax,1              ; two bytes per expanded font row
    mov  dx,[bx+ax]        ; DL/DH are the two CGA bytes
    mov  es:[di],dx

    ; Advance to next visual scanline in CGA's split memory.
    test di,2000h
    jz   .to_odd
    sub  di,2000h-80       ; odd row -> next even row
    jmp  .next
.to_odd:
    add  di,2000h          ; even row -> next odd row
.next:
    dec  bp
    jnz  .row
    ret

The important detail is the row advance. In a linear framebuffer the next scanline would be +80. In CGA mode 4 the adjacent display line is in the other half of the 16 KiB page, so the loop alternates:

even y  -> odd y+1:  +2000h
odd y   -> even y+1: -2000h + 80

That is the classic CGA split-bank trap. Any renderer that ignores it produces two interleaved half-height images instead of the observed text rows.

Inner Loop: Expansion Table Builder

If the program wants three text colours, it can either build three tables or rebuild one table before each colour run. A compact builder for one colour is:

for bits in 0..255:
    hi = bits >> 4
    lo = bits & 15
    table[bits*2 + 0] = expand4(hi, colour)
    table[bits*2 + 1] = expand4(lo, colour)

The cost is 512 bytes per colour table. For three colours:

green table:  512 bytes
red table:    512 bytes
yellow table: 512 bytes
total:       1536 bytes

That is too much for a tiny cracktro if the font and strings are already small. A more size-oriented version would avoid the tables and spend cycles expanding bits directly:

expand_nibble:
    ; AL = four font bits in low nibble, BL = two-bit colour code
    xor  ah,ah
    test al,8
    jz   @f
    mov  ah,bl
    shl  ah,6
@@:
    test al,4
    jz   @f
    mov  dl,bl
    shl  dl,4
    or   ah,dl
@@:
    test al,2
    jz   @f
    mov  dl,bl
    shl  dl,2
    or   ah,dl
@@:
    test al,1
    jz   @f
    or   ah,bl
@@:
    ret

For this screen, the table-free path is plausible because only roughly sixty characters are drawn. The CPU cost is invisible to the user: the page is static and rendered once.

Bottom Prompt And Keyboard Gate

The bottom line says Press <return> for Menu Screen, so the display path is probably followed by a simple keyboard loop:

wait_return:
    xor  ah,ah
    int  16h               ; wait for key
    cmp  al,0dh
    jne  wait_return

After that the cracktro can hand control to the game menu:

    ; restore vectors / state if the loader changed them
    ; jump to original menu entry or execute original game file

The prompt is useful evidence because this is not merely a title card. It is a gate in front of a still-accessible game/menu path, like the F1/F2 command area in Battlezone Graffiti, but reduced to one return-key continuation.

Raw Page Alternative

A raw-page renderer would be trivial:

show_raw_page:
    mov  ax,0004h
    int  10h
    mov  ax,0b800h
    mov  es,ax
    mov  ds,[page_segment]
    xor  si,si
    xor  di,di
    mov  cx,2000h
    rep  movsw

The problem is data size:

raw CGA page:  16,384 bytes
visible text:  about 60 characters * 8 font bytes = about 480 font-row bytes
strings:       well under 100 bytes

Even if the code carries its own font subset, string rendering is smaller. If it uses BIOS or ROM font access, the payload becomes smaller still. For a page this sparse, a raw copy only makes sense if the crack was embedded in a larger loader where simplicity mattered more than size, or if the page was stored in a general compressor shared with other assets.

Why This Still Belongs In The Early PC Timeline

Montezuma's Revenge Graffiti is visually plain, but technically it is a useful data point in the Software Pirates Inc. line:

The interesting part is not a heavy effect; it is the economy of the artifact. The whole visible layer can be explained by:

  1. int 10h mode setup and CGA palette selection.
  2. A black-page clear.
  3. An 8x8 graphics-font loop that knows CGA split-bank row stepping.
  4. A BIOS keyboard wait for return.
  5. A handoff to the underlying game/menu screen.

That pattern is exactly the bridge between simple cracked-game splash pages and later PC intros: direct video memory, a tiny renderer, a key gate, and a group signature placed over a commercial-game entry path.