Contents of the POLY.DOC file
POLYPHONIC MUSIC ON THE IBM PC
Rocketdyne Microcomputer Users Group
Sitting at the keyboard of my IBM PC, I realized that other computers
could produce music with three simultaneous tones for chords and harmony
while my IBM was limited to only one tone at a time. To rectify this, I
wrote an assemblylanguage program which produces three tones from the
IBM PC's internal speaker without any hardware modification.
Normally, a single tone is generated through the speaker using the 8253
Programmable Interval Timer. The microprocessorloads a divisor into
the 8253's register for counter 2. The 1.19 MHz system clock is divided
by this number and the resulting square wave is sent to the speaker.
Once the divisor is loaded, the microprocessor is free to do something
else while the tone plays in the background.
Since the hardware is not set up to produce three tones simultaneously,
it becomes necessary to get the microprocessor to do the dividing of the
system clock. The program loop which performs the divisions for the
three tones must be very quick in order to produce tones with high audio
frequencies. I found when using memory to store intermediate division
results, the relatively slow memory accesses took too many clock cycles
giving three low growls at best. Using the 8088 internal registers
speeded up the loop considerably since the register accesses are about
ten times faster than memory accesses.
The assembly language routine presented here was written to be called
from BASIC. The code is divided into three sections. The first section
(from "tri" to "sort") sets up the pointers to the datapassed to the
routine. Also, the 8088 internal registers are zeroed.
The second section (from "sort" to "loop") sorts the 16-bit integer num-
bers passed to the routine. The three most significant bits of the
integer identify the remaining 13 bits as either voice 1, 2, or 3 period
data, note duration data, tempo data or end-of-song flag. The bit pat-
tern 000 identifies that the song has ended and causes the routine to
return toBASIC.The pattern 001 sets the duration of time that the
current notes will play before the next notes start. The pattern 010
sets the tempo of the playback. The pattern 100 identifies that the 13
remaining bits represent period data for voice 1.The pattern 101 iden-
tifies voice 2 period data, and the pattern 110 identifies voice 3 data.
The last section (from "loop" to "end")performs the division of the
system clock and switching of the speakerfor each of the three voices.
This is the section of the program that actually makes the tones. For
speed, the routine for each voiceis written entirely rather than writ-
ten as a single subroutine called three times. This was done because
jumps and calls require at least fifteen clock cycles.
Although the assembly language routine parses the 16-bit binary data
into three bits identifying data type and thirteen bits representing a
numerical value, BASIC treats this data as an array of 2's complement
16-bit single precision integers. This means that the decimal equiv-
alent of the number is given by the following equation.
N = -2**15*a(15) + 2**14*a(14)
2**13*a(13) + 2**12*a(12)
2**11*a(11) + 2**10*a(10)
2**9*a(9) + 2**8*a(8) + 2**7*a(7)
2**6*a(6) + 2**5*a(5) + 2**4*a(4)
2**3*a(3) + 2**2*a(2) + 2**1*a(1)
where a(n) is a 1 or 0 in the nth bit of the 16 bit number. Notice that
the most significant bit(the 16th bit)represents a negative number
(-32768) whereas all theother bits represent positivenumbers. The
numerical range of the 2's complement integers is -32768 to 32767.
It can be seen that the pattern of the three most significant bits can
be represented asa decimal number. The patternfor identifying dura-
tion data(001) corresponds to a decimal valueof 8192. Tempo data
(010) equals 16384, voice 1 data (100) equals -32768, voice 2 data (101)
equals -24576, and voice three data (110) equals -16384. The 13 remain-
ing bits can represent decimal numbers ranging from 0 to 16383.
The accompanying BASIC program contains data statements which load an
integer array with the data for playing the first part of the Rondo Alla
Turca by Wolfgang A. Mozart. The first entry in the integer data array
sets the tempo ofthe playback. A typical tempo value of 512 plus the
tempo identifier 16384 gives the integer data value 16896.
A note's duration is specified by its length relative to a 32nd note.
For example, a quarter note equals eight 32nd notes, so a decimal value
of 8 is added to the duration identifier, 8192. The resulting number is
8192 + 8 = 8200.Likewise, a whole note is represented by the decimal
integer 8192 + 32 = 8224.
The voice data is determined by adding the number representing the fre-
quency of the desired note to the number identifying the desired voice
of the three possible. The following table gives the numbers represent-
ing the notes of the scale. For example, middle C on voice 1 is repres-
ented by adding the voice 1 identifier with the value for middle C. The
resulting number is -32768 + 1024 = -31744. To turn voice 1 off, the
data would be -32768 + 0 = -32768.
NOTE DATA TABLE
LOW MIDDLE HIGH
--- ------ ----
C= 512C = 1024 C = 2048
C# = 542C# = 1085 C# = 2170
D= 575D = 1149 D = 2299
D# = 609D# = 1218 D# = 2435
E= 645E = 1290 E = 2580
F= 683F = 1367 F = 2734
F# = 724F# = 1448 F# = 2896
G= 767G = 1534 G = 3069
G# = 813G# = 1625 G# = 3251
A= 861A = 1722 A = 3444
A# = 912A# = 1825 A# = 3649
B= 967B = 1933 B = 3866
No tone = 0