A 4-channel beeper engine with support for contended memory. Technical information below.
Download archive (source code, converter, documentation)
Technical details
AMP16K is based on AMP, an older engine by Hikaru/Intense, and is largely identical to it. Both are accumulating non-PWM engines with a constant-time inner loop and interrupting drums. The sound generation is similar to that in Squeeker by Zilogat0r.
A special modification is used to make it work in contended memory. This is based on the following ideas and observations:
1. A looped or repeated code accessing lots of contended RAM, that is approaching the duration of one scanline (224 T states on a 16/48K Spectrum), would exhibit a tendency to 'snap' to this duration. This means that as long the code has certain duration that is less than 224 T states, the amount of slowdown applied to it by the ULA during screen time would be constant.
2. Because the inner loop code would be slowed down by a constant amount during screen time, it is possible to slow down the routine during border time as well using additional delay code, so as to match that speed. Each frame, the inner loop is modified to run with this extra delay during border time, whereas during screen time it is changed back to run without it. This is the key idea to make it work in contended memory.
As in the original AMP, the song data parser has branching and uneven timing. To accommodate for this, the parser is run at the end of screen time, and mode 2 interrupts are used to even out the timings. Because using IM2 is problematic on a ZX Spectrum 16K, the engine is presumed to be incompatible with a number of peripherals that put random data on the bus (DivIDE, Kempston interface...).
2. Because the inner loop code would be slowed down by a constant amount during screen time, it is possible to slow down the routine during border time as well using additional delay code, so as to match that speed. Each frame, the inner loop is modified to run with this extra delay during border time, whereas during screen time it is changed back to run without it. This is the key idea to make it work in contended memory.
As in the original AMP, the song data parser has branching and uneven timing. To accommodate for this, the parser is run at the end of screen time, and mode 2 interrupts are used to even out the timings. Because using IM2 is problematic on a ZX Spectrum 16K, the engine is presumed to be incompatible with a number of peripherals that put random data on the bus (DivIDE, Kempston interface...).
In order to run equally in contended memory as well as uncontended, the engine performs a CPU speed test in the beginning. If the test result is above a certain threshold, the loop modifying code is made inactive, and the inner loop runs with the extra delay on at all times.
There's
an attempt at balancing the timings of the loop modifying code and
other things. This doesn't entirely work due to heavy contention and other reasons.
However, the influence on the resulting sound, if any, appears insignificant, so
it was decided not to pursue it further. =)
Revisions
20201002
- Added the 'masking' variant
- Removed usage of VLA in converter source
- Readme updated
- Added the source code package
- Article corrected
- Initial publication