Topic: Squeeker by zilogat0r
I keep forgetting to write about Squeeker engine by Zilogat0r. Many thanks to Shiru for pushing me to study it earlier this year. In case you have not heard of it before, I am attaching the track by Factor6 - see the attachment called "squeekf6.z80".
It has a very unusual sound loop, which suggests a nice solution to the common problem of overbearing mid- and high-tones, while the basses are low. This is what the sound loop looks like:
mxb exx ; (1)
xor a
ld bc,#0400
ld sp,#80c4
mxa rla
pop de
pop hl
add hl,de
push hl
pop hl
djnz mxa
ld c,a ; (2)
ld a,20
add a,h
ld a,15
adc a,c
out (254),a
exx
dec hl ; (3)
ld a,h
or l
jr nz,mxb
OK. Part (3) is the easiest: HL' stores the number of times the sound loop repeats, i.e. this is simply the tempo control. Easy. Part (1) is also not too bad. The SP is directed at the buffer at #80c4 that stores 8 words - the full data describing the state of 4 available channels. For each channel 2 words are stored: first, the word which is added to the channel current state counter (i.e., the period of the wave) and the second word is the actual state. Two words are read off the stack, added together and then the second word is re-saved back onto the stack (PUSH HL : POP HL). This is done for all 4 channels, i.e. we have four independent 16-bit channels.
However, part (2) looks very strange at first. Note how A is used to accumulate values of the overflow bit C during part (1), except that RLA is done BEFORE, not after the first addition happens, so the overflow produced by the fourth channel is IGNORED. What? Now, watch my hands:
ld c,a ; save into C the number 0..7 with states of first three channels
ld a,20 ; add 20 to H, so it overflows when H>255-20
add a,h
ld a,15 ; discard the result in A, put 15 into A instead
adc a,c ; add the overflow we had after adding 20 to H and also the contents of C
out (254),a
In the end, what we end up outputting into the port is the number 15+0..7(depending on overflow bits of the first 3 channels)+1(if H in the fourth channel is above 255-20). If all the additions are 0, we end up outputting something into the tape port and onto the border too. However, as long as at least one of the channels 1..3 produces a pin, or if the duty cycle of the channel 4 is sufficiently advanced, we end up setting bit 4 of the port 254, i.e. producing sound.
So, effectively, we have here a very rare beast: a mixed synthesis beeper engine. The first three channels work as the standard "pin-based" generators, whereas channel 4 produces much wider square pulses. This makes it possible to use channel 4 for bass, which sounds solid and strong. Cool idea!
Can one do better? I guess one can. One can align the engine to 8t, slightly speed it up, and also actually switch to the kind of mixing which I used in the "Octode XL", i.e. the form of accumulating PWM. After all, with "Octode XL" it was possible to choose "volumes" of individual channels, so it is pretty similar to what we have in Zilogat0r's engine.