1990 · Ocean

Darkman

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).

Three tunes, a fourth that never starts, and eleven sound effects, played by the game's own sound driver.

01 · Music

The music plays before a level and never during one

The title pages and the briefing text play three tunes, and a fourth is in the game and never starts. Jonathan Dunn is credited with the sound. The player below runs the game's own driver on the kit's 6502 simulator, calls it once a frame, and plays what the driver writes to the sound chip.

The driver is the title copy at $A0C8, in the bytes of level 1's file from $817C to $93AB, and the player calls $A242 once a frame, 50.12 times a second on a PAL machine. The page supplies only the player, the note display, and the filter, which starts as a model of a 6581 chip; its cutoff curve is one chip's, and real chips differ. Each tune was checked against the same calls on the kit's 6502 simulator (test_music.js): 520,674 writes to the sound chip over 12,000 frames of each tune, in order, with no difference.

Level 1's title code starts a tune by calling $A164 with X set to six times the tune number. The title pages play tune 3, or tune 1 when the new-high-score flag at $110F is set. Tune 0 starts at $0F70 whenever no voice is playing while the briefing text is typed. Nothing in the game loads X with 12, so tune 2 never starts, and it is the only tune that ends: the other three loop. Once play begins, nothing calls the tune player again.

TuneStarted atTrack lists, voices 1 to 3PlaysWave programsPatterns
0$0F70, with the briefing text$ADAD $ADA0 $ADB2Loops every 1,920 frames (38.3 s)0 and 10, 1, 6
1$0AC1, when the new-high-score flag is set$AF5A $AF93 $AFA6An opening of 1,344 frames (26.8 s), then loops every 960 (19.2 s)4 to 62 or 8, and 26 to 31
2Never$AEB8 $AEBC $AEC0Ends on frame 457 (9.1 s)2 and 321 to 23
3$0AC8 on the title pages, and $1320 once no voice is playing$B10B $B143 $B0F3Loops every 5,376 frames (107.3 s)7 to 1069 to 71, 73, 75, 76 or 77, and 78

The tune table at $AA8D holds the three track-list addresses of each tune. Two pattern numbers that share an address cannot be told apart from the address, so the player shows both.

The driver is not a program of its own on the disk. Level 1's file holds it twice: once to play tunes, and once to play sound effects. $1C11 copies 19 pages of the file, from $80B4 to $A000, when the title pages are set up, and $1BE9 copies 13 pages from $747B to $B000 when play is set up. The second copy covers the first from $B000 upwards, so tune 3's track lists, which begin at $B0F3, are in the file at $91A7 and gone from memory in play.

A tune is three track lists, one for each voice. A track list is a row of pattern numbers with a few commands between them, and the pattern tables at $AAA5 and $AAF6 give each pattern's address. A pattern is a stream of notes, each with a length in frames, and commands that change the instrument. A stream picks a wave program from the table at $ACF7, and each note runs it: a short list of steps that sets the waveform and the envelope, and waits. The pulse width, the pitch slide, the arpeggio, and the filter each have a table of their own. Notes come from a table of 94 pitches at $A98C and $A9EA, where entry 45 is A3.

Stream byteWhat the driver does
$00 to $5FA note, followed by its length in frames
$60A rest, followed by its length
$80The pattern ends, and the track list gives the next
$81 nUse wave program n
$82Start a pulse-width sweep, from an index and a width
$83Start a pitch slide, from an index
$84, $8DStart an arpeggio, from a list
$85Start a filter sweep
$86, $89Play a drum, followed by a number and a length
$87 nEvery note after this lasts n frames and has no length byte
$88Every note after this has its own length byte again
$8AStop this voice
$8BContinue at the address in the next two bytes

The commands of a pattern or sound-effect stream. The sound-effect copy of the driver reads the same commands.

Track list byteWhat the driver does
Below $F0A pattern number
$F0 nJump to byte n of the track list, which is how a tune loops
$F1 nPlay the next pattern n times
$F2Stop the voice
$F3 nTranspose the next pattern by n semitones, a signed number
$F4 nFade the volume out
$F5Fade the volume in, from a rate and a target

The commands of a track list.

02 · Sound effects

Eleven sound effects, and the last is never asked for

In play, a second copy of the driver sits at $B000 and plays sound effects only. Press a button to hear one. The numbers are the game's own, and the names say what the levels use each effect for, where the code makes that plain.

The driver is the sound-effect copy at $B0C8, in the bytes of level 1's file from $7543 to $80B3. Each button starts the effect as a level does, with $B0EE, and calls $B176 once a frame. Each effect was checked against the same calls on the kit's 6502 simulator: 18,547 writes to the sound chip over 400 frames of each effect, in order, with no difference. The page repeats the chip's registers every 32 bytes from $D400 to $D7FF, as the chip does, so the stray writes described below land where they do on a real chip.

A level plays an effect by calling $B0EE with X set to four times the effect number. That picks a row of the sound table at $BB48, which has one byte for each voice: the number of a stream, or a value with bit 7 set that leaves the voice alone. A stream is the same kind of list of notes and commands that a tune uses, and the table at $BB74 gives each stream's address. Effects 0, 2, 6, and 7 use voice 1 alone, and the others use voices 2 and 3 together. Effect 10 plays one note and starts it again every 255 frames, so it never ends, and no level asks for it.

EffectXVoicesFramesSecondsAsked for byStray zero lands on
0$001130.26Level 1: the strike, the punch, and the crouch. Levels 2, 4, and 6: walking and crouching, and climbing in levels 4 and 6. Level 3: the hit test. Level 5: taking energy.-
1$042 and 3150.30Darkman losing energy in levels 1, 2, 4, and 6. Level 5: the finale.$D4BB no register (27)
2$081160.32An enemy's shot in level 1. Level 5: the shots.$D503 voice 1 pulse width, high byte
3$0C2 and 3230.46Score pick-ups in levels 2, 4, and 6, and level 1's exit marker.$D4BC no register (28)
$D47C no register (28)
4$102 and 3791.58Energy pick-ups in levels 2, 4, and 6.$D450 voice 3 pulse width, low byte
$D464 voice 1 control register
5$142 and 31072.13A pick-up's score in levels 2 and 4, level 3's objects, and level 6's falling objects.$D4A7 voice 2 frequency, low byte
$D4C0 voice 1 frequency, low byte
6$181340.68The mask game's fire button, in levels 1, 3, and 5.$D4F6 filter cutoff, high byte
7$1C1110.22The mask game's cursor scan, objects starting in level 1, and the shooters and blasts of levels 2 and 4.$D477 filter routing
8$202 and 3631.26Level 3's objects, and falling objects in levels 4 and 6.$D449 voice 2 pulse width, low byte
$D47B no register (27)
9$242 and 3671.34An enemy dying in level 1.$D423 voice 1 pulse width, high byte
$D462 voice 1 pulse width, low byte
10$282 and 3Never ends-Nothing.$D436 filter cutoff, high byte
$D484 voice 1 control register

Voices are numbered as the player numbers them. Seconds are frames at 50.12 a second.

The effect starter has a fault. For every voice it starts, $B0EE writes a zero to one of the sound chip's control registers. It finds the register's offset in the three bytes at $B8B4, which are 00, 07, and 0E, but it indexes them with the position in the sound table, four times the effect number plus the voice, where it should use the voice. Effect 0 reads $B8B4 itself and gets the right offset by luck. Every other effect reads from the bytes that follow the table, and the zero goes to whatever address that byte gives. The last column lists them. The chip repeats its registers every 32 bytes, so a stray write at $D464 clears register 4, the control register of voice 1, and one at $D477 clears register 23, the filter's resonance and voice routing. A write at $D4BB reaches no register at all.