Using HVSC and smart search of specific parts in the music player for 1987 Street Sports Basketball (C64) I was surprised to see that the player went back to 1983's Mountain King. In fact, the player was found in more games. here's a list:
; Mountain King 1983 Beyond
; Donkey Kong 1983 Nintendo
; Track and Field 1984 Atarisoft
; WarGames 1984 Coleco Vision
; Sesame Street letter Go Round 1984 CCW, CBS Software
; Pole Position 1984 Atarisoft
; Math Mileage 1984 Douglas D. Dragin
; Chevytech 1985 Chrevrolet, unknown programmers.
; Crystal Castles 1986 Unknown
; World Games 1986 Epyx
; Street Sports Basketball 1987 Epyx
; Street Sports Soccer 1988 Epyx
; Street Sports Football 1988 Epyx
; Chevytech 1988 Chrevrolet
; The Sporting News Baseball 1988 Epyx
I went on and disassembled the player that is in Mountain King, which I consider the first version for now and since Douglas D. Dragin is the one credited as programmer (and also on some other games in the list above) I think it is safe to assume it was Dragin that coded the player.
In the file you can read up on the player specifics and the source code as well.
But I will show some details in this post, and provide example programs (PRG) to run. See below.
Take a look at the global variable list:
; Global variables at zero page
;
; $d6 Player playing frame boolean (0 = not playing, $ff = playing)
; $d7-$d9 BLOCK repeat counter for each voice
; $e0-$e1 voice 0 track position
; $e2-$e3 voice 1 track position
; $e4-$e5 voice 2 track position
; $e6-$e7 pointer to jump for effect routine
; $e8-$e9 $e9 signals song playing (if 0), if the song data signals "end" then $e8/$e9 will be updated to point to offset of effect 0, and e9 will be non-zero
; $ea voice/track (doubled: 0, 2, 4)
; $eb voice number (0, 1, 2)
; $ec-$ed voice 0 starting track position
; $ee-$ef voice 1 starting track position
; $f0-$f1 voice 2 starting track position
; $f2 voice 0 note duration
; $f3 voice 1 note duration
; $f4 voice 2 note duration
; $f7-$f9 pattern repeat counter for voice 0, 1 and 2
; $fa filter state of voices (bits 0, 1 or 2 set)
; $fb filter mode (low, band, hi)
; $fc filter resonance
; $fd current volume
; $fe current effect/note
As shown, these are all at the zero page.
Song init
Each player can have multiple songs, and each song is initialized by passing the track index to $8006 in the accumulator to assign a track to a voice. In the ripper code (not original game code) the voice song/sound fx number is passed in the accumulator when calling the ripper Init routine. The routine sets the timer to 17512 cycles. Which is ~58 Hz on NTSC machines, and 56 Hz on PAL machines. So the ripper used the timer to call the play routine as well. In the game code, depending on a hard coded number in the software it is either 17000 or 17512 cycles, for 17000 basically 60 Hz (NTSC) or 57 Hz (PAL). My guess is that this hard coded number was different in PAL and NTSC versions.
Tracks
Each track is assigned to a specific SID voice. At initalization, the starting track for each voice is identified. It is an array of effects and notes. Parts in each track can repeat, and whole blocks can repeat. A song can end, but it may also loop. The player can handle whole three voice songs, or just sound effects assigned to a single voice.
Effects
Take a look at the possible effects:
; C0 Get voice 3 ADSR output, divide by 4 and set high frequency byte of voice 3 to the outcome. Also "Default" effect, when the song ends.
; C1 XX SET WAVEFORM/VOICE CONTROL REGISTER (XX=WAVEFORM/VOICE value)
; C2 XX SET PULSE WIDTH (XX=PW value)
; C3 XX YY SET ADSR (XX=AD value, YY=SR value)
; C4 XX YY SET FILTER CUT OFF FREQUENCY (XX=cut-off lo byte, YY=cut-off hi byte)
; C5 XX SET FILTER RESONANCE (XX=filter control value, including resonance nibble)
; C6 XX SET MAIN VOLUME $D418 (XX=volume and filter modes, volume nibble is used)
; C7 XX SET NOTE DURATION (XX=duration in ticks)
; C8 LOOP TO STARTING POSITION (RESTART SONG)
; C9 TURN ON FILTER ON CURRENT VOICE
; CA XX SET FILTER MODE: LOW, BAND, HI. (XX=filter mode as high nibble)
; CB TURN OFF FILTER ON CURRENT VOICE
; CC XX SET IN-PATTERN REPEAT NUMBER (XX=repeat value)
; CD XX YY DO THE IN-PATTERN REPEAT (XXYY is the offset of the start of the repeat, lo byte hi byte)
; CE SET NOTE FREQUENCY (not used)
; CF XX SET BLOCK REPEAT NUMBER (XX=repeat value)
; D0 XX YY DO BLOCK REPEAT (XXYY is the offset of the larger block to repeat, lo byte hi byte)
Effect 0, or C0, is a weird one. This can only be useful for a sound effect really, for voice 3. It converts the volume envelope of Voice 3 to the high frequency byte of Voice 3. So with increasing volume, the pitch will become higher, and with decreasing volume the pitch will become lower.
Note frequency table
Notes are looked up in a frequency table that is generated at initalization. In a clever way, the Fn values for the SID are taken from the highest octave for each note, and the rest of the lower octaves are then calculated from those by simply dividing by two each time for the next octave. However, the table is based on NTSC clock speed and not PAL clock speed. In fact, this method would never be possible on a PAL machine, since B-7 has a theoretical Fn value of 24-bits, $106d0.
You have to realize that the PAL machine is slower than an NTSC machine. PAL is running at 985248.4 Hz while NTSC is running at 1022727 Hz. That means that each cycle on a PAL machine is 1.01497 microseconds, but on an NTSC machine it is 0.97778 microseconds. So taking the impossible B-7 on a PAL machine, we would need to be able to tell the C64 that we need a higher frequency of $106d0 (16-bit Fn value), since our clock is slower, to meet the tone frequency of 3951 Hz!
Remember: Fout = (Fn * Fclk / 16777216) Hz !
The frequency table generated in the Mountain King player assumes the machine is NTSC, it doesn't correct for different clock speeds. As a result of using the timer on top of that, the game music will be slower in speed, but also lower in pitch, and out of tune in the higher ranges. Take again the example of B-7. In the game an Fn of $FD2E is used. That is 3951 Hz on an NTSC machine, all is well. But on a PAL machine that is actually 3806 Hz, closer to an A#7 (3729 Hz). Similarly, A-5 is 880 Hz, but using the NTSC Fn number on a PAL machine, we end up with 847 Hz, closer to G#5 (830 Hz). This is why many of the NTSC games had pretty out of tune sounding songs. In the zip file below I also have a fixed version of Mountain King, for PAL. Note the difference on a PAL machine!
Repeat parts and block effects
;---------------------------------------
; Any piece of a track that needs repeating is first marked with the command CC (0C) followed by how many times it should repeat.
; this piece is then followed by command CD (0D) which means do the repeat, followed by the offset to start from. (which is the byte following the CC command + repeat value)
; Example:
;
; |-----------------------|
; v |
; $0100 CC XX ..............CD 00 01 --
;
;
; Larger parts (or blocks) can also be repeated using command CF (0F), that precedes a block and is followed by a byte depicting how many times it should repeat.
; The command D0 (10) then marks the repeat happening, just like command CD, and is followed by the offset to start the repeat from. Again, this is the position
; right from the CF command.
; Example:
;
; |-----------------------|
; v |
; $0100 CF XX ..............D0 00 01 --
;
;---------------------------------------
Downloads
The source code with my comments here: Mountain King Music Player Source.
Example tunes from different games mentioned above that use this player by Dragin: Dragin Player Tunes.
EDIT: I also added a PAL fix for Street Sports Basketball in the zip above.