Decompile from asm:
DseTrackEvent_VolumeFade 0x020723C0
DseTrackEvent_PanFade 0x02072668
Both were deferred near-matches. Each reads a signed byte as the fade target and
a little-endian pair as its duration in ticks, writes the target, and then either
snaps current to it when the duration is zero, zeroes the duration when there is
nothing to travel, or divides the distance by the duration to get the per-tick
delta. The two differ only in which struct dse_fade they address -- volume at
0x2c, pan at 0x3c -- both of which already exist in dse.h.
What closed them was declaration order, and in the opposite direction to the
obvious one. The target loads ptr_next_byte[2] first, then [0] and [1]; writing
the target expression first, so that its load comes first in the source, leaves
the loads in ascending address order at score 620. Declaring ticks first and the
target second emits them in the target's order and scores 0. So for this
scheduler the later-declared expression's loads are issued first, and the fix is
to write the declarations in the reverse of the order the asm reads them.
Seven other spellings of the same two statements were tried -- explicit locals
for each byte in the target's load order, s8/s16/s32 intermediates, an inline
cast, and a pointer-cast subscript -- and every one of them stayed at 620. Only
the declaration swap moves it.
The lib/DSE headers extract_function.py generates do not include dse.h, so both
were given it; every other header in that directory already does.
Three deferred DSE functions remain, and this commit does not close them.
DseTrackEvent_TuningFade is the same family with a bend fade plus the SetTuning
tail; hoisting container out of channel before the flag test takes it from 935 to
760, which is progress and not a match. DseTrackEvent_SetupKeyBendLfo is
unchanged in substance: its instructions have matched for some time and only
register assignment differs. All 120 orderings of its five byte locals were tried
this time, along with eleven structural variants, and the best is 55 rather than
the 65 it sat at; the earlier note suggested exhaustive permutation as the untried
move, and it has now been tried and does not close it.
No comments are added to any pmd-sky file.
Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Decompile from asm:
DseTrackEvent_SetBpm 0x02071AE0
DseTrackEvent_SetBpm2 0x02071B20
DseTrackEvent_Signal 0x0207296C
SetBpm and SetBpm2 have identical bodies. The two differ only in the label their
literal pool uses, so they are the same routine reached from two opcodes rather
than variants of one; both are decompiled as written rather than one calling the
other, because that is what the target does.
They scale the sequence's tempo fade by the new bpm and divide 0x03938700 --
sixty million microseconds, i.e. one minute -- by the result to get
microseconds_per_beat, guarding a zero divisor by substituting 1. The shifts
type the intermediate: the tempo is read with an arithmetic shift and the
product with a logical one, so the value handed to _u32_div_f is unsigned.
Signal writes the stream byte to the sequence and passes it to the sequence's
signal_callback with code 8, following DseTrackEvent_SetInstrument in
lib/DSE/src/main_02071BF4.c, which makes the same call with a different code.
The callback's four arguments -- id, code, value, callback_arg -- come from the
existing function-pointer type in dse.h, not from the call site.
No comments are added to any pmd-sky file.
Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Decompile from asm:
DseTrackEvent_SetupVolumeLfo 0x020724A8
DseTrackEvent_SetupPanLfo 0x020726C4
DseTrackEvent_SetupLfo 0x02072770
Each reads five bytes and initialises one lfo_settings entry: waveform index,
a signed 16-bit amplitude, a phase-change time, and zeroes for the envelope
fields. The two fixed-slot handlers also set type and output_type, using the
same slot-to-output_type mapping as the Use handlers -- 2 for volume, 3 for pan.
SetupLfo is the indexed form and takes its slot from channel + 0x61.
struct dse_lfo_settings gains field_0xE. All four of these handlers store a zero
byte at that offset, which the struct previously described as trailing padding.
The struct's size is unchanged: it already rounded to 0x10 for the s32
amplitude's alignment, and the 0x10 stride is confirmed by the shift-by-4 the
asm uses to index the array. Adding the field is therefore byte-neutral to
every existing user, which the build confirms.
All five stream bytes are read into locals before the first store. Reading them
inline instead interleaves the loads between stores, and the target issues all
five ldrb up front.
DseTrackEvent_SetupKeyBendLfo is deliberately not included. It is the same
shape and reaches an instruction-for-instruction identical body -- every
mnemonic, offset and immediate matches -- differing only in which registers the
allocator picks, at score 65. Reordering the locals, introducing an explicit
amplitude temporary, and dropping the pointer local were all tried and moved the
score without closing it. It is left in asm rather than landed as a near-match.
No comments are added to any pmd-sky file.
Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Decompile from asm:
DseTrackEvent_TuningDeltaCoarse 0x02071EB4
DseTrackEvent_TuningDeltaFine 0x02071F3C
DseTrackEvent_TuningDeltaFull 0x02071FC4
All three are DseTrackEvent_SetTuning with the new tuning derived from the old
one instead of replacing it, and they share its tail verbatim: recompute
bend_final from the tuning, the channel's bend fade and the synth's bend, then
mask interrupts and set update_flags bit 0x10 on every voice in the channel.
They differ only in how the delta is scaled. Coarse shifts the signed byte left
8, Fine shifts it left 2, and Full takes a little-endian pair of bytes and adds
it whole.
Coarse and Fine need the delta written before the existing tuning --
(delta << n) + channel->tuning, not channel->tuning + (delta << n). The target
loads the stream byte first and folds the shift into the add's second operand;
the other order reverses both the loads and the add, at score 215. Full is
unaffected because its byte pair is assembled before the add either way.
No comments are added to any pmd-sky file.
Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Decompile from asm:
DseTrackEvent_SetVolume 0x0207227C
DseTrackEvent_VolumeDelta 0x02072310
DseTrackEvent_SetExpression 0x0207241C
DseTrackEvent_SetPan 0x02072554
DseTrackEvent_PanDelta 0x020725D4
All five follow DseTrackEvent_SetTuning in lib/DSE/src/main_02071BF4.c, which
was already landed and is a complete template for this shape: write the fade,
recompute the final value, then mask interrupts through IME while walking
channel->voice_list and setting an update_flags bit on every voice. The bit
differs by what changed -- 0x20 for volume and expression, 0x40 for pan, where
SetTuning uses 0x10.
Offsets all resolve against existing dse.h types. struct dse_fade is 0x10, so
the channel's bend, volume and pan fades sit at 0x1c, 0x2c and 0x3c, which is
what the stores at 0x2c/0x34/0x38 and 0x3c/0x44/0x48 address. container is at
0xc4 and struct dse_synth puts pan at +7 and song_and_global_volume at +8, both
of which the asm loads with ldrsb.
The volume handlers scale by song_and_global_volume * volume * expression and
divide by 127 * 127; the compiler reproduces the target's smull-based division
by that constant, so the magic word 0x82061029 needs no special handling.
Two details that were not obvious. The final pan is v + (container->pan - 0x40),
not (v + container->pan) - 0x40: the target subtracts before adding, and the
other grouping emits the two instructions in the opposite order. And the discarded
read of IME before restoring it only survives if IME is declared through a
volatile type; without volatile the compiler drops the load and the restore is
all that remains.
No comments are added to any pmd-sky file.
Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Decompile from asm:
DseTrackEvent_SetBank 0x02071B60
DseTrackEvent_SetBankMsb 0x02071B8C
DseTrackEvent_SetKeyBend 0x02072184
All three assemble a 16-bit value from the event stream and hand it to a
DseChannel_ setter, following DseTrackEvent_SetBankLsb in
lib/DSE/src/main_02071BF4.c. SetBankLsb replaces the low byte of swd_id;
SetBankMsb is its mirror and replaces the high byte, which is why its operands
read (*ptr_next_byte << 8) + (channel->swd_id & 0xFF). Written the other way
round the addition is emitted in the wrong order and scores 30.
SetBank and SetKeyBend take both bytes from the stream. They differ only in the
width cast the target applies before the call -- lsr for the unsigned bank,
asr for the signed bend -- which is what types each callee's second parameter.
DseChannel_SetBank and DseChannel_SetKeyBend are still asm. Their prototypes are
provisional and declared in the new headers. Note DseChannel_SetBank is now
declared in two places: here and in lib/DSE/src/main_02071BF4.c, which declared
it from its own call site earlier. They agree, but nothing in the build checks
that, and the duplicate should collapse into one header when the callee lands.
This commit also restores four `; 0xADDRESS` label comments in
lib/DSE/asm/main_02071AE0.s that a previous precommit run stripped. The addresses
came from the pre-split file at 00d4d642. Nothing was mismatched by their
absence -- the ROM built fine without them -- but extract_function.py finds a
function by searching its label line for `; 0x`, so SetBank and SetBankMsb could
not be extracted until they were back. precommit.py now keeps that form.
No comments are added to any pmd-sky file.
Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Decompile from asm:
DseTrackEvent_SetNoteRandomRegion 0x02072144
DseTrackEvent_SetupLfoEnvelope 0x020727C8
DseTrackEvent_UseLfo 0x02072938
SetNoteRandomRegion reads two bytes and stores the smaller in
note_random_region_begin and the larger in note_random_region_end, so the pair
is sorted regardless of the order it was written in.
SetupLfoEnvelope and UseLfo are the indexed forms of the per-slot handlers added
in the previous commit: instead of a fixed lfo_settings entry they take the
index from channel + 0x61, which UseLfo also writes. That byte falls inside
dse.h's field_0x5A[10], so it is spelled field_0x5A[7]; a name for it would have
to come from a real pmdsky-debug sync.
UseLfo needed the sub-struct bound to a pointer local. Indexing
channel->lfo_settings[idx] directly folds the array's 0x74 base into each
store's offset, giving strb [r1, #0x75]; the target computes the element address
once, add r1, r3, #0x74 then add r1, r1, idx lsl #4, and stores at [r1, #1] and
[r1, #2]. Binding struct dse_lfo_settings *lfo also reproduces the frame push
the target has, which no arrangement of the indexed form did.
No comments are added to any pmd-sky file.
Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Decompile from asm:
DseTrackEvent_SetupKeyBendLfoEnvelope 0x0207222C
DseTrackEvent_UseKeyBendLfo 0x0207225C
DseTrackEvent_SetupVolumeLfoEnvelope 0x02072504
DseTrackEvent_UseVolumeLfo 0x02072534
DseTrackEvent_SetupPanLfoEnvelope 0x02072720
DseTrackEvent_UsePanLfo 0x02072750
All six index struct dse_channel's existing lfo_settings[4], which sits at
offset 0x74 with a 0x10 stride -- the stride the asm computes as ip lsl #4, and
the offset another handler in this family loads directly as channel + 0x74. The
three pairs address entries 0, 1 and 2, so key bend, volume and pan each own one
lfo_settings slot. Within an entry the setters write +0xa and +0xc, which are
msec_until_lfo_started and lfo_envelope_len_msec, and the Use handlers write +1
and +2, which are type and output_type.
output_type is set to 1, 2 and 3 for key bend, volume and pan respectively,
which is consistent with the slot each one uses; that is an observation about
the constants, not something the bytes label.
The Use handlers needed an explicit if/else rather than a ternary. Every
conditional-expression spelling tried -- v ? N : 0, v == 0 ? 0 : N, !v ? 0 : N,
and (v != 0) -- emits movne before moveq, where the target emits moveq before
movne. The statement form reverses the pair and matches.
No comments are added to any pmd-sky file.
Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Decompile from asm:
DseTrackEvent_SetOctave 0x02071AC0
DseTrackEvent_OctaveDelta 0x02071ACC
DseTrackEvent_SetTuningJitterAmplitude 0x0207216C
DseTrackEvent_SetUnknown2 0x020721B0
DseTrackEvent_SetKeyBendRange 0x020721C8
DseTrackEvent_Dummy2Bytes2 0x0207299C
These are the same handler family as the previous two commits, but they live in
lib/DSE, which is compiled with different flags: -O4,p and -enum int, where the
arm9 modules use -O4,s and -enum min. Signatures follow the parameter names
already used by the landed handlers in lib/DSE/src/main_02071A98.c rather than
the ones the previous two commits introduced.
Field names come from struct dse_channel in lib/DSE/include/dse.h and match the
offsets the asm uses: bend_jitter_amplitude at 0xa, field_0x56 at 0x56, and
bend_sensitivity_override at 0x59, whose meaning agrees with SetKeyBendRange
writing to it.
SetTuningJitterAmplitude and SetUnknown2 read their two operand bytes in
opposite orders -- little-endian for the first, big-endian for the second --
which the asm's shift operands show directly.
Landing these needed two adjustments the arm9 path does not. extract_function.py
hardcodes asm/, src/, include/ and main.lsf, so it was run with lib/DSE as the
working directory; the object lines it then inserts into main.lsf carry no
lib/DSE/ prefix and were corrected by hand. It also emits
.include "main_XXXX.inc", which is right for arm9 where the search path is
./asm/include, but lib asm is built with -Ilib/DSE/asm/ and its existing files
use .include "include/main_XXXX.inc"; the four split files were fixed to match.
No comments are added to any pmd-sky file.
Authored by Claude (Opus 5) under human direction. Confirmed by a matching
build: build/pmdsky.us/pmdsky.us.nds: OK.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>