1986 · Activision · Music

Labyrinth

This minisite awaits a maintainer’s check. A model this site has not proven yet worked on it (claude-sonnet-5-5), so a maintainer will measure its listing on one it has (how the check works).

Five tunes and 44 sound effects, played by the game's own sound code running in the page.

01 · Music

Music: five tunes from the game's own sound code

The player below runs the sound code that sits in every load of the game, the note player $253F and the event engine $1C2D, on the kit's 6502 simulator. It calls both once a frame, as the raster interrupt $113A does, and plays what they write to the SID.

Each tune was checked against the same code run in the simulator, a frame at a time: every write to the sound chip, in order, with no difference. The game sets the SID filter in all five tunes, so the player models a 6581's, and real chips differ from each other in cutoff by large factors. The sound effects have no player yet.

A tune is a header, three note sequences, and a set of instruments

A tune starts when a routine calls $2517 with the address of its header. The header holds the low and high halves of an instrument directory, then groups of three voice pointers. Each voice reads pairs of a note and an instrument, and a pair whose instrument is zero ends that voice's sequence. The next group of pointers then starts, and a group whose first pointer has a zero high byte ends the tune. $25FA turns each note into a frequency and writes it into the instrument's event template, and $1C9F starts that template as a sound stream.

Where each tune plays

Sound effects are short event streams queued by request number

The game's other sounds, such as a door opening or a coin refused, are not tunes. A handler loads a request number into the accumulator and calls $1C0C, which does nothing while a tune plays. Otherwise it looks the number up in the selector table at $1FA9, whose complement picks the first pending slot, and files the request there. A positive selector in the next byte files that following request in its own slot too, so request 5 starts streams 5 and 6 together. The pointer tables $1F33 and $1F6E give each stream's address, and $1C2D reads the streams once a frame in the same way as it reads a tune's events.

The 36 requests that can be queued alone, each started through the reset $1C6C and the request, and each checked against the game's code a frame at a time. Requests 0 and 55 to 58 are not requests, and no code asks for them. Stream 0 is a filter stream, but request 0 files it in voice slot 0, which expects a voice stream, and the simulator ends up running code at $0000. The selector table ends at request 54, so a number from 55 up would read the bytes of the streams at $1FE0 as selectors. Requests that a stream pairs with another, such as 6 after 5, are heard with the first. The last eight entries are pitched effects: the helper $0CE8, reached through the stub at $0860, adds 7 times A to Y, writes the note that many semitones below note 99 into the request's stream, and then queues the request. Every caller asks for request 49 or 53. The pitch inputs listed are the two fixed calls and the lowest, middle and highest values scene 24's wall hit can build from its row and cell band.