Files
disasm/_wip/Golf (JU)/copilot/COMPRESSED_PARSER_AUDIT.md
T
2026-10-18 16:47:03 -04:00

5.3 KiB

Pass 1 compressed and packed-data parser audit

Summary

Four distinct packed-data consumers are confirmed. Only one is a general run-length decoder. The others are pointer-selected sprite/object lists or cell-code interpreters.

1. $CBEA: pointer-selected OAM sprite-list parser

Inputs:

  • $15 selects a sprite-list variant.
  • $16 == 4 selects the pointer tables at $CC3E/$CC44; other values select $CC24/$CC31.
  • $33 is added to the generated OAM X coordinate.
  • X is the destination OAM-shadow offset.

Format:

  • One source byte per sprite.
  • $FF terminates the list early.
  • A hard limit of $15 (21) source entries prevents runaway parsing.
  • The source byte is both the CHR tile index and the index into coordinate tables $CC4A and $CC5F.

Output record at $0200,X:

  • byte 0: $88 + table_CC5F[tile] (Y)
  • byte 1: source byte (tile)
  • byte 2: $00 (attributes)
  • byte 3: $33 + table_CC4A[tile] (X)

This is a compact sprite-layout parser, not RLE compression.

2. $DD78-$DE39: primary course-map RLE decoder

Pointer selection:

  • $2B is the zero-based hole index.
  • $E6C3,Y and $E6D5,Y form the source pointer in $04/$05.
  • Destination begins at $0300 through $06/$07.

Header:

  • First three source bytes are copied to $19-$1B.
  • Source pointer then advances by three bytes.

Commands:

  • $FF: end primary stream.
  • $00-$0F: emit header byte 0, (low_nibble + 1) times.
  • $10-$1F: emit header byte 1, (low_nibble + 1) times.
  • $20-$2F: emit header byte 2, (low_nibble + 1) times.
  • $A0-$AF: emit (low_nibble + 1) values cycling through $AB-$AD using state byte $8D.
  • $30-$9F and $B0-$FE: emit the command byte literally.

Output is divided into 16-byte units. After each unit, $04/$05 advances by $1C consumed source bytes and $06/$07 advances by $10 destination bytes.

Hole 2 / Hole 3 length correction

The decoder does not enforce a 480-byte limit. RAM $0300-$04FF is cleared before decoding, providing a 512-byte destination area. Therefore the measured outputs are valid:

  • Hole 2: 482 bytes
  • Hole 3: 483 bytes
  • Other currently measured streams: 480 bytes

The earlier 480-byte expectation came from the common case of 30 16-byte units, not from a decoder bound. Both longer streams remain within the 512-byte course buffer and do not indicate malformed pointers or an incorrect opcode model.

3. $DE3B-$DE5D: optional auxiliary block parser

After the primary $FF, the decoder advances the source pointer past the primary stream and examines the next byte.

  • $55 is a sentinel meaning the optional block is absent.
  • Otherwise, records are copied in four-byte groups from the source into $0104+.
  • X begins at $04 and increases by four per record.
  • After each group, $D4F2 advances the corresponding destination/object bookkeeping.
  • Parsing ends when X reaches $44, allowing at most 16 four-byte groups.
  • $04/$05 is then advanced by the number of consumed source bytes.

This is a bounded fixed-record block, not a terminator-scanned RLE stream.

4. $DE5E-$DF52: per-hole OAM/object package parser

The parser begins at the source position following the optional auxiliary block.

Fixed setup record

The first bytes initialize OAM-shadow entries beginning at $0298 and setup fields $65-$66, $57, $1A-$1B. Tables at $E37D-$E3DE expand one compact source selector into multiple sprite records.

Variable object list

At $DF03, each object record begins with:

  • byte 0: object/shape selector, or $FF terminator
  • byte 1: X/base-position adjustment
  • byte 2: Y/base-position adjustment

The selector indexes span tables at $E283/$E284, then source tables at $E2EB, $E2A2, and $E334. One compact three-byte object record expands to one or more four-byte OAM entries.

  • $FF terminates the variable object list.
  • Values at or above table position $2B select OAM attribute $42; lower positions select $02 through the intentional overlapping-instruction idiom at $DF3A/$DF3B.
  • The parser resumes at the next source record using $1C as the consumed-byte offset.

This is a table-expanded object/sprite list, not a byte-run compressor.

5. $D72D-$D7FF: course-cell code interpreter

This routine reads one byte through ($19),Y from the generated course map. It is not a stream decompressor. It interprets cell codes for collision or terrain response:

  • $EF and $FD are special sentinels.
  • Other values index the property table at $D80D.
  • Property values $10, $20, $40, and $FF select distinct collision or response paths.
  • Fine X/Y bits are converted into a bit mask and tested against the selected property byte.

The $FF comparison at $D74D belongs to this cell-property dispatch and must not be mistaken for a compressed-stream terminator.

Parser-boundary conclusions

  • $CBEA sprite layouts are bounded by $FF or 21 entries.
  • Primary hole streams are bounded by their first $FF and decode into a 512-byte RAM area, not a fixed 480-byte area.
  • Optional auxiliary blocks are bounded by $55 or 16 four-byte groups.
  • Per-hole object lists are bounded by a secondary $FF and expand through fixed lookup tables.
  • $D72D is a map-cell interpreter, not another compressed-data parser.

No remaining $FF comparison in reachable code has been found that represents an unclassified compressed-stream parser.