153 lines
5.3 KiB
Markdown
153 lines
5.3 KiB
Markdown
# 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.
|