PC Motherboard Speakers This document explains the theory and use of the PC speaker located on your motherboard. Borland C++ 3 (and possibly other versions) for DOS has a function called `sound'. Using the `sound()', you can use your pc speaker to make sound. The following is the exact same thing, only we have more control over the process. +-+-+-+-+-+-+-+-+-+-+ + Table of Contents + +-+-+-+-+-+-+-+-+-+-+ 1. Theory 2. Turning on the Speaker 3. Making Sounds 4. Turning off the Speaker 5. Conclusion +-+-+-+-+-+ + Theory + +-+-+-+-+-+ The theory is fairly simple. We use the PPI (Programmable Peripheral Interface), and the Programmable Timer. +-+-+-+ + PPI + +-+-+-+ We use port B (61h) of the PPI to turn on the speaker. Note that the PPI is based on a frequency of 1.193 Mega Hertz. +-+-+-+-+-+-+-+-+-+-+-+ + Programmable Timer + +-+-+-+-+-+-+-+-+-+-+-+ The programmable timer chip has three channels. Channel 0, channel 1, and channel 2. For sound we use the third channel. We'll also need to use the command register which is located at port 43h. The third channel is located at port 42h. Port 42h is connected to the pc speaker. It issues square-wave pulses which are used to make sounds. +-+-+-+-+-+-+-+-+-+-+ + The Whole Process + +-+-+-+-+-+-+-+-+-+-+ We turn on the speaker and timer with the PPI located at port 61h. Next we set the necessary modes for channel 2, and send it to the command register at 43h. Finally we put numerical values into channel 2 to make sounds. Each number has a different frequency (although when there is only a small difference in the values you probably won't hear the change). Each machine is different and so it may sound a little different; due to the casing or the speaker itself. A misleading factor is also present: LOW values sent to channel 2 are HIGH pitch and HIGH values sent to channel 2 are LOW pitch. Keep these thoughts in mind when you are using this `technique'. We must use direct I/O in order to send values to ports. We do this with two low-level instructions. These instructions will always use the Accumulator register (AX). To get AX, we have the values of AH and AL forming one full register. AH is the higher byte, AL is the lower byte. Thus, AH + AL = AX. Note that when using the direct I/O instructions you will never use AH; you may only use AL or AX. +-+-+-+ + In + +-+-+-+ `IN' will read in the value from the port number and put it into the register for us to use. The syntax for this instruction is: in , The register is the accumulator, AX as explained above. If the port number is higher than 255 (FF hex), you must place the port number in DX, and use DX in the instruction. +-+-+-+ + Out + +-+-+-+ `OUT' will transfer the value from the register into the port number. The syntax for this instruction is: out , The register is the accumulator, AX as explained above. If the port number is higher than 255 (FF hex), you must place the port number in DX, and use DX in the instruction. +-+-+-+-+-+-+-+-+-+ + Implementations + +-+-+-+-+-+-+-+-+-+ With `IN' and `OUT', you have direct I/O access. With direct I/O accces, you can control anything and everything. This is why you may be advised to be careful when you don't know what you are doing with ports. Ok, so now that you hopefully understand the theory, lets begin with the code... +-+-+-+-+-+-+-+-+-+-+-+-+-+ + Turning on the Speaker + +-+-+-+-+-+-+-+-+-+-+-+-+-+ 3 lines of code will turn on the speaker and timer. in al,61h ;read in port B (61h) or al,03h ;modify necessary bits out 61h,al ;put the modified al back into port B (61h) of the PPI Now we must set the necessary mode for channel 2 in order to manipulate the speaker. mov al,0B6h ;AL=0B6h out 43h,al ;and send it to the control port, located at 43h +-+-+-+-+-+-+-+-+ + Sending Tones + +-+-+-+-+-+-+-+-+ The speaker is now under your control. The idea is to put the frequency in AX and send it to port 42h. As I mentioned earlier, the frequency number does *NOT* equal the value you put into AX. So how do you get the frequency? This is where the 1.193Mhz value comes in. To get the correct value you must divide the `wanted value' by `1.193'. The result (rounded) is what you put in AX to get the correct hertz. mov ax,2345 ;put 2345 in AX out 42h,al ;put lower byte into 42h mov al,ah ;put higher byte into AL out 42h,al ;put higher byte into 42h The above code is pretty self explanitory. We put the value in AX. Then we put AL (the lower byte of AX) into port 42h. Then we place AH (the higher byte)into AL and send it to 42h which successfully places AX into 42h. And thats it. +-+-+-+-+-+-+-+-+-+-+-+-+-+ + Turning Off the Speaker + +-+-+-+-+-+-+-+-+-+-+-+-+-+ To turn the speaker off we use the PPI again. in al,61h ;read in from port B (61h) again and al,0FDh ;clear necessary bits out 61h,al ;put back into al, and speaker is off We read in from 61h again. Then we modify the read in value and send it back to 61h, turning off the speaker and timer. +-+-+-+-+-+-+-+-+ + Conclusion + +-+-+-+-+-+-+-+-+ Okay, you may be thinking "big deal, one tone. Whats the point?" Well for one thing you can do more than one tone you just have to write a routine to do it; which isn't too hard if you know assembly. You can get a random value and put it into the speaker and have some sort of loop; be it n times, or a conditional comparation. You can make delays any way you wish. You can also have a lot of values in one area in your program, and you have a routine that gets the next or specific value and sends it to your speaker. Some possible uses? Just for fun. Challenge of creating something interesting Error beep. Selection of an Item. And so on. Just use your imagination and experiment, and you'll surely come across new ideas... Have fun, MetGod - metgod@hfactorx.org "My Terminal is my Soul."