-
Notifications
You must be signed in to change notification settings - Fork 0
/
Mother Music Format.txt
852 lines (702 loc) · 42 KB
/
Mother Music Format.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
MOTHER MUSIC FORMAT
v1.0
By Justin Olbrantz (Quantam)
The permanent home of this specification and others, with the latest updates, is https://github.com/TheRealQuantam/RetroDocs.
The Mother sound engine is the final version of the Hirokazu Tanaka engine which was derived from the Yukio Kaneoka engine dating all the way back to Nintendo's earliest NES games by Research and Development 1. An earlier version of the Tanaka engine would be used for the well-known Metroid and Kid Icarus, and the Mother engine would take Tanaka and R&D1 through the end of the NES era. Apart from Mother, other hit games to use the Mother engine include Dr. Mario and the legendary Tetris.
The Tanaka-Kaneoka engines show a clear progression of features culminating in the Mother engine. Most of the awful inflexibility of the Metroid engine previously documented is gone, with new music data-based mechanisms for timbre, tempo, and looping control. The engine appears to take some inspiration from the R&D4 engines such as that of Super Mario Bros. 3, adopting a per-channel version of their block/playlist structure. While the Metroid engine was decidedly a hybrid system, mixing music data with hard-coding, only 1 (new) part of the Mother engine is a hybrid: the pitch envelope system. Overall the engine is vastly more flexible than the Metroid engine and highly efficient at eliminating redundancy in music data, but the block system makes the engine vastly more complicated to compose for.
Basic Features:
- Single byte notes and rests, though current note length must be previously defined with an additional byte
+ Hybrid tempo system that mimics staff notation divisive rhythm
- 5-octave chromatic scale from A1 (55 Hz) to E7 (2637 Hz) for square wave (game-specific); triangle is 1 octave lower.
- 9 note lengths from whole notes through 16th notes with dots and triplets for some lengths for all tempos, plus several tempo-specific lengths (game-specific)
- 8 tempos from 82 to 225 BPM (game-specific)
- 5 channels - square 1, square 2, triangle, noise, DMC - with 4 separate event sequences (DMC events are encoded on the noise channel)
+ Multi-timbre support
- Square channels: duty cycle, channel volume or non-looping volume envelopes, looping pitch envelopes
- Triangle channel: looping pitch envelopes
- Global set of 10 noise presets (game-specific)
- Global set of 2 DMC presets (game-specific)
- Single-level unnested loops
- Per-channel block structure allow reuse of data as well as more than 256 bytes per channel
- Global transpose setting
Major Differences with the Metroid Engine:
- Timbre, global tempo, and triangle auto-release now controllable by music data
- Support for DMC sounds
- New volume envelope format that is half the size but doesn't allow modification of duty cycle
- Pitch envelopes
- Global transpose setting settable by music data
- Track looping is now per-channel and no longer limited to whole track looping
- Per-channel block structure allows reuse of data as well as more than 256 bytes per channel
Some general notes in interpreting this specification:
- All word (16-bit) values in the format are little-endian, with the least-significant byte preceding the most-significant, unless noted otherwise.
- All values are unsigned unless noted otherwise.
- All numbers preceded by $ are hex, and in general unless they represent byte strings or addresses most numbers without $ are in decimal.
- Byte values are generally shown as hex "ab" where "a" is the high nibble and "b" is the low nibble (standard mathematical digit ordering). However, when it is necessary to show bit fields, bytes are shown in binary as "abcd efgh" (mathematical order); in bit fields "-" means that the value of the bit does not matter in the given context.
- Many command lists (e.g. channel data commands) are encoded ambiguously, where 1 value could be interpreted as multiple different commands (e.g. 00 could match 0n). These lists are ordered such that the first matching command is the correct interpretation.
Track Structure
At the highest level Mother's track structure is almost identical to Metroid's: a table of offsets into blocks containing the headers for each track; however, the headers contain less metadata and the metadata they do contain (such as tempo and transpose) are now merely the initial values, and may be changed by music data during play. However, due to the amount of music in an RPG like Mother and the self-imposed limitation of 256 byte-large blocks containing track headers, Mother has 2 banks of music, 1 for "low" tracks 1-$18 and the other for "high" tracks $19-$31; it is worth noting that these banks are nearly packed to the gills, with the maximum of $19 tracks per bank being possible with the 256-byte limit.
What is VERY different, however, is what the header's channel data addresses point to. Rather than pointing to the actual music data as in Metroid, they point to a playlist of blocks for the channel. This is an array of 16-bit words that specify the addresses of blocks of music data and in which order they should be played, terminated either by an end of track code or a goto code that specifies the channel should continue from a specified location in the playlist. The playlist performs the function of (infinitely) looping the track (though note that some tracks loop on some channels but eventually end the track on another), reusing blocks of music data like "subroutines" in other engines, and freeing the engine from the Metroid engine's limitation of 256-byte channel data (now each block is limited to 256 bytes); they may even be used for example to have square 1 and square 2 play the same sequence of notes with a delay on 1 channel or different timbre by having different first blocks that then both jump to the same playlist location. And while it is not recommendable to perform local loops with the playlist as channel music data loops are more efficient for that purpose, the playlist may be used to add additional levels of looping to a track.
Accordingly, the "end of track" 0 command in music data now means "end of block", and the "end of track" command is performed by the playlist. This means that most blocks on all channels will be terminated, however it is still possible for the final blocks of some channels to be unterminated because another channel will end the track before erroneous data is played.
Bank configuration:
$8000-$9fff: Bank $1c
$a000-$bfff: Bank $1d
1c:903e byte[$18]: Low track header offset table
1c:9056 byte[$19]: High track header offset table
1c:906f struct[$18]: Low track header block
1c:915f struct[$19]: High track header block
Track Header Format:
+0 byte: Initial global transpose in modified sign-magnitude format with the format nmmm mmmm. If n is clear the transpose value = +m; if n is set the transpose value = -1 - m. The transpose value must be an even number, as it is 2x the number of semitones and fractional semitones are not supported.
+1 byte: Initial note length table offset (implicitly, tempo). See Time and Tempo for a list of possible values.
+2 word[4]: Channel playlist addresses in order of square 1, square 2, triangle, and noise, or a high byte of $ff (canonically $ffff) if none.
Example
81 0C 8F 99 97 99 9F 99 A7 99
81: Initial global transpose
n = 1: Negative
m = 1: With n = 1 the transpose value = -1 - 1 = -2 (-1 semitone)
0C: Initial note length table offset
t = $c: Table offset $c: 180 BPM
8F 99: Square 1 channel playlist address $998f
97 99: Square 2 channel playlist address $9997
9F 99: Triangle 1 channel playlist address $999f
A7 99: Noise channel playlist address $99a7
Block Playlist Format (word values, shown in mathematical order, not their physical little-endian byte order):
00--: Global end of track. All channels stop when this is encountered on any channel.
ff-- AAAA: Goto. Set the current playlist location address (not block address) to A.
AAAA: Play the block of music data at address A.
Examples
Square 1 channel: 62 99 00 00
62 99: Play block address $9962
00 00: End of track
Square 2 channel of SAME track: 71 99
71 99: Play block address $9971
Note that there is NO termination for this channel, as the track will be terminated by the end of track command on square 1.
Square 1 playlist at $b0f8: 0C B1 15 B1 FF FF 02 B1
0C B1: Play block address $b10c
15 B1: Play block address $b115
FF FF 02 B1: Go to playlist address $b102
Square 2 playlist at $b100 of same track: 12 B1 1F B1 FF FF 02 B1
12 B1: Play block address $b112
1F B1: Play block address $b11f
FF FF 02 B1: Go to playlist address $b102. This looks familiar.
Triangle playlist of same track: 2F B1 00 00
2F B1: Play block address $b12f
00 00: End of track
Note 2 things here. $b102 is the second entry in the square 2 playlist (block $b11f); in other words, the square channels have a couple initial unique blocks before merging to the same data (square 1 is delayed by 26 frames, among other things). Second, triangle block $b12f is much longer than any of the square channel blocks (16 seconds per loop), so the square channels loop repeatedly until the triangle channel terminates the track at 8:33. The irony of this is that in fact the triangle channel IS looping (32 loops of 16 sec), but it's looping using the music data loop construct within the block; it is not at all clear why it was not simply made to loop infinitely like the rest.
Time and Tempo
The note length system in Mother is identical to that in Metroid, save that music data may change the table offset during play. Note/rest durations are frame-based, and a master table specifies note durations in frames. At any given point the track has a $10-element window into this table for use by all channels, and set note length commands select 1 of these lengths. This table is arranged (and used in game music) to provide 8 different tempos with a common set of 9 musical lengths with common indices; various other lengths are available based on the window used for the track, but they are not common between tempos.
The master table giving lengths in frames is:
0 : 4 8 16 32 64 24 48 12 10 5 2 1 5 10 20 40
10: 80 30 60 15 12 6 3 2 6 12 24 48 96 36 72 18
20: 16 8 3 1 4 2 0 144 7 14 28 56 112 42 84 21
30: 18 9 3 1 2 8 16 32 64 128 48 96 24 21 10 4
40: 1 2 192 9 18 36 72 144 54 108 27 24 10 20 40 80
50: 160 60 120 30 26 13 5 1 2 23 11 22 44 88 176 66
60: 132 33 29 14 5 1 2 23
When arranged by the base offsets used by the game the table looks like this:
0 1 2 3 4 5 6 7 8 9 a b c d e f
BPM 16 8 4 2 1 4D 2D 8D 4T 8T 32
0 (225): 4 8 16 32 64 24 48 12 10* 5* 2 1
c (180): 5 10 20 40 80 30 60 15 12* 6* 3* 2
18 (150): 6 12 24 48 96 36 72 18 16 8 3 1 4 2 0 144
28 (129): 7 14 28 56 112 42 84 21 18* 9* 3* 1 2
35 (113): 8 16 32 64 128 48 96 24 21* 10* 4 1 2 192
43 (100): 9 18 36 72 144 54 108 27 24 * *
4c (90): 10 20 40 80 160 60 120 30 26* 13* 5 1 2 23
5a (82): 11 22 44 88 176 66 132 33 29* 14* 5* 1 2 23
Using these windows, the following standard length codes are defined:
Whole Half 4th 8th 16th 32nd
Normal: 4 3 2 1 0 a*
Dotted: 6 5 7
Triplet: 8* 9*
* Triplets and 32nds in some tempos are not whole numbers of frames, and will not properly mix with other lengths. These can either be avoided in those tempos or worked around by varying the lengths of notes of that type by 1 frame or inserting short (e.g. 1 frame) rests so that 3 triplets or 2 32nds add up to the correct whole number of frames. Note critically that all lengths beyond quarter triplet are MISSING for 100 BPM.
Notes on the triangle wave channel are capable of auto-release prior to the end of the specified note duration. Depending on the current auto-release settings specified by the set timbre command on the triangle channel, notes may be set to either never auto-release, auto-release after a fixed number of frames (up to 8), or dynamic auto-release of notes 1 frame before the end of the note or after 15 frames, whichever is sooner.
Notes and Rests
The Mother engine uses 3 different key sets: 1 for melodic channels (though note that triangle is 1 octave lower than square channels) and 2 for the noise channel (1 for noise presets and 1 for DMC presets).
1 oddity of the Tanaka-Kaneoka engines is that rather than keys being numbered sequentially, key values for everything except DMC presets are actually byte offsets into various lookup tables (this made somewhat more sense for the Kaneoka engine and Kondo engine derived from it). For melodic keys that table is the pitch period table which has word-sized elements, resulting in all valid key values being even. For these keys it's easiest to pretend that the bottom bit is not part of the key value and must simply be 0, but the transpose feature added in the Mother engine exposes this detail as the transpose value is added directly to the key value to get the byte offset into the array. While not at all recommendable, it's possible to have both key values and transpose values be odd to produce an even sum that will correctly reference a key pitch. For this document we will pretend that melodic key numbers exclude the bottom bit, though there is 1 place where the truth of the matter can't be hidden.
The melodic key set has a nominal base key of A#1 for all known games, though for unknown and possibly historical reasons its key 0 is A1 and key 1 is rest. While the engine is capable of using key numbers 2-$4d and $50-$57 (additional ranges with an exploit), no known game uses the entire available range.
Mother's Key Table:
Oct C C# D D# E F F# G G# A A# B Max Detune at B
1: 0* 0.96 cents
2: 2 3 4 5 6 7 8 9 a b c d 1.91 cents
3: e f 10 11 12 13 14 15 16 17 18 19 3.83 cents
4: 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 7.69 cents
5: 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 15.49 cents
6: 32 33 34 35 36 37 38 39 3a 3b 3c 3d 31.41 cents
7: 3e 3f 40 41 42
* Key code 0 is an exploit: if key code 0 IMMEDIATELY follows a set length command it will be assumed to be a note/rest rather than being interpreted as an end of block command. While not relevant to any known game, this same exploit can be used with keys $4e-$4f and above $57.
Like the true way melodic keys work, noise codes are indices into an array of noise presets, though here they are 1-based offsets into entries of 3 bytes. Rest is code 1, and some games like Mother exploit the fact that only the first byte of the 3 is necessary to silence the channel to place the first preset at 2. All together, Mother supports the following presets: 2, 4, 7, $a, $d, $10, $13, $16, $19, $1c.
Finally there are the DMC presets. Each byte-sized key/rest command on the noise channel is actually 2 commands in 1: the low bits contain a noise preset code and the high bits contain a DMC preset code. There are actually 2 variants of the Mother engine, depending on whether 2 bits or 3 are dedicated to the DMC code. Mother uses 2 bits and is limited to 3 DMC presets, although it only uses 2. In both versions of the engine DMC key code 0, as well as any other unused codes, is rest for the DMC channel.
DMC Presets:
1: Kick/bass drum
2: Snare drum
Channel Data
1 major difference between the Tanaka-Kaneoka engines and non-Nintendo engines is that while each channel has separate music data, the end of track command (part of the playlist, in the Mother engine) applies globally, and all channels are affected when ANY channel encounters it. Because of this, it is not necessary to explicitly terminate all channels for tracks that do not loop, only the 1 that will reach its end first. Unlike the Metroid engine, however, all channels must be made to loop for looping tracks.
Channel Data Format:
00: End of block. Advance to the next entry in the channel's playlist.
9c nmmm mmmm: Set global transpose. If n is clear the transpose value = +m; if n is set the transpose value = -1 - m. The transpose value must be an even number, as it is 2x the number of semitones and fractional semitones are not supported.
9e tt: Set global note length table offset (implicitly, tempo) to t
ff: End loop. If loop counter is not 0 decrement and loop, else continue. MUST be in the same block as its start loop command.
bL: Set current channel note length code to L. For triangle set linear counter according to current triangle auto-release settings. Must be IMMEDIATELY followed by a note/rest.
cL (11nn nnnn): Begin loop and set number of times to play loop to n (1-$3e or 0 is interpreted as $100)
Melodic Channels Only:
9f pppx xxxx CC: Set channel timbre. MUST NOT be used on noise channel. Set pitch envelope to p (0 if none) and base ctrl 1 value to C.
For square channels: Set volume envelope to x (0 if none), and C has the form ddLV vvvv:
d: Duty cycle: 12.5% (0), 25% (1), 50% (2), or 75% (3)
L: Disable length counter and play forever. Should always be set, as the engine does not provide any control over the length counter.
V: If set v is the channel volume, else v is the volume decay speed. Should always be set.
v: Channel volume 0-$f.
For triangle channel: p (in addition to setting the pitch envelope), x, and C control auto-release in a way that seems contradictory and will be clarified later in this section. C has the form Fnnn nnnn:
F: If set disables auto-release if n is non-0. If clear, apply n.
n: The number of quarter-frames to play notes before auto-release.
p and x set the auto-release settings:
x != 0: Set auto-release time to x quarter-frames
p is 0-1 or 6-7: Set dynamic auto-release
p is 2-5: Disable auto-release
02: Rest for current note length. Acts as a rest if EITHER the unmodified byte OR byte + transpose value is 02. E.g. the command 9C 83 would set a transpose value of -2 semitones, and a 06 "note" would then resolve to a rest (6 - 4 = 2).
kkkk kkk0: Play key k + transpose value/2 for current note length. This sum cannot be 1.
Noise Channel Only:
DDpp pppp: Perform noise channel command p and DMC channel command D for current note length
Noise channel:
p = 1: Noise channel rests
p > 1: Play noise preset p
DMC channel:
D = 1-2: Play DMC preset D
Else: DMC channel rests
As previously stated, any byte immediately following a set length command is interpreted as a note/rest, allowing access to keys with the same values as other commands; however, as no known game uses keys outside the normally accessible range, this only (additionally) allows access to key code 0.
Some notes on the set timbre command are in order. The ctrl 1 register ($4000, $4004, etc.) value specified is the base value for the channel, and can be modified in a couple ways. For square wave channels, if the channel uses a volume envelope the bottom 4 bits of this value (the volume field) will be replaced by the current volume from the envelope. However the initially specified volume is retained, and will be restored if the envelope ends by restoring the base volume.
On the triangle channel the ctrl 1 register controls auto-release at the hardware level, and the set note length and set timbre commands fight over what is written to this register. The set timbre command sets the auto-release settings and ctrl 1 value as specified; the set note length command sets ctrl 1 BASED ON the current auto-release settings. The outcome will be different depending on the order of commands. If the set timbre command comes last, auto-release will be directly controlled by the ctrl 1 value specified, and the auto-release settings specified won't take effect until the next set note length command. If the set note length command comes last, the ctrl 1 value specified in the set timbre command will be completely overwritten based on the auto-release settings.
Examples
Square channel (current note length base offset is +4c: 90 BPM):
9F B3 31: Set timbre: envelopes and base ctrl 1
p = 5: Pitch envelope 5
x = $13: Volume envelope $13
d = 0: Duty cycle 0 (12.5%)
L = 1: Disable length counter
V = 1: v is volume
v = 1: Base volume 1. Will not actually be used as volume envelope $13 does not restore base volume.
D2: Begin loop
n = $12: Play loop $12 times
B2: Set note length
L = 2: Quarter note (40 frames)
1A x3: Notes
k = $d: Key B2
B1: Set note length
L = 1: 8th note (20 frames)
1A: Note
k = $d: Key B2
1C: Note
k = $e: Key C3
FF: End loop
Triangle channel of same track:
9F A0 00: Set timbre: pitch envelope, auto-release settings, and base ctrl 1
1:x = 0: No explicit auto-release length
1:p = 5: Pitch envelope 5. Auto-release disabled.
2:F, 2:n: Ignored since set note length is done before notes are played
B7: Set note length. Also overwrites F and n from previous command with auto-release settings from p and x: disable auto-release.
L = 7: Dotted 8th note (30 frames)
24: Note
k = $12: Key E2 (triangle is 1 octave lower)
B0: Set note length
L = 0: 16th note (10 frames)
02: Rest
Noise channel of same track:
D4: Begin loop
n = $14: Play loop $14 times
B2: Set note length
L = 2: Quarter note (40 frames)
41: Note event
D = 1: Play DMC preset 1 (kick drum)
n = 1: Noise channel rests
44: Note event
D = 1: Play DMC preset 1
n = 4: Play noise preset 4
41: Note event
D = 1: Play DMC preset 1
n = 1: Noise channel rests
07: Note event
D = 0: DMC channel rests
n = 7: Play noise preset 7
FF: End of loop
Square channel:
9C 8D: Set global transpose
n = 1: Negative
m = $d: Transpose value = -1 - $d = -$e (-7 semitones)
00: End of block
Triangle channel:
9F 0A 00: Set timbre: pitch envelope, auto-release settings, and base ctrl 1
1:x = $a: Set duration until auto-release to $a quarter-frames
1:p = 0: No pitch envelope
2:F, 2:n: Ignored since set note length is done before notes are played
B3: Set note length
L = 3: Half note (56 frames at current tempo). Notes will auto-release after 10 quarter-frames.
...
9F 00 00: Set timbre: pitch envelope, auto-release settings, and base ctrl 1
1:x = 0: No explicit auto-release length
1:p = 0: No pitch envelope. Do NOT disable auto-release; result: dynamic auto-release.
2:F, 2:n: Ignored since set note length is done before notes are played
B3: Set note length
L = 3: Half note (48 frames). Notes will auto-release after 15 frames.
...
B1: Set note length
L = 1: 8th note (12 frames). Notes will auto-release after 11 frames.
Timbre and Envelopes
Depending on the channel, up to 3 types of timbre control exist in the Mother engine: the ctrl 1 register (especially the duty cycle for square wave channels), volume envelopes, and pitch envelopes, all of which are set by the set timbre command.
Volume envelopes in the Mother engine are fairly short non-looping sequences of volume values that end either by restoring the base volume for the channel or silencing the channel. To reduce the space required, the Mother engine packs 2 volume values into each byte of the envelope. The engine theoretically supports up to $1f volume envelopes, but Mother has $1b envelopes.
Examples
A8 76 FF: Short decay envelope into sustain. Frames 0-3 will have the volume values $a, 8, 7, 6, then the channel base volume will be used.
91 91 91 91 91 91 91 91 91 91 F0: Odd short, non-sustaining, 30 Hz tremolo envelope of center 5 and depth (center to peak) 4 that silences the channel after 20 frames (1/3 sec). 30 Hz is above the limit of hearing, so this will likely have a peculiar frequency modulation effect.
Pitch envelopes are the only truly janky feature of the Mother engine. They are a hybrid system that mixes period-linear absolute (i.e. not relative to the previous frame) period offset arrays with hard-coded behavior (including the length and loop points of the arrays). Making matters worse is the fact that on the triangle channel the "forever" field of the auto-release settings is encoded in the same bits as the pitch envelope number, meaning only certain envelope/auto-release setting combinations are possible. While it is entirely conceivable that the set of envelopes could be game-specific, all games I've investigated have had the same set of pitch envelopes.
A basic description of the pitch envelopes is as follows, showing the envelope number, "forever" field, and a basic description; for a more detailed description see the additional information section.
E F Description
1: 0 6 Hz vibrato with depth (center to peak) of 2 period units
2: 1 Slides period from 0 to -10 over $10 frames, performs 15 Hz depth 1 vibrato for 2 cycles, then sustains a constant -10 period offset
3: 1 Fixed -2 period offset
4: 1 Pitch slide from +9 to 0 period offset followed by 6 Hz depth 1.5 vibrato. Is NOT applied to key $23.
5: 1 6 Hz depth 2 vibrato that has a fade-in period of $21 frames
6: 0 Pitch slide from +9 to 0 period offset followed by 6 Hz depth 1.5 vibrato. Is NOT applied to key $23.
7: 0 6 Hz depth 2 vibrato
Additional Tables and Information
Track Table
Track bank 0: Header offset table 903e, header block base 906f:
1 Eight Melodies @906f: +24 +18 ffff ffff 76c ffff
Triangle @ 76c: 92aa 92b1 92bc 92c7 92d3 92df 92e8 92f5 9301 0000
2 Battle Theme 1 @9079: +0 +28 9309 9311 931b 932d
Square 1 @9309: 9592 | 9335
Square 2 @9311: 95a4 | 940b 944e
Triangle @931b: 95b6 | 94e7 94f9 94e7 94e7 94f9 952a
Noise @932d: 95c8 | 9543
3 Battle Theme 2 @9083: +0 +28 956e 9578 9582 958a
Square 1 @956e: 9592 | 95cb 960d
Square 2 @9578: 95a4 | 95ec 96b1
Triangle @9582: 95b6 | 971f
Noise @958a: 95c8 | 977a
4 Battle Theme 3 @908d: +0 +28 979e 97a8 97b2 97be
Square 1 @979e: 9592 | 97c8 9800
Square 2 @97a8: 95a4 | 9814 9895
Triangle @97b2: 95b6 | 98df 98fe 98f3
Noise @97be: 95c8 | 992b 992b
5 Victory @9097: +0 +0 995a 995e 9960 ffff
Square 1 @995a: 9962 0000
Square 2 @995e: 9971
Triangle @9960: 9980
6 Pollyanna (I Believe in You) @90ab: +0 +35 9b0e 9b18 9b22 9b2a
Square 1 @9b0e: 9b32 | 9bab 9bef
Square 2 @9b18: 9b53 | 9c96 9ccd
Triangle @9b22: 9b74 | 9d05
Noise @9b2a: 9b8c | 9c3f
7 Bein' Friends @90a1: +0 +28 9d84 9d90 9d9c 9da8
Square 1 @9d84: | 9db4 9e29 9e29 9ec2
Square 2 @9d90: | 9ddb 9e4e 9e4e 9f17
Triangle @9d9c: | 9e07 9e72 9e72 9f5a
Noise @9da8: | 9e15 9eae 9eae 9fab
8 Advent Desert @90b5: -2 +c 998f 9997 999f 99a7
Square 1 @998f: 99af | 99cb
Square 2 @9997: 99b8 | 9a3d
Triangle @999f: 99c1 | 9aea
Noise @99a7: 99c7 | 9b03
9 Magicant @90bf: +0 +4c a083 a08b a091 a09f
Square 1 @a083: a0a5 | a0af
Square 2 @a08b: | a0ac
Triangle @a091: | a11b a127 a138 a138 a149
Noise @a09f: | a161
a Snow Man @90c9: +0 +35 a171 a187 a18d ffff
Square 1 @a171: a1fd | a207 a22c a207 a254 a272 a28c a272 a2a0
Square 2 @a187: a203 ffff a173
Square 2 @a173: | a207 a22c a207 a254 a272 a28c a272 a2a0
Triangle @a18d: | a197 a197 a1d0
b Mount Itoi @90d3: +0 +4c a2c4 a2ca a2d0 a2d6
Square 1 @a2c4: | a2dc
Square 2 @a2ca: | a2f6
Triangle @a2d0: | a33f
Noise @a2d6: | a36d
c Factory @90dd: +0 +35 a68e a686 a698 a6a0
Square 1 @a68e: a707 | a6b1 a6f4
Square 2 @a686: | a6ae a6f1
Triangle @a698: | a70d a716
Noise @a6a0: | a72a a73d a74f a73d a755
d South Cemetery @90e7: +0 +35 a37c a384 a38c a394
Square 1 @a37c: a39c | a3fd
Square 2 @a384: a3b4 | a3fa
Triangle @a38c: a3cf | a443
Noise @a394: a3e2 | a46d
e Twinkle Elementary School @90f1: +0 +18 a480 a486 a48c a492
Square 1 @a480: | a498
Square 2 @a486: | a4dc
Triangle @a48c: | a508
Noise @a492: | a52e
f Humoresque of a Little Dog @90fb: +0 +18 a565 a56d a573 a579
Square 1 @a565: a621 | a582
Square 2 @a56d: | a57f
Triangle @a573: | a627
Noise @a579: | a65f
10 Poltergeist @9105: -8 +18 9fda 9fe4 9fee 9ff8
Square 1 @9fda: a000 | a005 a01b
Square 2 @9fe4: a023 | a023 a03e
Triangle @9fee: a046 | a051 a05f
Noise @9ff8: a068 | ad5d
11 Basement @910f: +0 +28 a768 a770 a77c a784
Square 1 @a768: a78c | a7b1
Square 2 @a770: a797 | a7be a7b4 a7e1
Triangle @a77c: a7a2 | a7ec
Noise @a784: a7ac | a803
12 My Home @9119: +2 +43 a8ec a8e6 a8f2 a8f8
Square 1 @a8ec: | a939
Square 2 @a8e6: | a903
Triangle @a8f2: | a959
Noise @a8f8: | a8fe
13 Cave 2 @9123: +0 +35 a973 a97d a985 a98d
Square 1 @a973: a9b1 aa0a | aa12
Square 2 @a97d: a995 | a9ef
Triangle @a985: a9ca | aa2a
Noise @a98d: a9e2 | aa4c
14 The Paradise Line @912d: +0 +18 bb4f bb63 bb6f bb83
Square 1 @bb4f: bb8b | bc34 bc34 bc48 bc34 bc34 bc48 bc5b
Square 2 @bb63: bbb8 | bca5 bca5 bd03
Triangle @bb6f: bbd9 | bd4d bd4d bd5b bd4d bd4d bd5b bd65
Noise @bb83: bc06 | bd86
15 Fallin' Love @9137: +0 +43 aa75 aa83 aa8d aa93
Square 1 @aa75: | aabd aac5 aac1 aac5 aaa7
Square 2 @aa83: aa99 | aa9d aad4
Triangle @aa8d: | ab0c
Noise @aa93: | ab1b
16 Mother Earth @9141: +0 +28 ba33 ba2d ba39 ba3f
Square 1 @ba33: | ba9c
Square 2 @ba2d: | ba45
Triangle @ba39: | bb17
Noise @ba3f: | bb49
17 Tank @914b: +0 +18 ab37 ab47 ab55 ab5d
Square 1 @ab37: | abce abd6 abd6 abe8 ac43 ab7a
Square 2 @ab47: | ac0a ac12 ac12 ac24 ab65
Triangle @ab55: | acf3 ab95
Noise @ab5d: | ad3d abb2
18 Ruins of Desert @9155: +0 +c a80c a814 a81a ffff
Square 1 @a80c: | a820 a827
Square 2 @a814: | a824
Triangle @a81a: | a892
Track bank 1: Header offset table 9056, header block base 915f:
19 Queen Mary's Song @915f: +0 +28 addb adc3 ffff ffff
Square 1 @addb: ade5 ffff adc5
Square 1 @adc5: | 92b1 92bc 92c7 92d3 92df 92e8 92f5 9301 ade1
Square 2 @adc3: adeb | 92b1 92bc 92c7 92d3 92df 92e8 92f5 9301 ade1
1a Wisdom of the World @9169: +0 +5a ae52 ae58 ae5e ffff
Square 1 @ae52: | ae64
Square 2 @ae58: | aec0
Triangle @ae5e: | af21
1b Tombstone @9173: +24 +4c b0e5 b0dd ffff ffff
Square 1 @b0e5: b0f1 | 9c99
Square 2 @b0dd: | b0ed 9c99
1c Game Over @917d: +0 +4c b0f8 b100 b108 ffff
Square 1 @b0f8: b10c b115 ffff b102
Square 1 @b102: | b11f
Square 2 @b100: b112 | b11f
Triangle @b108: b12f 0000
1d Big Victory @9187: +0 +18 b147 b14b b14d ffff
Square 1 @b147: b14f 0000
Square 2 @b14b: b161
Triangle @b14d: b175
1e Airplane @9191: +0 +18 af4e af54 af5a af6c
Square 1 @af4e: | af72
Square 2 @af54: | afb2
Triangle @af5a: | affd b02e b02e b09d b0ba b09d b0c4
Noise @af6c: | b03d
1f Level Up @919b: +6 +0 ae1e ae28 ae30 ffff
Square 1 @ae1e: b14f ae38 | ae43
Square 2 @ae28: b161 | ae40
Triangle @ae30: b175 | ae4f
20 Recovery @91a5: -4 +18 adef adf3 adf5 ffff
Square 1 @adef: adf7 0000
Square 2 @adf3: ae04
Triangle @adf5: ae10
21 Fanfare @91af: -4 +43 b51e b522 b524 ffff
Square 1 @b51e: b532 0000
Square 2 @b522: b526
Triangle @b524: b53f
22 Live House @91b9: -8 +18 b184 b18a b196 b1a2
Square 1 @b184: | b1a8
Square 2 @b18a: | b1c4 b1c4 b1da b1c4
Triangle @b196: | b1ed b1ed b201 b1ed
Noise @b1a2: | b212
23 All That I Needed (Was You) @91c3: +0 +18 b222 b238 b248 b25a
Square 1 @b222: b2c5 b2d9 b2d9 b31e b329 b329 b329 b34e b2d9 b31e b25e b2ae
Square 2 @b238: b2ae b37b b3b5 b37b b3b8 b37b b272 0000
Triangle @b248: b2d1 b426 b426 b443 b446 b446 b453 b426 b29b
Noise @b25a: b46a b491
24 Melody 1 - Doll @91cd: +48 +28 9259 925d ffff ffff
Square 1 @9259: 929e 92b1
Square 2 @925d: 92ae 0000
25 Melody 2 - Canary @91d7: +24 +28 9261 9265 ffff ffff
Square 1 @9261: 92a4 92bc
Square 2 @9265: 92b9 0000
26 Melody 3 - Monkey @91e1: +0 +28 9269 926d ffff ffff
Square 1 @9269: 92a4 92c7
Square 2 @926d: 92c4 0000
27 Melody 4 - Piano @91eb: +0 +28 9271 9275 ffff ffff
Square 1 @9271: 929e 92d3
Square 2 @9275: 92d0 0000
28 Melody 5 - Cactus @91f5: +48 +28 9279 927d ffff ffff
Square 1 @9279: 92a4 92df
Square 2 @927d: 92dc 0000
29 Melody 6 - Dragon @91ff: +24 +28 9281 9285 ffff ffff
Square 1 @9281: 92a4 92e8
Square 2 @9285: 92e5 0000
2a Melody 7 - EVE @9209: +48 +28 9289 928d ffff ffff
Square 1 @9289: 929e 92f5
Square 2 @928d: 92f2 0000
2b Melody 8 - Tombstone @9213: +24 +28 9291 9295 ffff ffff
Square 1 @9291: 92a4 9301
Square 2 @9295: 92fe 0000
2c Giegue @921d: +0 +43 b547 b54d ffff ffff
Square 1 @b547: | b553
Square 2 @b54d: | b559
2d Ending @9227: +0 +28 b8c0 b8b2 b8d4 b8e2
Square 1 @b8c0: b6df b931 b67c b781 b7c7 b825 b69d | b6df
Square 2 @b8b2: b6a0 b8f0 b666 b72e | b6a0
Triangle @b8d4: b724 b9ff b693 b83b | b724
Noise @b8e2: b729 ba23 b698 b882 | b729
2e Choucream Zoo @9231: +0 +28 b55f b565 a77e a786
Square 1 @b55f: | b56b
Square 2 @b565: | b573
Triangle @a77e: | a7ec
Noise @a786: | a803
2f Phone @923b: +0 +18 ffff b57b ffff ffff
Square 2 @b57b: | b581
30 Youngtown @9245: +0 +28 b589 b58f b595 ffff
Square 1 @b589: | b5a1
Square 2 @b58f: | b5cd
Triangle @b595: | b602
31 Cave 1 @924f: +0 +28 b63c b644 ffff ffff
Square 1 @b63c: b64a | b653
Square 2 @b644: | b650
Reading the table:
31 Cave 1 @924f: +0 +28 b63c b644 ffff ffff
- Track number $31. Note that this is the number used to play the track in game. To get the track's index in the bank header offset arrays, subtrack 1 or $19 from tracks in the 2 banks. Thus this is $31 - $19 = index $18 in the bank 1 header offset array.
- Track name "Cave 1"
- Track header address $924f
- +0 initial transpose value in decimal (+0/2 = +0 semitones)
- Initial note length table base offset $28 (~129 BPM)
- Square 1 playlist address $b63c, square 2 $b644
- No triangle or noise channel data for this track
Square 1 @b63c: b64a | b653
- Square 1 playlist starting at address $b63c
- Plays block $b64a, then repeatedly plays block $b653. The playlist is terminated by ffff b63e which loops as shown, but this is hidden to make the playlist more readable.
Square 1 @9291: 92a4 9301
Square 2 @9295: 92fe 0000
- Square 2 channel terminates the track with an end of track command
- Square 1 is terminated implicitly by square 2, and has no terminator of its own
Square 2 @a187: a203 ffff a173
Square 2 @a173: | a207 a22c a207 a254 a272 a28c a272 a2a0
- Square 2 channel has 2 playlist segments due to a goto command
- The goto is not hidden because it does not loop but goes to a different playlist segment. The second playlist segment does loop so its terminator is hidden.
1 Eight Melodies @906f: +24 +18 ffff ffff 76c ffff
Triangle @ 76c: 92aa 92b1 92bc 92c7 92d3 92df 92e8 92f5 9301 0000
Track 1 is very special. Its triangle channel points into RAM, as the playlist is constructed dynamically. This is the Eight Melodies track, and it is made up of 8 pieces of the melody (the inner 8 blocks). Depending on whether a piece has been picked up in game, the block will either be as shown here or a placeholder beep.
The base (no pieces acquired) version of this track is copied from 1c:8217 ($a words) during initialization. Game-specific code in the begin track process then selects either the melody piece from 1c:822b (8 words) or the placeholder beep (block address $9299) for each piece:
823B LDA $761E
823E STA $B0
8240 LDY #$00
8242 BEQ $8250
8244 LDA #$99
8246 STA $076E,Y
8249 INY
824A LDA #$92
824C STA $076E,Y
824F INY
8250 TYA
8251 CMP #$10
8253 BEQ $8269
8255 LSR $B0
8257 BCC $8244
8259 LDA $822B,Y
825C STA $076E,Y
825F INY
8260 LDA $822B,Y
8263 STA $076E,Y
8266 INY
8267 BNE $8250
8269 RTS
1c:8fd6 byte[$68?]: Note length table in frames
Code to load the current length for notes:
8CAA LDA $8FD6,Y
Code to limit the triangle auto-release length to 15 frames ($3c quarter-frames) when in dynamic auto-release mode:
8D73 CMP #$3C
8D75 BCC $8D79
8D77 LDA #$3C
1c:8f4c big-endian word[$43]: Key pitch table, in period register units. For square wave channels, period = 1789773 / frequency / 16 - 1 (triangle channel is 1 octave lower). Entry 1 must be 0 as it is rest.
Code to load the period for a key:
8CC6 LDA $8F4D,Y
...
8CE1 LDA $8F4D,Y
8CE4 STA $0780,X
8CE7 LDA $8F4C,Y
8CEA ORA #$08
8CEC STA $0781,X
1c:8928 byte[$1f?]: Noise presets table. Data actually starts at offset 1 ($b201), and is NOT an array, as some entries overlap. The first byte should always be $10 as preset 1 is rest.
+0 byte: Ctrl 1 value
+1 word: Period regs value
Code to play a noise preset:
8D93 LDA $8928,Y
8D96 STA NoiseVolume_400C
8D99 LDA $8929,Y
8D9C STA NoisePeriod_400E
8D9F LDA $892A,Y
8DA2 STA NoiseLength_400F
Code to play a DMC preset if any:
8DA6 TYA
8DA7 AND #$C0
8DA9 CMP #$40
8DAB BEQ $8DB2
8DAD CMP #$80
8DAF BEQ $8DBC
8DB1 RTS
8DB2 LDA #$0E <- preset 1: kick drum
8DB4 STA $B1
8DB6 LDA #$07
8DB8 LDY #$00
8DBA BEQ $8DC4
8DBC LDA #$0E <- preset 2: snare drum
8DBE STA $B1
8DC0 LDA #$0F
8DC2 LDY #$02
8DC4 STA DmcLength_4013
8DC7 STY DmcAddress_4012
8DCA LDA $07F7
8DCD BNE $8DE3
8DCF LDA $B1
8DD1 STA DmcFreq_4010
8DD4 LDA #$0F
8DD6 STA ApuStatus_4015
8DD9 LDA #$00
8DDB STA DmcCounter_4011
8DDE LDA #$1F
8DE0 STA ApuStatus_4015
8DE3 RTS
Volume Envelope Table
1c:8ded word[$1b]: Volume envelope table. Indices are 0-based, unlike in the set timbre commands and the following table.
Envelope 1 @8e4f: 233456776554 ff
Envelope 2 @8e56: 5a9888776666655555 ff
Envelope 3 @8e76: 111122223333444444455555556666777888 ff
Envelope 4 @8e89: f987777766655544 ff
Envelope 5 @8e92: a876 ff
Envelope 6 @8e98: 99 ff
Envelope 7 @8e48: 235678888887 ff
Envelope 8 @8e9a: dcba998887765544 ff
Envelope 9 @8ea3: 2344333333333332 ff
Envelope a @8e95: 7432 ff
Envelope b @8eac: 7776655544433221 f0
Envelope c @8eb5: 44433332221111 f0
Envelope d @8ebd: 33332222111111 f0
Envelope e @8ec5: 222222111111 f0
Envelope f @8ecc: 1111111111110100 f0
Envelope 10 @8ed5: 998877766655544433333332222222222111111111111111 f0
Envelope 11 @8f20: 23455544333322 ff
Envelope 12 @8f28: 87654321443321113221111121111111111111 ff
Envelope 13 @8eee: 6555544433333333222222221111111111111111 f0
Envelope 14 @8f3c: 666542213221111121111111111111 ff
Envelope 15 @8f03: fbbaaa99999998887777776666665554444443333322222222111111 f0
Envelope 16 @8e23: 7611111431 ff
Envelope 17 @8e60: 111122223333444444455555556666777888765432 ff
Envelope 18 @8e3f: 9876632287765311 f0
Envelope 19 @8e38: 233332222222 ff
Envelope 1a @8e2d: 91919191919191919191 f0
Envelope 1b @8e29: 334566 ff
Code to load an envelope address:
8B28 LDA $8DED,Y
8B2B STA $B2
8B2D LDA $8DEE,Y
8B30 STA $B3
Pitch Envelopes
Pitch envelopes are difficult to describe as they're a mixture of data and code. To start with, the simple envelopes that just do vibrato, #5 with a fade in (for convenience period offsets are in decimal):
Envelope 1 @8a33: | +0 +1 +1 +2 +1 +0 -1 -1 -2 -1
Envelope 5 @8a12: +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +1 +0 +0 +0 +0 -1 +0 +0 +0 +0 +1 +1 +0 +0 +0 -1 -1 +0 | +0 +1 +1 +2 +1 +0 -1 -1 -2 -1
Envelope 7 @8a33: | +0 +1 +1 +2 +1 +0 -1 -1 -2 -1
Envelope 2 is effectively as follows, but the loop at the end is actually hard-coded:
Envelope 2 @8a3d: +0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -10 -9 -10 -11 | -10
Envelope 3 is hard-coded to -2 period units. Oddly, it checks whether the key number is < $4c/2 = $26, but then sets the offset to -2 in both cases.
Envelopes 4 and 6 are simple (identical) vibrato envelopes, but hard-coded to NOT apply to key $46/2 = $23:
Envelope 4 @8a06: +9 +8 +7 +6 +5 +4 +3 +2 +2 +1 +1 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +1 +0 +0 +0 +0 -1 +0 +0 +0 +0 +1 +1 | +0 +0 +0 -1 -1 +0 +0 +1 +1 +2
Envelope 6 @8a06: +9 +8 +7 +6 +5 +4 +3 +2 +2 +1 +1 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +1 +0 +0 +0 +0 -1 +0 +0 +0 +0 +1 +1 | +0 +0 +0 -1 -1 +0 +0 +1 +1 +2
The weirdness on envelopes 3, 4, and 6 are found on all games using this engine that I've investigated.
The code. It actually starts at $89b4, with the pitch envelope index in the high 3 bits of $b0 and the current index into the envelope in $b2:
8977 LDA $B2 <- envelopes 4 and 6
8979 CMP #$31
897B BNE $897F
897D LDA #$27
897F TAY
8980 LDA $8A06,Y
8983 PHA
8984 LDA $07C3,X
8987 CMP #$46
8989 BNE $8990
898B PLA
898C LDA #$00 <- hard-coded +0 pitch adjustment if current key number is $46/2 = $23
898E BEQ $89EE
8990 PLA
8991 JMP $89EE
8994 LDA $B2 <- envelope 2
8996 TAY
8997 CMP #$10
8999 BCS $89A1
899B LDA $8A3D,Y
899E JMP $89F4
89A1 LDA #$F6
89A3 BNE $89F4
89A5 LDA $07C3,X <- envelope 3
89A8 CMP #$4C <- utterly pointless key number check as both paths are the same
89AA BCC $89B0
89AC LDA #$FE
89AE BNE $89F4
89B0 LDA #$FE
89B2 BNE $89F4
...
89B4 LDA $07D1,X <- actually starts here
89B7 STA $B2
89B9 LDA $B0
89BB CMP #$20
89BD BEQ $89D3 <- envelope 1 branch
89BF CMP #$A0
89C1 BEQ $89E2 <- envelope 5 branch
89C3 CMP #$60
89C5 BEQ $89A5 <- envelope 3 branch
89C7 CMP #$40
89C9 BEQ $8994 <- envelope 2 branch
89CB CMP #$80
89CD BEQ $8977 <- envelope 4 branch
89CF CMP #$C0
89D1 BEQ $8977 <- envelope 6 branch
89D3 LDA $B2 <- envelopes 1 and 7
89D5 CMP #$0A
89D7 BNE $89DB
89D9 LDA #$00
89DB TAY
89DC LDA $8A33,Y
89DF JMP $89EE
89E2 LDA $B2 <- envelope 5
89E4 CMP #$2B
89E6 BNE $89EA
89E8 LDA #$21
89EA TAY
89EB LDA $8A12,Y
89EE PHA
89EF TYA
89F0 STA $07D1,X
89F3 PLA
89F4 PHA <- common path
89F5 LDA $07C8,X
89F8 BNE $8A04 <- don't mess with it if a sound effect is playing
89FA PLA
89FB CLC
89FC ADC $B1
89FE LDY $BE
8A00 STA Sq0Timer_4002,Y
8A03 RTS
8A04 PLA
8A05 RTS
The code that determines whether a given envelope has the triangle "forever" flag set:
8D5C LDA $079C <- pitch envelope in top 3 bits
8D5F AND #$C0
8D61 BNE $8D66
8D63 TYA
8D64 BNE $8D6E
8D66 CMP #$C0
8D68 BEQ $8D63
8D6A LDA #$FF <- forever. Either bit 6 or 7 is set, but not both.