Skip to content
FreeDOSDeep Dive Published Updated 7 min readViews unavailable

PC Speaker Tones on DOS: PIT Channel 2 and Port 61h

Generate a classic PC speaker tone by coordinating PIT channel 2 and port 61h without disturbing unrelated system-control bits.

The classic PC speaker is a small hardware path with two cooperating controls: PIT channel 2 produces a programmable square-wave signal, and system-control port 61h gates that signal to the speaker. It is not a sound card, sample playback engine, or DOS audio API. On an original PC or XT, port 61h corresponds to bits of the 8255 PPI; AT-compatible systems preserve the programming interface through motherboard logic even when the original component implementation differs.

The safest general-purpose application usually asks BIOS to beep or uses a documented sound driver. Direct programming is useful for diagnostics, very simple tones, and retro software that intentionally owns the machine. It can conflict with a resident sound driver or other program using the timer, so the code must save state, change only its control bits, and restore them after playback.

Frequency is a divisor, not an arbitrary pitch register

PIT channel 2 receives a reference clock of approximately 1.19318 MHz on the traditional PC design. In square-wave mode, the reload count divides that input to produce an output frequency. A rough divisor is clock_hz / target_hz; the result must fit the counter and cannot be zero. Integer division means the actual tone differs slightly from the requested frequency. Do not treat the nominal clock value as a precision musical tuning reference across all clones and emulators.

For example, a nominal 440 Hz tone uses a divisor near 2712. This is only an illustration; hardware clock tolerance and integer rounding affect the audible result. The PIT control word must select channel 2, the intended access mode, square-wave operation, and binary counting. Then write the divisor bytes to the channel-2 data port in the order specified by the timer interface.

save port 61h and any channel-2 state owned by this program
write a channel-2 square-wave control word to PIT control port
write low byte, then high byte of the nonzero divisor to channel 2
read port 61h; set only the channel-2 gate and speaker-enable bits
wait for the requested tone duration using an independent timer
read port 61h; clear only those two bits and preserve all others
restore state only if the program had exclusive ownership

The outline is pseudocode, not a portable driver. The control word, port accesses, and restoration policy must match the target machine and any installed driver. On a PC-compatible port, bits 0 and 1 of 61h are commonly used to gate PIT channel 2 and connect its output to the speaker. Preserve the other bits with a read-modify-write operation; writing a hard-coded 00h or 03h can alter unrelated system-control state on AT-compatible hardware.

Preserve ownership and avoid corrupting timer users

PIT channel 0 drives the system timer and should not be reprogrammed for a beep. Channel 2 is the tone generator, but it can still be used by a resident sound driver, diagnostic tool, or other application. A second program that changes its reload value while your tone is playing can produce the wrong pitch or silence. DOS is not a multiuser hardware scheduler; process suspension does not automatically restore device registers.

If you can read or save the previous state reliably, restore it only after the sound path is no longer active and only if no other owner took over. Some timer state is not safely reconstructible from a single latch read after a mode change. A program that cannot guarantee exclusive ownership should avoid direct programming or use the same sound driver’s public API as other software.

There is a practical difference between gate and speaker-enable control. Turning off the channel-2 gate stops the timer output path; disconnecting the speaker-enable bit prevents the signal from reaching the transducer. Both bits are often changed together for a tone, but preserving their prior values matters if another component expects channel 2 to remain configured. Save the original port byte, update only bits 0 and 1, and restore state only under an explicit ownership model.

The divisor is a 16-bit count. Calculate it with a sufficiently wide integer and reject zero or values outside the documented counter range. A divisor of one is not a safe “highest pitch” test: it produces a very high frequency and can expose emulator or hardware edge cases. Start with a moderate audible range. For a target note, calculate divisor = round(clock_hz / frequency_hz) and derive the actual result as clock_hz / divisor; report actual frequency if the utility is a tuner or diagnostic.

Use a duration mechanism separate from the PIT channel you are using for pitch. Reprogramming channel 2 and then relying on a CPU-speed delay loop makes the tone length machine-dependent. Polling the timer in an unbounded loop can hang the system. A BIOS wait may be too coarse for short notes and is not a guarantee of musical timing. For a simple diagnostic beep, a broad timing tolerance is usually acceptable; for music or effects, use a sound driver with a documented event or buffer model.

Electrical and compatibility limits

The output is a simple speaker drive, not arbitrary PCM samples. Rapidly toggling port bits to approximate a waveform consumes CPU, is sensitive to interrupt latency, and can produce audible artifacts or damage assumptions in a multitasking environment. Avoid turning a PC speaker into a high-rate bit-banging DAC. If the requirement is sampled speech, stereo, or calibrated volume, the hardware path is insufficient.

Emulators may synthesize the speaker output through host audio devices and scheduling. Muted host audio, audio latency, virtual-machine throttling, or emulator options can make a correct guest tone inaudible or delayed. Conversely, an emulated beep does not prove that a physical motherboard speaker is connected. Keep “register sequence succeeded” separate from “sound was audibly produced.”

Diagnostics and validation

Start with a single conservative tone on a disposable machine or emulator, not a test that changes port 61h repeatedly. Verify that the PIT divisor is nonzero, mode fields are correct, and only channel 2 is selected. Confirm that port 61h’s other bits are unchanged before and after the test. Then test a short duration, a longer duration, and an abort/cleanup path.

If there is no sound, check the output device and emulator audio routing first, then inspect whether the BIOS beep path works, whether the machine actually implements a speaker, and whether another resident driver owns channel 2. If the tone is stuck on, ensure the cleanup code runs after every normal error and user abort. If the computer behaves differently after the program exits, compare the saved and restored port values and remove direct port access from any code that can run alongside a sound driver.

For a support log, include the PIT divisor, selected mode, original and final port-61 values, requested duration, independent elapsed-time measurement, and emulator or hardware model. A short controlled sweep across moderate divisors can distinguish a wrong calculation from an audio-routing problem; avoid extreme values and long tests. Confirm that unrelated port bits are unchanged and that channel 0’s system tick continues at its normal rate.

If the code must support more than one known PC-compatible target, hide speaker access behind a capability boundary. Return “tone unavailable” where direct hardware access is unsupported and let the caller choose a BIOS beep or sound-driver backend. This keeps low-level port arithmetic out of application logic and enables tests to simulate the tone provider without touching hardware.

The practical rule is to leave channel 0 alone, program only channel 2, mask port 61h changes, and treat the speaker as a shared low-level resource. Use direct PIT programming only when the application has a clear reason and a bounded ownership interval; otherwise, defer to BIOS or the installed sound driver.

Related:

Sources:

Comments