Music in the game is managed by Music Scripts ("MScripts" for short).
This is still early in the reverse-engineering phase. Documentation will be fleshed out as I know more.
# Memory Locations
There are a few memory locations relevant to MScripts:
| Bank | Address Range | Description |
| ---- | ------------- | ---------------- |
| 5 | $826A - $828D | Ops lookup table |
| 5 | | |
# MScripts Structure
BScripts have the following structure:
| Script Offset | Size (bytes) | Description |
| ------------- | ------------ | ---------------------------- |
| 0 | 1 | Op ID, Length, or Note/Value |
| 1+ | N | Parameters |
| ... | | |
Scripts will end with an End Script (0xFF) or Go To (0x05 + address).
## Values
* Ops start with value 0xEE.
* Note lengths are in range of 0x80—0xED (bit 7 discarded to yield a length).
* The rest are note or noise values.
# Ops
Operations are identified by a number between 0 and 7, and a special value 0xFF.
Implementations are all in Bank 12.
Here's a quick summary of the ops:
| Op ID | Name | Arguments | Implementation Address |
| ----- | ---------------------------------- | ----------- | ---------------------- |
| $EE | Set SQ2 pitch bias | 1 (1 byte) | $853B |
| $EF | Set SQ envelope depth | 1 (1 byte) | $852F |
| $F0 | Set SQ envelope mode | 1 (1 byte) | $8523 |
| $F1 | Set SQ fade | 1 (1 byte) | $8517 |
| $F2 | Set SQ control bits | 1 (1 byte) | $8483 |
| $F3 | Set note duration | 1 (1 byte) | |
| $F4 | Restart music | 0 | $84B7 |
| $F5 | Return from subroutine | 1 (2 bytes) | $84F0 |
| $F6 | Set per-channel note transpose | 1 (1 byte) | $8502 |
| $F7 | Set global note transpose | 1 (1 byte) | $850E |
| $F8 | Jump to subroutine | 0 | $84D0 |
| $F9 | Save address (see $FE) | 0 | $848F |
| $FA | [[#Op $FA No-Op\|No-op]] | 0 | $8480 |
| $FB | Restart loop if >= N iterations | 1 (1 byte) | $043D |
| $FC | [[#Op $FC End Loop\|End of loop]] | 0 | $8458 |
| $FD | [[#Op $FD Begin Loop\|Begin loop]] | 1 (1 byte) | $841F |
| $FE | Restore address (from $F9) | 0 | $84A5 |
| $FF | Finish channel playback | 0 | $8408 |
## Op $EE: Set SQ2 Fade
TODO
## Op $EF: Set EQ Envelope Depth
TODO
## Op $F0: Set EQ Envelope Mode
**Function Address:** $8523
**Arguments:**
1. Mode (1 byte)
**Modes:**
| Mode | Name | Description |
| ---- | ---------------------------- | --------------------------------------------------------------- |
| 0 | Volume Decay | Ramps down the volume smoothly |
| 1 | Curve but Held | Clamps the envelope into a range 0..15 and then slows the decay |
| 2 | Pre-built attack/decay curve | Pitches up and then down in a set pattern |
## Op $F1: ...
TODO
## Op $F2: ...
TODO
## Op $F3: ...
TODO
## Op $F4: ...
TODO
## Op $F5: ...
TODO
## Op $F6: ...
TODO
## Op $F7: ...
TODO
## Op $F8: ...
TODO
## Op $F9: ...
TODO
## Op $FA: No-Op
**Function Address:** $8480
**Arguments:** None.
Does nothing. Simply acts as padding to help keep scripts across channels in sync.
## Op $FB: ...
TODO
## Op $FC: End Loop
**Function Address:** $8458
**Arguments:** None.
Marks the end of the loop.
If the loop counter has hit the loop length (and remember, the loop counter is 1-based), the loop will end.
If it's under the loop length, the loop will restart.
## Op $FD: Begin Loop
**Function Address:** $841F
**Arguments:**
1. Number of iterations (1 byte)
Marks the start of a loop. This address will be used when looping.
The loop will end when after it completes the specified number of loops, or when another operation breaks out of the loop.
The initial loop counter is set to 1 internally. If the loop length is also 1, the loop will end in one cycle.
Loops cannot be nested.
## Op $FE: Restart Loop If >= N Iterations
**Function Address:** $043D
**Arguments:**
1. Number of iterations to check (1 byte)
If >= N iterations have been completed, restart the loop, advancing the loop iteration count.
If < N iterations have been completed, proceed with the next operation in the script.
**Note:** At least one loop must complete for this to work correctly, as it must have encountered the End Loop operation in order to get the end-of-loop address to perform a restart.
## Op $FF: ...
TODO