Earlier today, someone asked me why 64bit versions of windows don’t support the internal PC speaker beeps. The answer is somewhat complicated and ends up being an interesting intersection between a host of conflicting tensions in the PC ecosystem.
Let’s start by talking about how the Beep hardware worked way back in the day. The original IBM PC contained an Intel 8254 programmable interval timer chip to manage the system clock. Because the IBM engineers felt that the PC needed to be able to play sound (but not particularly high quality sound), they decided that they could use the 8254 as a very primitive square wave generator. To do this, they programmed the 3rd timer on the chip to operate in Square Wave mode and to count down with the desired output frequency. This caused the Out2 line on the chip to toggle from high to low every time the clock went to 0. The hardware designers tied the Out2 line on the chip to the PC speaker and voila – they were able to use the clock chip to program the PC speaker to make a noise (not a very high quality noise but a noise nonetheless).
The Beep() Win32 API is basically a thin wrapper around the 8254 PIC functionality. So when you call the Beep() API, you program the 8254 to play sounds on the PC speaker.
Fast forward about 25 years… The PC industry has largely changed and the PC architecture has changed with it. At this point they don’t actually use the 8254 as the programmable interrupt controller, but it’s still in modern PCs. And that’s because the 8254 is still used to drive the PC speaker.
One of the other things that happened in the intervening 25 years was that machines got a whole lot more capable. Now machines come with capabilities like newfangled hard disk drives (some of which can even hold more than 30 megabytes of storage (but I don’t know why on earth anyone would ever want a hard disk that can hold that much stuff)). And every non server machine sold today has a PC sound card. So every single machine sold today has two ways of generating sounds – the PC sound card and the old 8254 which is tied to the internal PC speaker (or to a dedicated input on the sound card – more on this later).
There’s something else that happened in the past 25 years. PCs became commodity systems. And that started exerting a huge amount of pressure on PC manufacturers to cut costs. They looked at the 8254 and asked “why can’t we remove this?”
It turns out that they couldn’t. And the answer to why they couldn’t came from a totally unexpected place. The American’s with Disabilities Act.
The ADA? What on earth could the ADA have to do with a PC making a beep? Well it turns out that at some point in the intervening 25 years, the Win32 Beep() was used for assistive technologies – in particular the sounds made when you enable the assistive technologies like StickyKeys were generated using the Beep() API. There are about 6 different assistive technology (AT) sounds built into windows, their implementation is plumbed fairly deep inside the win32k.sys driver.
But why does that matter? Well it turns out that many enterprises (both governments and corporations) have requirements that prevent them from purchasing equipment that lacks accessible technologies and that meant that you couldn’t sell computers that didn’t have beep hardware to those enterprises.
This issue was first noticed when Microsoft was developing the first 64bit version of WIndows. Because the original 64bit windows was intended for servers, the hardware requirements for 64bit machines didn’t include support for an 8254 (apparently the AT requirements are relaxed on servers). But when we started building a client 64bit OS, we had a problem – client OS’s had to support AT so we needed to bring the beep back even on machines that didn’t have beep hardware.
For Windows XP this was solved with some custom code in winlogon which worked but had some unexpected complications (none of which are relevant to this discussion). For Windows Vista, I redesigned the mechanism to move the accessibility beep logic to a new “user mode system sounds agent”.
Because the only machines with this problem were 64bit machines, this functionality was restricted to 64bit versions of Windows.
That in turn meant that PC manufacturers still had to include support for the 8254 hardware – after all if the user chose to buy the machine with a 32bit operating system on it they might want to use the AT functionality.
For Windows 7, we resolved the issue completely – we moved all the functionality that used to be contained in Beep.Sys into the user mode system sounds agent – now when you call the Beep() API instead of manipulating the 8254 chip the call is re-routed into a user mode agent which actually plays the sounds.
There was another benefit associated with this plan: Remember above when I mentioned that the 8254 output line was tied to a dedicated input on the sound card? Because of this input to the sound card, the sound hardware needed to stay powered on at full power all the time because the system couldn’t know when an application might call Beep and thus activate the 8254 (there’s no connection between the 8254 and the power management infrastructure so the system can’t power on the sound hardware when someone programs the 3rd timer on the 8254). By redirecting the Beep calls through the system audio hardware the system was able to put the sound hardware to sleep until it was needed.
This redirection also had had a couple of unexpected benefits. For instance when you accidentally type (or grep) through a file containing 0x07 characters in it (like a .obj file) you can finally turn off the annoying noise – since the beeps are played through the PC speakers, the PC mute key works to shut them up. It also means that you can now control the volume of the beeps.
There were also some unexpected consequences. The biggest was that people started noticing when applications called Beep(). They had placed their PCs far enough away (or there was enough ambient noise) that they had never noticed when their PC was beeping at them until the sounds started coming out their speakers.
 Thus providing me with an justification to keep my old Intel component data catalogs from back in the 1980s.
Isn't it an 8253, not an 8254? Your previous post http://blogs.msdn.com/larryosterman/archive/2005/11/04/489135.aspx refers to it as such, as does the other information I can find about it.
How hard would it be to make a beep driver that utilizes the older chip?
On my system, and a quick google shows that this is by no means unique, the only sound outputs are headphones and the internal speaker. Headphones are useless if not worn, and as such the internal speaker is the only speaker that I can hear while not at my PC. In previous versions of Windows, Beep() could be used to notify a user even if the normal output was muted.
Back when I made that post I thought it was an 8253. But I checked the sources for the function that Beep.Sys uses to program the timer and it claims that it's an 8254 so I went with the source code.
How hard would it be to use the chip? Not hard but now that Win7 has shipped, the number of machines with an 8254 that's actually wired to something is going to dwindle to around 0. The machines will still have an 8254 (it's on the SuperIo chip in every PC) but it won't be connected to any hardware.
Looking a bit further, it seems that the 8253 and 8254 are both timers with the same interface.
There's a linux driver for the beeper that not just beeps but actually plays sampled sounds over the beeper - horribly. (sure something similar existed for DOS back in the day).
This is a welcome change. On more than one occasion a programmer at work has accidentally sent a binary file to the console and turned his workstation into a beeping monster, attracting the attention of everyone around him. The next step is usually a hard reset, since Ctrl+Break doesn't flush the 10,000 beeps that are queued up. It's also good for laptops since for some reason the laptop hardware manufacturers like to set the volume of the PC speaker emulation to something like 200dB.
Of course, the PC beep is still often used for diagnostics. When you put together a machine for the first time, you want to hear exactly one short beep after POST.
Of course, the POST doesn't cover everything. I fried my old nVidia 6600GT by not plugging in the auxilliary power, but trying to run the PC normally. There was no warning.
Things were different when I accidentally tried the same trick with my Radeon 5870: it makes an ungodly screaming noise instantly upon power-up when not fully plugged in.
blah: I know some games pulled the same trick. The good news is that CPUs were so powerful back in the day that they could be dedicated to doing nothing but fiddling the silly square wave generator.
Ah, the good ole PC speaker how I love to hear you sing back in the day.
Mostly thanks to this gem: http://support.microsoft.com/kb/138857
You are imprecise a couple of places in your post. The 8254 is the PIT, a Programmable Interval Timer. The 8259 is the PIC, the Programmable Interrupt Controller. You refer to the 8254 as the PIC a couple of times. Quite confusing.
>> There's a linux driver for the beeper that not just beeps but actually plays sampled sounds over the beeper - horribly. (sure something similar existed for DOS back in the day).
It was actually easy to write a program doing that.. I did.
You just reprogram the timer so that it stops generating a wave and stays always in 0 or 1 status.. and then reduce the wave file to 1 bit per sample and play that bit on the timer.. the inertia of the speaker does the smoothing between samples, but the result is horrible anyway :)
If you think that KB entry is a gem, check out this one: http://support.microsoft.com/kb/261186
Um, the plural of "American" is "Americans", not "American's". An apostrophe makes a noun possessive, not plural.
Is there still a way to play a tone of a certain frequence forever?
Old Pascalprograms on DOS used to play sounds by activating the speaker, sleeping a while and stopping it.
Now I tried to port these functions and I found a solution for W9x (using old assembler) and WinNT-32bit (Calling directly DeviceIoControl with IOCTL_BEEP_SET), but this fails on 64bit (and after your article it should also fail on Win7)
Dell desktops (Optiplexes, at least) have an internal speaker, presumably on-board. They also wire the output of the actual (on-board) sound card to it, in case nothing is connected to the speaker or headphones ports. So even if Windows 7 routes Beep() to the sound card, it could get re-routed back to the internal speaker!
Sound routing is funny. I was one testing different speakers, and found that my computer was still playing when no speakers were connected! After ruling out the internal speaker, I found it was the VoIP phone connected as a USB audio device!