n0p wrote on 2023-08-28, 19:12:
But right next command disconnects speaker from channel 2 (Bit 0 set to 0), leaving it at low until next trigger.
Doesn't make sense to me, as it should generate small number of very shot high pulses on speaker.
What am i missing?
It seems you don't correctly understand how the two low-order bits of port 61 are interfacing with the speaker.
Bit 0 is directly connected to the "channel 2 gate" input of the timer chip. The function of the "gate" input depends on the counter mode. In the mushroom demo, channel 2 operates in hardware-retriggerable one-shot mode. This is one of the two modes in which the "gate" input does not gate the counting, but instead the rising edge of that input starts the counting. So the duration of the low pulse at the output of timer 2 is not related to the duration the gate pin is high. Most other PC speaker drivers use the "interrupt on terminal count" mode, in which the pulse (output low) begins immediately after writing a byte to port 42h, which seems more efficient, because you can avoid the 8-bit I/O instructions to port 61h.
Bit 1 of port 61h is ANDed with the "channel 2 output" pin (look up the polarity (A ND vs. NAND) if required) in a way that the transistor that drives the speaker never gets turned on while bit 1 is low, but the transistor follows the output if bit 1 is high. You can put the system into a state in which the speaker transistor is permanently turned on (some games, especially those that use PWMing, do this, likely by accident). If the transistor is turned on permanently, the speaker reproduces the noise on the +5V line. So if you happen to hear quiet low-volume buzzing noise from the speaker after running an application that uses the speaker, this is likely what happened.
It seems strange that the mushroom demo uses mode 1 ("mushroom") instead of mode 0 ("standard") which requires the gate pin to be pulsed. On the other hand, around 25 years ago, i tried modifying the Linux PC speaker kernel driver to use the later "standard" algorithm, and found out that the "standard" algorithm seems to be more sensitive to jitter than the "mushroom" one: The Linux PC speaker driver (the one I linked some posts before) does not disable other interrupts during playback (after all, Linux is a multi-tasking system!), and after modifying the driver to use the standard algorithm and omit the port 61h stuff, I could hear a quite loud buzzing noise whenever I moved the mouse (which invokes interrupts on the serial port at a constant frequency), but in the initial way the driver worked, the quality still was degraded, but no obvious buzzing happened during mouse moves. Up to today, I do not understand the difference between using mode 0 and using mode 1, if you always pulse the gate pin directly directly after loading a new value.
In the PC architecture, timer channel 2 is the most versatile one, because that is the only channel that has a programmable gate pin. Channels 0 and 1 have the gate pin tied high. Also, as long as you don't set bit 1 of port 61h, the output of timer 2 has no observable function, so you can use the timer channel 2 for software-internal purposes as you like without disturbing PC operation. I like to use timer 2 for micro-benchmarking: I set it up in a down-counting mode (typically mode 2, "rate generator"), and then I can just set bit 0 of port 61h during execution of the code fragments that need to be timed, so the "timing enable" operation is minimal and can be kept separate from the timer set-up and timer read-back code. If you use mode 2, every rising edge of the gate will reset the count, so will will read back only the duration of the latest code fragment that sets port 61h, bit 0. On the other hand, modes 0 and mode 4 don't do so, so you can accumulate the duration of multiple timed code blocks.