Difference between revisions of "Music Engine Description"
(Add more data tables and some information on variables used.) |
|||
(23 intermediate revisions by 3 users not shown) | |||
Line 1: | Line 1: | ||
− | There are two separate music engines in Zelda 2. Both are in bank 6 (along with | + | There are two separate music engines in Zelda 2. Both are in bank 6 (along |
− | the song data). The music engine for the title screen resides at $8000 and the | + | with the song data). The music engine for the title screen resides at |
− | engine for the rest of the game is at $9000. Each of these engines is called | + | <code>$8000</code> and the engine for the rest of the game is at |
− | once per frame during the appropriate part of the game. | + | <code>$9000</code>. Each of these engines is called once per frame during the |
+ | appropriate part of the game. | ||
+ | |||
+ | Changes to the music engine should be very easy to make, as there is an | ||
+ | abundance of free space in bank 6. | ||
== Title Music Engine == | == Title Music Engine == | ||
− | ' | + | The title music engine is different from the engine that plays music |
+ | during the rest of the game. In particular, it has a wider range of | ||
+ | notes that can be represented. If you want to look into the actual | ||
+ | code, the title screen music engine main loop is at <code>$8000</code> (vesus | ||
+ | <code>$9000</code> for the game music loop). | ||
+ | |||
+ | The metadata for the songs is all the same as in the other areas, | ||
+ | although the title music is actually broken up into several songs | ||
+ | itself. These song boundaries control the timing of the title scroll. | ||
+ | |||
+ | The following songs exist in the title song table: | ||
+ | |||
+ | # Intro | ||
+ | # Start | ||
+ | # Build up | ||
+ | # Main | ||
+ | # Breakdown | ||
+ | |||
+ | The start of song 2 triggers the title to scroll into view. The start | ||
+ | of song 4 triggers a countdown until the story scroll begins. After | ||
+ | song 5 finishes, the engine loops back to song 2. | ||
+ | |||
+ | Within the note data, a special format is used. Rather than encoding | ||
+ | the duration and pitch together in a single byte, the title music has | ||
+ | "pitch" bytes and "duration" bytes. Any byte with the highest bit set | ||
+ | (i.e. anything >= <code>$80</code>) is interpreted as a duration change, which | ||
+ | sets the duration of all future notes. Anything else is a pitch value | ||
+ | which indicates a note of the current duration should be played. The | ||
+ | low nybble of duration values keys into a lookup table at bank 6 <code>$8084</code> | ||
+ | and store the duration byte at <code>$07FF</code>. Values are as follows: | ||
+ | |||
+ | * <code>$80</code> - 8 ticks (sixteenth note) | ||
+ | * <code>$81</code> - 24 ticks (dotted eighth note) | ||
+ | * <code>$82</code> - 16 ticks (eighth note) | ||
+ | * <code>$83</code> - 32 ticks (quarter note) | ||
+ | * <code>$84</code> - 48 ticks (dotted quarter note) | ||
+ | * <code>$85</code> - 64 ticks (half note) | ||
+ | * <code>$86</code> - 96 ticks (dotted half note) | ||
+ | * <code>$87</code> - 128 ticks (whole note) | ||
+ | * <code>$88</code> - 11 ticks (eighth note triplet, first two) | ||
+ | * <code>$89</code> - 10 ticks (eighth note triplet, third) | ||
+ | * <code>$8A</code> - 80 ticks (half note + eighth note) | ||
+ | |||
+ | The pitch values are stored in a lookup table at bank 6 from <code>$808F</code> to <code>$810E</code>. Any value from <code>$00</code> to <code>$7F</code> represents an entry in this table. | ||
+ | <code>$46</code> is A4 and every semitone away from that adds or subtracts 2 from | ||
+ | the value. Thus, C5 (three semitones above A4) is <code>$4C</code>. As in the game music engine, the value | ||
+ | <code>$02</code> represents a rest. Unlike the game music engine, <code>$00</code> also represents a rest but the code has special handling for <code>$02</code> so using that is recommended. | ||
+ | |||
+ | -2- -3- -4- -5- -6- -7- | ||
+ | C : $04 $1C $34 $4C $64 $7C | ||
+ | C# : $06 $1E $36 $4E $66 $7F | ||
+ | D : $08 $20 $38 $50 $68 | ||
+ | Eb : $0A $22 $3A $52 $6A | ||
+ | E : $0C $24 $3C $54 $6C | ||
+ | F : $0E $26 $3E $56 $6E | ||
+ | F# : $10 $28 $40 $58 $70 | ||
+ | G : $12 $2A $42 $5A $72 | ||
+ | G# : $14 $2C $44 $5C $74 | ||
+ | A : $16 $2E $46 $5E $76 | ||
+ | Bb : $18 $30 $48 $60 $78 | ||
+ | B : $1A $32 $4A $62 $7A | ||
+ | |||
+ | |||
+ | By way of an example, let's look at the main section of the vanilla | ||
+ | title music. The title metadata starts at bank 6 <code>$84DA</code>: | ||
+ | |||
+ | 08 11 14 16 19 1E 1E 1E | ||
+ | ^ Main section | ||
+ | |||
+ | The main section is the fourth song, starting at <code>$84DA + $16 = $84F0</code>: | ||
+ | |||
+ | 3F 3F 00 | ||
+ | |||
+ | This means there is a single phrase for the main section, which is | ||
+ | repeated twice. The phrase data is at <code>$84DA + $3F = $8519</code>. | ||
+ | |||
+ | 00 3D 86 3A 1A 5F | ||
+ | |||
+ | From this, we see the melody note data is located at <code>$863D</code>: | ||
+ | |||
+ | 83 Quarter notes | ||
+ | 02 Rest | ||
+ | 48 C5 | ||
+ | 82 Eighth notes | ||
+ | 46 B4 | ||
+ | 83 Quarter notes | ||
+ | 3E G4 | ||
+ | 34 D4 | ||
+ | 84 Dotted quarter notes | ||
+ | 2E B3 | ||
+ | 83 Quarter notes | ||
+ | 30 C4 | ||
+ | 34 D4 | ||
+ | 3A F4 | ||
+ | 38 E4 | ||
+ | 34 D4 | ||
+ | 30 C4 | ||
+ | 82 Eighth notes | ||
+ | 34 D4 | ||
+ | 83 Quarter notes | ||
+ | 30 C4 | ||
+ | 85 Half notes | ||
+ | 2E B3 | ||
+ | 82 Eighth notes | ||
+ | 02 Rest | ||
+ | 00 End of Data | ||
== Main Music Engine == | == Main Music Engine == | ||
− | |||
− | |||
=== Data Tables === | === Data Tables === | ||
Line 36: | Line 143: | ||
with 5 bits of pitch information and 3 bits of duration, as such: | with 5 bits of pitch information and 3 bits of duration, as such: | ||
− | + | D1 D0 P4 P3 P2 P1 P0 D2 | |
− | |||
− | |||
The Get Note Duration routine shifts things around to get the D bits into the | The Get Note Duration routine shifts things around to get the D bits into the | ||
Line 49: | Line 154: | ||
|'''00'''||04||0C||08||10||18||20||05||06 | |'''00'''||04||0C||08||10||18||20||05||06 | ||
|- | |- | ||
− | |'''08'''||04||0F||09||12||1B||24||06||06 | + | |'''08'''||''04''||''0F''||09||12||1B||24||06||06 |
|- | |- | ||
|'''10'''||05||0F||0A||14||1E||28||07||06 | |'''10'''||05||0F||0A||14||1E||28||07||06 | ||
Line 57: | Line 162: | ||
|'''20'''||07||15||0E||1C||2A||38||13||12 | |'''20'''||07||15||0E||1C||2A||38||13||12 | ||
|- | |- | ||
− | |'''28'''||07||15||0E||1C||2A||38|| | + | |'''28'''||07||15||0E||1C||2A||38||09||0A |
|} | |} | ||
− | The | + | The base value of each row is the third one, which represents an eighth note. The other values are usually related numbers to give these normal note lengths: |
− | |||
− | eighth note. The | ||
− | |||
− | + | # Sixteenth note† | |
+ | # Dotted eighth note† | ||
+ | # Eighth note | ||
+ | # Quarter note | ||
+ | # Dotted quarter note | ||
+ | # Half note | ||
+ | |||
+ | The last two columns are used to do various kind of triplets. Since the values in the table are not always divisible by three, triplets need to be made by rounding. However, that would introduce error in the length of the notes, so a second value is used for the final triplet to correct that error. For example, in row <code>$10</code>, one would use <code>$07</code>, <code>$07</code>, <code>$06</code> for eighth note triplets. These lengths add up to 20 which is exactly twice the eighth note length of 10 in that row. | ||
+ | |||
+ | † Row <code>$08</code> has weird values for columns 0 and 1 that are best avoided. | ||
==== Pitch LUT ==== | ==== Pitch LUT ==== | ||
Line 99: | Line 210: | ||
|70|| G7||G#7|| A7||A#7|| B7|| C8 | |70|| G7||G#7|| A7||A#7|| B7|| C8 | ||
|} | |} | ||
+ | |||
+ | Value <code>$02</code> represents a rest. | ||
Note that although the table goes through <code>$7A</code> values above | Note that although the table goes through <code>$7A</code> values above | ||
− | <code>$3E</code> aren't usable due to the 5 bit limit for pitch data. | + | <code>$3E</code> aren't usable due to the 5 bit limit for pitch data in |
− | + | the song data. However, the sound effects routines make use of these | |
− | + | extra notes. For example, the sword beam effect alternates between | |
+ | playing C7 and F6. | ||
==== Noise Samples ==== | ==== Noise Samples ==== | ||
Line 111: | Line 225: | ||
noise period in the low nybble. | noise period in the low nybble. | ||
− | + | $90E8 - Sword slash | |
− | + | $9123 - Enemy hurt | |
− | + | $912C - Crumble block | |
=== Variables === | === Variables === | ||
Line 120: | Line 234: | ||
description of what they are. | description of what they are. | ||
− | + | $00E0 - Current phrase note data address (low byte) | |
− | + | $00E1 - Current phrase note data address (high byte) | |
− | + | $00E2 - Song offset, converted from song id to be an index instead of a bit field | |
− | + | $00E3 - Current phrase index | |
− | + | $00E4 - Drums loop start | |
− | + | $00E5 - Tempo | |
− | + | $00E6 - Pulse 1 low bits (used for vibrato effect) | |
− | + | $00E7 - Pulse 2 low bits (used for vibrato effect) | |
− | + | $00E8 - Pitch storage, temporary | |
− | + | $00E9 - Play sound effect | |
− | + | $00EA - Disable music | |
− | + | $00EB - Request new song | |
− | + | $00EC - Play sound effect | |
− | + | $00ED - Play sound effect | |
− | + | $00EE - Play sound effect | |
− | + | $00EF - Play sound effect | |
− | + | $0707 - Current world | |
− | + | $075F - Queued song, loaded after screen transitions | |
− | + | $07DA - Ganon Laugh sample | |
− | + | $07DB - Song to resume after fanfare | |
+ | $07DF - ??? Likely sound effect flag for pulse 1 channel override | ||
− | + | $07E0 - ??? Likely sound effect flag for noise channel | |
− | + | $07E2 - Pulse 2 envelope index | |
− | + | $07E3 - Pulse 1 envelope index | |
− | + | $07E4 - Drums current note duration | |
− | + | $07E5 - Bass current note duration | |
− | + | $07E6 - Harmony current note duration | |
− | + | $07E7 - Melody current note duration | |
− | + | $07E8 - Drums next note index | |
− | + | $07E9 - Bass next note index | |
− | + | $07EA - Harmony next note index | |
− | + | $07EB - Melody next note index | |
− | + | $07EC - Ganon Laugh counter | |
+ | $07ED - Sound FX counter (E9) | ||
+ | $07EE - Sound FX counter (E9) | ||
+ | $07F5 - Sound FX counter (ED) | ||
− | + | $07FA - Current SFX (E9) | |
− | + | $07FB - Current song | |
− | + | $07FD - Current SFX (ED) | |
− | + | $07FE - ??? Likely sound effect flag for pulse 2 channel override | |
− | + | $07FF - Current SFX (EF) | |
=== Routines === | === Routines === | ||
Line 236: | Line 354: | ||
Plays the sound effect requested in <code>$E9</code>. | Plays the sound effect requested in <code>$E9</code>. | ||
− | + | '''TODO''' document further. | |
==== Play EF SFX <code>$92F4</code> ==== | ==== Play EF SFX <code>$92F4</code> ==== | ||
Line 242: | Line 360: | ||
Plays the sound effect requested in <code>$EF</code>. | Plays the sound effect requested in <code>$EF</code>. | ||
− | + | '''TODO''' document further. | |
==== Play EE SFX <code>$9408</code> ==== | ==== Play EE SFX <code>$9408</code> ==== | ||
Line 248: | Line 366: | ||
Plays the sound effect requested in <code>$EE</code>. | Plays the sound effect requested in <code>$EE</code>. | ||
− | + | '''TODO''' document further. | |
==== Play Noise <code>$959D</code> ==== | ==== Play Noise <code>$959D</code> ==== | ||
Line 311: | Line 429: | ||
Plays the sound effect requested in <code>$EC</code>. | Plays the sound effect requested in <code>$EC</code>. | ||
− | + | '''TODO''' document further. | |
==== Play Music <code>$9B18</code> ==== | ==== Play Music <code>$9B18</code> ==== | ||
Line 400: | Line 518: | ||
melody, harmony, bass, and drums. The general process is as follows: | melody, harmony, bass, and drums. The general process is as follows: | ||
− | + | decrement duration | |
− | + | if duration == 0 { | |
− | + | load next note | |
− | + | increment note pointer | |
− | + | set duration for note | |
− | + | if no overriding sound effect { | |
− | + | play note on appropriate channel | |
− | + | } | |
− | + | } | |
Some channels have unique processing at different points in this. For example, | Some channels have unique processing at different points in this. For example, |
Latest revision as of 05:08, 19 August 2023
There are two separate music engines in Zelda 2. Both are in bank 6 (along
with the song data). The music engine for the title screen resides at
$8000
and the engine for the rest of the game is at
$9000
. Each of these engines is called once per frame during the
appropriate part of the game.
Changes to the music engine should be very easy to make, as there is an abundance of free space in bank 6.
Contents
- 1 Title Music Engine
- 2 Main Music Engine
- 2.1 Data Tables
- 2.2 Variables
- 2.3 Routines
- 2.3.1 Main Music Loop $9000
- 2.3.2 Configure Pulse1 Channel $9031
- 2.3.3 Configure Pulse2 Channel $9038
- 2.3.4 Play Note Pulse1 $9042
- 2.3.5 Play Note $9044
- 2.3.6 Play Note Pulse2 $9067
- 2.3.7 Play Note Triangle $906B
- 2.3.8 Get Note Duration $906F
- 2.3.9 Vibrato Pulse1 $9089
- 2.3.10 Vibrato Pulse2 $9097
- 2.3.11 Vibrato $909E
- 2.3.12 Play E9 SFX $920B
- 2.3.13 Play EF SFX $92F4
- 2.3.14 Play EE SFX $9408
- 2.3.15 Play Noise $959D
- 2.3.16 Crumble Bridge SFX $956C
- 2.3.17 SFX Noise Decay $9587
- 2.3.18 Play ED SFX $95A7
- 2.3.19 Crumble Block / Enemy Hurt SFX $95E6
- 2.3.20 Crumble Block / Enemy Hurt SFX Decay $95EE
- 2.3.21 Play Noise Samples $9612
- 2.3.22 Sword Strike SFX $9604
- 2.3.23 Play EC SFX $990B
- 2.3.24 Play Music $9B18
- 2.3.25 Next Song $9B24
- 2.3.26 Mute All $9B3B
- 2.3.27 Load Song $9B61
- 2.3.28 Load Song Data $9B80
- 2.3.29 Play Notes
Title Music Engine
The title music engine is different from the engine that plays music
during the rest of the game. In particular, it has a wider range of
notes that can be represented. If you want to look into the actual
code, the title screen music engine main loop is at $8000
(vesus
$9000
for the game music loop).
The metadata for the songs is all the same as in the other areas, although the title music is actually broken up into several songs itself. These song boundaries control the timing of the title scroll.
The following songs exist in the title song table:
- Intro
- Start
- Build up
- Main
- Breakdown
The start of song 2 triggers the title to scroll into view. The start of song 4 triggers a countdown until the story scroll begins. After song 5 finishes, the engine loops back to song 2.
Within the note data, a special format is used. Rather than encoding
the duration and pitch together in a single byte, the title music has
"pitch" bytes and "duration" bytes. Any byte with the highest bit set
(i.e. anything >= $80
) is interpreted as a duration change, which
sets the duration of all future notes. Anything else is a pitch value
which indicates a note of the current duration should be played. The
low nybble of duration values keys into a lookup table at bank 6 $8084
and store the duration byte at $07FF
. Values are as follows:
$80
- 8 ticks (sixteenth note)$81
- 24 ticks (dotted eighth note)$82
- 16 ticks (eighth note)$83
- 32 ticks (quarter note)$84
- 48 ticks (dotted quarter note)$85
- 64 ticks (half note)$86
- 96 ticks (dotted half note)$87
- 128 ticks (whole note)$88
- 11 ticks (eighth note triplet, first two)$89
- 10 ticks (eighth note triplet, third)$8A
- 80 ticks (half note + eighth note)
The pitch values are stored in a lookup table at bank 6 from $808F
to $810E
. Any value from $00
to $7F
represents an entry in this table.
$46
is A4 and every semitone away from that adds or subtracts 2 from
the value. Thus, C5 (three semitones above A4) is $4C
. As in the game music engine, the value
$02
represents a rest. Unlike the game music engine, $00
also represents a rest but the code has special handling for $02
so using that is recommended.
-2- -3- -4- -5- -6- -7- C : $04 $1C $34 $4C $64 $7C C# : $06 $1E $36 $4E $66 $7F D : $08 $20 $38 $50 $68 Eb : $0A $22 $3A $52 $6A E : $0C $24 $3C $54 $6C F : $0E $26 $3E $56 $6E F# : $10 $28 $40 $58 $70 G : $12 $2A $42 $5A $72 G# : $14 $2C $44 $5C $74 A : $16 $2E $46 $5E $76 Bb : $18 $30 $48 $60 $78 B : $1A $32 $4A $62 $7A
By way of an example, let's look at the main section of the vanilla
title music. The title metadata starts at bank 6 $84DA
:
08 11 14 16 19 1E 1E 1E ^ Main section
The main section is the fourth song, starting at $84DA + $16 = $84F0
:
3F 3F 00
This means there is a single phrase for the main section, which is
repeated twice. The phrase data is at $84DA + $3F = $8519
.
00 3D 86 3A 1A 5F
From this, we see the melody note data is located at $863D
:
83 Quarter notes 02 Rest 48 C5 82 Eighth notes 46 B4 83 Quarter notes 3E G4 34 D4 84 Dotted quarter notes 2E B3 83 Quarter notes 30 C4 34 D4 3A F4 38 E4 34 D4 30 C4 82 Eighth notes 34 D4 83 Quarter notes 30 C4 85 Half notes 2E B3 82 Eighth notes 02 Rest 00 End of Data
Main Music Engine
Data Tables
There are several data tables used by the music engine.
Pulse Envelope
This table is used to set the volume of the pulse channels over time. The index used starts at a certain value and decrements, so the table is in reverse order. The index is halved before use, so each value lasts two frames.
90 | 91 | 91 | 91 | 92 | 92 | 92 | 92 |
93 | 93 | 94 | 94 | 94 | 95 | 95 | 95 |
96 | 96 | 96 | 97 | 97 | 97 | 98 | 98 |
Duration LUT
This table is used to determine the duration of a note. The note data is stored with 5 bits of pitch information and 3 bits of duration, as such:
D1 D0 P4 P3 P2 P1 P0 D2
The Get Note Duration routine shifts things around to get the D bits into the
three least significant bits and masks it before use. This value is then added
to the tempo value in $E5
which is always a multiple of 8 to index
into the following table:
00 | 04 | 0C | 08 | 10 | 18 | 20 | 05 | 06 |
08 | 04 | 0F | 09 | 12 | 1B | 24 | 06 | 06 |
10 | 05 | 0F | 0A | 14 | 1E | 28 | 07 | 06 |
18 | 06 | 12 | 0C | 18 | 24 | 30 | 08 | 10 |
20 | 07 | 15 | 0E | 1C | 2A | 38 | 13 | 12 |
28 | 07 | 15 | 0E | 1C | 2A | 38 | 09 | 0A |
The base value of each row is the third one, which represents an eighth note. The other values are usually related numbers to give these normal note lengths:
- Sixteenth note†
- Dotted eighth note†
- Eighth note
- Quarter note
- Dotted quarter note
- Half note
The last two columns are used to do various kind of triplets. Since the values in the table are not always divisible by three, triplets need to be made by rounding. However, that would introduce error in the length of the notes, so a second value is used for the final triplet to correct that error. For example, in row $10
, one would use $07
, $07
, $06
for eighth note triplets. These lengths add up to 20 which is exactly twice the eighth note length of 10 in that row.
† Row $08
has weird values for columns 0 and 1 that are best avoided.
Pitch LUT
This table is used to detemine the pitch of a note. The note data is stored as described above, and the fact that the least significant bit is masked out is advantageous since the pulse channels can use 11 bits of timer data, so each entry in this table is actually two bytes wide.
Rather than show the raw bytes here, it's more useful to show the rough note that those values represent. If the raw values are interesting to you, check the ROM for them. It's worth noting that some of the higher notes are several cents off from the listed note.
00 | 02 | 04 | 06 | 08 | 0a | 0c | 0e | |
00 | C3 | --- | E3 | G3 | G#3 | A3 | A#3 | B3 |
10 | C4 | C#4 | D4 | D#4 | E4 | F4 | F#4 | G4 |
20 | G#4 | A4 | A#4 | B4 | C5 | C#5 | D5 | D#5 |
30 | E5 | F5 | F#5 | G5 | A5 | A#5 | B5 | C#3 |
40 | D3 | D#3 | F3 | F#3 | G#5 | C6 | C#6 | D6 |
50 | D#6 | E6 | F6 | F#6 | G6 | G#6 | A6 | A#6 |
60 | B6 | C7 | C#7 | D7 | D#7 | E7 | F7 | F#7 |
70 | G7 | G#7 | A7 | A#7 | B7 | C8 |
Value $02
represents a rest.
Note that although the table goes through $7A
values above
$3E
aren't usable due to the 5 bit limit for pitch data in
the song data. However, the sound effects routines make use of these
extra notes. For example, the sword beam effect alternates between
playing C7 and F6.
Noise Samples
Several sound effects use the same routine to load "samples" for the noise channel to play. These values have the noise volume in the high nybble and the noise period in the low nybble.
$90E8 - Sword slash $9123 - Enemy hurt $912C - Crumble block
Variables
This is just a dump of RAM addresses used by the music engine and a short description of what they are.
$00E0 - Current phrase note data address (low byte) $00E1 - Current phrase note data address (high byte) $00E2 - Song offset, converted from song id to be an index instead of a bit field $00E3 - Current phrase index $00E4 - Drums loop start $00E5 - Tempo $00E6 - Pulse 1 low bits (used for vibrato effect) $00E7 - Pulse 2 low bits (used for vibrato effect) $00E8 - Pitch storage, temporary $00E9 - Play sound effect $00EA - Disable music $00EB - Request new song $00EC - Play sound effect $00ED - Play sound effect $00EE - Play sound effect $00EF - Play sound effect
$0707 - Current world $075F - Queued song, loaded after screen transitions $07DA - Ganon Laugh sample $07DB - Song to resume after fanfare $07DF - ??? Likely sound effect flag for pulse 1 channel override
$07E0 - ??? Likely sound effect flag for noise channel $07E2 - Pulse 2 envelope index $07E3 - Pulse 1 envelope index $07E4 - Drums current note duration $07E5 - Bass current note duration $07E6 - Harmony current note duration $07E7 - Melody current note duration $07E8 - Drums next note index $07E9 - Bass next note index $07EA - Harmony next note index $07EB - Melody next note index
$07EC - Ganon Laugh counter $07ED - Sound FX counter (E9) $07EE - Sound FX counter (E9) $07F5 - Sound FX counter (ED)
$07FA - Current SFX (E9) $07FB - Current song $07FD - Current SFX (ED) $07FE - ??? Likely sound effect flag for pulse 2 channel override $07FF - Current SFX (EF)
Routines
This is a non-exhaustive list of the routines in bank 6 involving the playing of music and sound effects. Each routine also lists its address so you can search in a disassembly for the actual code for that routine if these descriptions aren't clear.
Main Music Loop $9000
Checks that the music disable flag is off, then runs several SFX routines. After the SFX routines, the main music routine runs. After all this, various music related memory locations are cleared.
Configure Pulse1 Channel $9031
Takes registers x and y and configures the APU registers $4000
and $4001
respectively.
Configure Pulse2 Channel $9038
Takes registers x and y and configures the APU registers $4004
and $4005
respectively.
Play Note Pulse1 $9042
Takes a pitch index in register a and plays the note on the pulse 1 channel.
Play Note $9044
Called by the various play note routines. Takes a pitch index in register a and
uses register x as a channel selector. For the pulse channels, this routine
also saves the lower 8 bits of the APU timer register to $E6
or $E7
which
are used for a vibrato effect.
Play Note Pulse2 $9067
Takes a pitch index in register a and plays the note on the pulse 2 channel.
Play Note Triangle $906B
Takes a pitch index in register a and plays the note on the triangle channel.
Get Note Duration $906F
Takes note data in register a and looks up the duration. The original note
data is saved in register x and the result of the duration lookup is put in
register a. The duration lookup is offset by the tempo stored in $E5
.
Vibrato Pulse1 $9089
Applies a vibrato effect on the pulse 1 channel. Takes the duration of the note left in register a and the APU timer low bits in register y.
Vibrato Pulse2 $9097
Applies a vibrato effect on the pulse 2 channel. Takes the duration of the note left in register a and the APU timer low bits in register y.
Vibrato $909E
Takes the duration of the note remaining in register a, the low bits of the APU timer in register y, and the channel to use in register x. This is called by the other vibrato routines after setting x appropriately.
Uses the $04
bit of the duration to either add or subtract 2 from the low
timer bits. Only the low bits are used because writing to the high bits resets
the phase of the channel which can cause clicks. None of the notes from the
pitch LUT are close enough to the boundary for the change to require the high
bits to change anyway, so this works out fine.
Play E9 SFX $920B
Plays the sound effect requested in $E9
.
TODO document further.
Play EF SFX $92F4
Plays the sound effect requested in $EF
.
TODO document further.
Play EE SFX $9408
Plays the sound effect requested in $EE
.
TODO document further.
Play Noise $959D
Configures the APU noise registers to make a noise burst. Takes configuration
for registers $400F
, $400E
, and $400C
in registers y, x, and a.
Crumble Bridge SFX $956C
Starts playing the sound effect for a crumble bridge crumbling. This uses both the noise and the triangle channels so it sets flags to disable the music on those channels. Continues with SFX Noise Decay.
SFX Noise Decay $9587
Counts down the timer in $07F5
and turns off the noise when it reaches 0.
Also clears the flag for the ED sound effect in $07FD
.
Play ED SFX $95A7
Plays the sound effect request in $ED
or continues the sound effect saved in
$07FD
. Existing sound effects which are higher than the current sound effect
will be ignored. If the same sound effect is requested that is already playing,
it will restart the sound effect except for $80
which gives priority to
continuing. When the sound is continued instead of newly played, the decay
routine will be called instead.
$02
- Crumble Bridge
$04
and$10
- Crumble Block / Enemy Hurt
$20
and$80
- Sword Strike
$40
- Very short noise burst, probably unused. Doesn't store the effect, and doesn't have an associated decay.
Crumble Block / Enemy Hurt SFX $95E6
Starts playing the sound of a block breaking or an enemy getting hurt.
Crumble Block / Enemy Hurt SFX Decay $95EE
Looks up noise register values based on the timer and the sound effect value.
Crumble Block samples are at $912C
while Enemy Hurt is at $9123
. Continues
on to Play Noise Samples.
Play Noise Samples $9612
Takes a sample value in register a. The low four bits of this are used to set
the noise period via APU register $400E
. The high four bits are used to set
the noise volume via APU register $400C
.
Continues on the SFX Noise Decay process.
Sword Strike SFX $9604
Starts playing the sound of swinging your sword. This looks up samples based on
the duration left at $90E8
and plays them via Play Noise Samples.
Play EC SFX $990B
Plays the sound effect requested in $EC
.
TODO document further.
Play Music $9B18
Plays the music for the game.
Runs Load Song if a new song is requested in $EB
. Otherwise, runs Play Notes
if the current song is set in $07FB
. If neither of these is true, do nothing.
Next Song $9B24
Checks if the previous song should loop. If not, calls Mute All and returns.
If the previous song was $01
(the "intro" to the main theme), sets the next
song to $02
(the main theme loop).
If the previous song was $10
(the item get / level up fanfare), sets the
current song from $07DB
. Otherwise, leaves the current song the same as the
previous song.
Proceeds to Load Song.
Mute All $9B3B
Clears the current song at $07FB
and sets the APU registers to mute the volume
on both pulse channels, the triangle channel, and the noise channel.
Load Song $9B61
Takes a song id in register a and preps some variables for that song.
If the song is $10
(item get / level up fanfare) then it first saves the
previous song to $07FB
if it is less than $10
so that the previous music
will resume after the fanfare.
Sets the phrase counter at $E3
to 0 and the song offset at $E2
based on the
song id. Song ids are a bit field, and the song offset is the position of the
lowest bit in the song id for indexing into the song table.
Proceeds to Load Song Data.
Load Song Data $9B80
Loads the music variables based on the current world and current song offset.
There are four blocks of code that are basically the same, for each of the four different music tables. Each one just uses a different position for the tables to load.
Based on the current world in $0707
it loads data from these offsets:
$00
- Overworld music -
$9B87
$01
or $02
- Town music -
$9BC6
$03
or $04
- Palace music -
$9C05
$05
or higher
- Great Palace music -
$9C42
First, uses the song offset in $E2
to index into the song table at the base
offset given above.
The phrase counter at $E3
is added to that and used as in index again to
retrieve the current phrase offset. If the current phrase offset is $00
that
means the song is over and the process goes back to the Next Song routine.
Otherwise, the following structure is read at the base offset above + phrase offset just calculated.
$E5
Tempo$E0
Note Data Low$E1
Note Data High$07E9
Bass Note Pointer$07EA
Harmony Note Pointer$07E8
Drum Note Pointer
After this, the drum loop start point at $E4
is also set to the same value as
the drum note pointer. The melody note pointer at $07EB
is set to $00
, and
all the durations (melody at $07E7
, harmony at $07E6
, bass at $07E5
, and
drums at $07E4
) are set to $01
.
Proceeds to Play Notes.
Play Notes
This routine is broken into four major and similar sections: one each for melody, harmony, bass, and drums. The general process is as follows:
decrement duration if duration == 0 { load next note increment note pointer set duration for note if no overriding sound effect { play note on appropriate channel } }
Some channels have unique processing at different points in this. For example,
the melody note data is null terminated, so if the loaded note in $00
then the
routine will jump to Load Song Data instead to go to the next phrase. If the
drums channel gets a $00
note it will reset the drums note pointer to the
drums loop start and repeat the phrase. The bass and harmony channels don't
have this looping behavior. This means that if the bass or harmony channels
aren't long enough, the engine will happily just play whatever data comes next
in the ROM.
The melody is played on pulse 2 channel, the harmony on pulse 1 (which gets subverted by sound effects that need just one pulse channel), the bass on the triangle channel, and the drums on the noise channel.
For the pulse channels, after the note is played, the envelope counter for that
channel ($07E2
for pulse 1, $07E3
for pulse 2) is set. Normally the
envelope is set to $2F
but song $40
(crystal fanfare and final boss music)
sets it to $18
instead. The envelope is not set if no note was played because
the pitch index was $02
which indicates a rest.
Also for the pulse channels, after the above pseudocode, the vibrato and
enveloping happens as long as there is no overriding sound effect. The envelope
works by checking the envelope counter set previously and decrementing it. The
counter value is halved and used to look up the envelope value in the LUT at
$9135
. These values are written directly to APU register $4000
or $4004
depending on the channel.
The bass channel has one special handling as well. The APU register $4008
is
set differently based on the current song id. For song $10
(item get / level
up fanfare) the register is set to $60
but all other songs use $1f
.
The noise channel is not affect at all by the pitch. Any pitch value that is non zero will play a burst of noise.