Writing the repeat address stops loop start flags from overwriting it until
the next key on. Hardware fetches the first block's header on each of the
first three samples of the key on delay, so writes before the last fetch are
overwritten and do not latch; later writes latch regardless of pitch or how
long the first block plays.
This replaces the first-block heuristic and removes is_first_block.
Key on/off is only processed on every second sample, and a keyed-on voice
stays silent for four samples before the first attack tick, so ENVX first
changes six or seven samples after the KON write.
Key on clears has_samples, so the remaining delay is only checked before
reading a new block, and voices that are idle or mid-block cost the same as
before. Key off during the delay does not cut it short. IRQ prediction
accounts for the delay before the first block read.
Require decay to cross the sustain threshold before entering sustain, including
when the threshold is the maximum envelope level. Keep release ending at zero.
Start the pitch counter at sample index three so the first interpolation window
contains four decoded samples. Add hardware capture regressions for pitches
0x2000 and 0x1500, where ring rotation cannot hide the initial source phase.
Retain one fractional bit in each Gaussian product before accumulation to match
hardware captures. Add capture regressions at unity and fractional pitch, and
update the scalar/SIMD comparison.
SampleVoice() returns silence for voices that are off, so the dump can be
written once in Execute() from the returned samples instead of at each
return point.
Constants which are a rotated 8-bit immediate, or the inverse of one
(e.g. 0xFFFFFFFF, 0x80000000, 0x1F800000), can be loaded with a single
mov/mvn instead of movw + movt.
s < t is the same as t > s, which lets the constant be encoded as a
compare immediate instead of being loaded into a register first,
e.g. for sltu rd, zero, rt.
0 - rt (negu) uses the zero register on ARM64. ARM32 uses rsb, which takes the
left hand side as an immediate, and sets the same flags as the equivalent subs.
The raw assembler can't encode a negative immediate, so addiu with a negative
immediate and loads/stores with a negative offset moved the constant into a
register first (two instructions on ARM64, three on ARM32).
Subtract the negated immediate instead. The overflow flag for subs is the
same as adds with the negated operand.
cmp is an alias of subs, so it takes the same immediates as add/sub.
The 5-bit limit is for ccmp, and meant any compare against a constant
above 31 loaded it into a register first.
sdiv/udiv never trap: x / 0 produces 0, and 0x80000000 / -1 produces 0x80000000.
Followed by msub/mls, that already gives the MIPS result for the unrepresentable
case, and the MIPS hi for divide by zero, so only lo needs fixing up when the
divisor is zero.
Drops two compares and branches from every div.