Programming the sound generators
Playing notes
Direct sound
+----+--+----+ D2 |1 o T 16| Vcc D1 |2 M 15| D3 D0 |3 S 14| CLK READY |4 13| D4 WE* |5 9 12| D5 CS* |6 9 11| D6 AUDIOOUT |7 1 10| D7 Vss |8 9 9| AUDIOIN +------------+Power supply
CPU interface
D0-D7 These input pins accept commands from the CPU.
CS* Chip select. When this pin is active (low) the TMS9919 will input data from the data bus. For proper operation, the device should be initialized by pulling both CS* and WE* high.
WE* Write enable. When active (low) this pin signals a write operation. As far as I know, all operations dealing with the TMS9919 are write operations. I'm not aware that there is anything to read. Anyone?
READY This pin goes low to put the CPU on hold until the TMS9919
is ready: it takes approximately 32 CLOCK cycles for the chip to load data
into a register.
CS* | WE* | READY |
---|---|---|
L | L | L |
L | H | L |
H | L | H |
H | H | H |
Sound pins
CLK Clock pin. Receives the basic clock signal used to generate
sounds. The acceptable frequency ranges from 0 to 4MHz. On the TI-99/4A,
this pin can receive either a 3.58 MHz signal from the VDP CPUCLK pin (pin
38), or a 447.4 kHz generated by the VDP GROMCLK pin (pin 37). The actual
signal is selected via a jumper in the console. This was probably done
so you could have either a SN76494N or a SN76489 as your sound chip. The
SN76494N would use the 447.4 kHz and divide it by 2 internally, whereas
the SN76489 would use the 3.58 MHz signal and divide it by 16 internally.
The end result is the same in both cases: an internal clock signal of 223.7
kHz.
AUDIOIN This input pin receives preformed sound signals and transmits them to the AUDIOOUT pin. It is an analog input. In the TI-99/4A is is connected to pin #44 of the side port via a 330 Ohm resistor, for sound input from the speech synthesizer. It is also connected in parallel to the cassette input port which allow us to ear that funny noise when reading a program from tape. Finally, the TMS9901 also controls this line via a transistor, which could be used by application programs to directly generate sounds via the CRU. This pin is marked n.c. on the SN76489.
AUDIOOUT This pin carries the sound data to the monitor's speaker
(pin 3 of the connector). It is connected to an internal analog audio amplifier.
--+ #7|--------+----||---+--uuu---+----< pin 3 monitor port | | 100uF | ?uH | | 0.1uF = 10nF = = 10nF | | | | | +--www---Gnd Gnd --+ 10 Ohm |
The chip contains three tone generators and a noise generator. Each tone generator consists in: a programmable frequency divider that divides the CLOCK frequency by a user-definable value to generate the appropriate tone, a fixed 1/2 frequency divider and a programmable attenuator that allows to control the volume in increments of 2 dB.
The noise generator comprises: a fixed 1/16 divider, a programmable divider (1/2, 1/4 or 1/8), a noise source and an attenuator identical to that of the tone generators. The programmable divider can also accept the output of tone generator number 3 as an input. The noise source is a shift register with a XOR feed-back to prevent lockup in zero state. It is possible to control this feedback circuit to toggle between a "white" noise and a periodic noise. The shift register is cleared each time the divider is loaded, then it shifts at the frequency determined by the divider output.
The four signals are combined by an analog summer, whose output drives
an audio amplifier, typically with a current from 0 to -160 uA. The audio
output can generate upto 10 mA.
______ _____ __________ ,--| 1/N |----| 1/2 |---|Attenuator|---, +1.5V ______ | ______ _____ __________ | | |\ CLOCK >--| 1/16 |--+--| 1/N |----| 1/2 |---|Attenuator|-, '-|\ '--|+\ | ______ _____ __________ '---| \ | \_ _ AUDIOOUT ,-----------+--| 1/N |----| 1/2 |-+-|Attenuator|-----| \_ _| / | | ,---------------' | / | |-/ | | ______ | _____ __________ ,--| / | |/ | '--| 1/16 |----| 1/n |----|shift|---|Attenuator|--' |/ '--WWW-' Summer 17K |
The four attenuators and the four programmable dividers can be accessed from the data bus, as 8 dedicated registers. The tone dividers are 10-bit wide and thus require two bytes of data.
1 | R0 | R1 | R2 | F6 | F7 | F8 | F9 | 0 | x | F0 | F1 | F2 | F3 | F4 | F5 |
R0-R2: Register address (see below)
F0-F9: Number by which to divide the frequency (>01-3F)
NB The register address is latched on-chip when the first byte is passed. This means that the second byte can be sent repeatedly, as it is identified by a 0 in the most significant bit. In this way, you can very rapidly sweep frequencies, by changing only the 6 most significant bits (F0-F5) with a single byte transfer.
1 | R0 | R1 | R2 | x | FB | F0 | F1 |
R0-R2: Register address (see below)
FB: Feed-back option 0 = periodic, 1 = white noise
F0-F1: Number by which to divide the frequency: 00 = 512, 01 = 1024,
10 = 2048, 11 = use generator #3
1 | R0 | R1 | R2 | A0 | A1 | A2 | A3 |
R0-R2: Register address (see below)
A0-A3: Attenuation. A0 = 16 dB, A1 = 8 dB, A2 = 4dB, A3 = 2 dB. 1111
= sound off
The attenuation accuracy is quite poor: +/- 1 dB
R0 | R1 | R2 | Register |
---|---|---|---|
0 | 0 | 0 | Tone 1 frequency divider |
0 | 0 | 1 | Tone 1 attenuator |
0 | 1 | 0 | Tone 2 frequency divider |
0 | 1 | 1 | Tone 2 attenuator |
1 | 0 | 0 | Tone 3 frequency divider |
1 | 0 | 1 | Tone 3 attenuator |
1 | 1 | 0 | Noise frequency divider |
1 | 1 | 1 | Noise attenuator |
______ _________ _______ \_____________/ \_____________/ CE* _____|_a_| 90-150 ns | \___________/ >0 | |a \ / READY | >0 | | | \_____________/ | \_______ / WE* |>0| | X first byte X second byte X D0-D7
a) 90-150 ns
Supply voltage: Vcc 7V Input voltage: AUDIOIN 0.9V All others 7V Output current on AUDIOOUT 10 mA Continuous power dissipation 1150 mW Free air temperature: 0 to 70 `C Storage temperature: -55 to 150 `C
Parameter | Min | Nom | Max | Unit |
---|---|---|---|---|
Supply voltage, Vcc | 4.5 | 5 | 5.5 | V |
High-level input voltage | 2 | . | . | V |
Low-level input voltage | . | . | 0.8 | V |
AUDIOIN input current | 0 | . | 1.8 | mA |
High-level output voltage (READY) | . | . | 5.5 | V |
Low-level output current (READY) | . | . | 2 | mA |
Input clock frequency | . | . | 4 | MHz |
Free-air temperature | 0 | . | 70 | `C |
Parameter | Test conditions | Min | Typ | Max | Unit |
---|---|---|---|---|---|
High-level output current (READY) | Vo = +5.5V | . | . | 10 | uA |
High-level input current (all digital pins) | Vi = Vcc | . | . | 10 | uA |
Low-level input current CE* pin
D0-D7, WE*, CLK pins |
Vi = 0 | . | -25
-10 |
-175
-70 |
uA |
Input bias voltage (AUDIOIN) | R = 4.7 KOhm to Vcc | 0.5 | 0.7 | 0.9 | V |
High-level output voltage (AUDIOOUT) | . | . | . | 5.5 | V |
Low-level output voltage (READY) | I = 2 mA | . | 0.25 | 0.4 | V |
Peak-to-peak output voltage (AUDIOOUT) | Vcc = +5V Attenuation = 0 dB
(other generators -30 dB) |
260 | . | . | mV |
Supply current | Outputs open | . | 30 | 50 | mA |
Input capacitance | . | . | . | 15 | pF |
Generator | Frequency | Volume |
---|---|---|
Tone 1 | >8z >xy | >9v |
Tone 2 | >Az >yx | >Bv |
Tone 2 | >Cz >yx | >Dv |
Noise | >En | >Fv |
Frequency = 111860.8 Hz Volume v: +1 = -2 dB (>F = off) xyz
Once the first byte is passed, the second byte can be modified as often as needed, as long as no other command is passed. This allows for rapid frequency changes, as only one byte needs to be passed (although only the 6 most-significant bits will be changed).
1 1 1 0 0 w r r >E | | | | 0 0 : 6991 Hz | 0 1 : 3496 Hz | 1 0 : 1748 Hz | 1 1 : freq of generator 3 | 0 : Periodic noise 1 : White noise
C |
- | .735 | .C1A | .60D | .B06 | .503 | .B01 |
############ | - | .732 | .419 | .A0C | .506 | .203 | .901 |
D |
- | .A2F | .D17 | .E0B | .F05 | .003 | .801 |
############ | - | .F2C | .816 | .40B | .A05 | .D02 | .601 |
E |
- | .72A | .315 | .A0A | .505 | .A02 | .501 |
F |
- | .128 | .014 | .00A | .005 | .802 | .401 |
############ | - | .D25 | .E12 | .709 | .C04 | .602 | - |
G |
- | .B23 | .D11 | .F08 | .704 | .402 | - |
############ | - | .B21 | .D10 | .708 | .304 | .202 | - |
A |
.93F | .C1F | .E0F | .F07 | .004 | .002 | - |
############ | .03C | .01E | .00F | .807 | .C03 | .E01 | - |
B |
.A38 | .51C | .20E | .107 | .903 | .C01 | - |
Notes
The underlined .E0F corresponds to the middle A at 440 Hz. Note that
many orchestra nowadays tend to tune their middle A at a higher pitch:
at 441, 442 or even upto 444 Hz. I'll leave you with the task to correct
this table accordingly..
You may have noted that the calculated frequency follows a log scale, dobbling from one column to another (taking rounding mistakes into account). That's because of the way our ear is designed: octaves, subjectively "equal" intervals of 12 half-tones correspond to a dobbling of the frequency. This allow us to ear a wide range of frequencies.
One last remark: this table works well to emulate keyboard instruments (piano, harpsichord, organ) that are "tempered", i.e. on which an A sharp is the same as a B flat. This is not the case for other instruments like the violin or the cello: on these the A sharp is a tad (a "coma" in french, a "microtone" in english?) higher than the B flat. Most people won't notice the difference, unless the same note is played on a piano and a violin together: it gives the impression that the violinist does not play in tune. He does! It's the piano that's wrong.
* Let's play a note LI R0,>8400 Sound port address on the TI-99/4A LI R1,>8E0F Middle A on generator 1 MOVB R1,*R0 SWPB R1 MOVB R1,*R0 LI R2,>9200 Volume: 4 dB below maximum MOVB R2,*R0 Play it ... Wait * Now let's make a noise (the note is still playing) LI R1,>DF00 Make sure generator 3 is off MOVB R1,*R0 LI R1,>CC1A C on generator 3 MOVB R1,*R0 SWPB R1 MOVB R1,*R0 LI R1,>E300 Periodic noise, picking frequency from gen 3 MOVB R1,*R0 LI R1,>F000 Volume: full blast ... Wait * Turn all sounds off LI R1,>9F00 Start with generator 1 LP1 MOVB R1,*R0 Turn it off AI R1,>2000 Next generator JNC LP1 Carry set when >EF becomes >00 B *R11 |
This example is deceptively simple: programming music is a fairly complicated task. First you must decide how to encode your music: do you want a separate list for each generator or a common list?
Then you must time each note. A good way is to place a sound list in the VDP memory and to let the ISR play it, but according to your needs you may want to do it yourself, for instance with the TMS9901 timer. You must also remember that each note should be followed by a small silence, unless you're playing legato (although to repeat a note in legato, you still need a small silence). Conversely, to play staccato or pizzicatti you must reduce the duration of a note and increase the silence accordingly.
And then, there is the problem of the volume: progressive changes in volume (crescendo, decrescendo) can be tricky to program as they must be integrated into your sound list. Finally for some instruments to sound natural, the sound of each note should be slowly fading over time. That's the case with the piano for instance (but not with the flute: as long as the musician is blowing steadily, the volume remains unchanged).
* Tentative routine to program direct sound via the CRU * R1 contains a pointer to a list of sound bits * R2 is the size of the list, in words * R3 is the delay between two bits (i.e. depends on the sampling rate) * In addition, the routine makes use of R4, R10 and R12. BITSND MOV R11,R10 CLR R12 CRU base of the TMS9901 LP2 MOV *R1+,R4 Get one word from the list LI R0,16 16 bits per word LP1 SLA R4,1 Test next bit JNC SK1 SBO 24 Send 1 to sound gate JMP SK2 SK1 SBZ 24 Send 0 to sound gate SK2 BL @DELAY Delay between two bits DEC R0 JNE LP1 Next bit DEC R2 JNE LP2 Next word B *R10 * Delay routine DELAY MOV R3,R12 Get delay value (in R12, to save a register) LP3 DEC R12 JNE LP3 Keep waiting B *R11 R12 is now >0000, which is the correct CRU value * NB: for short delays, we should account for the longer time between two * words, due to the 4 extra instructions needed to load a new word. * Main program START LI R1,BUFFER Buffer address LI R2,>0800 Buffer size LI R3,>AAAA Test pattern: highest possible pitch (1010 1010) L0 MOV R3,*R1+ Fill buffer with test pattern DEC R2 JNE L0 LI R1,BUFFER Buffer address LI R2,>0800 Buffer size LI R3,>0040 Delay value BL @BITSND Play buffer content ... Return when done (this may take time) |
Here, the tricky part is to determine the list of bits to be passed. I wonder if PC .RAW files could be used?
Also, I don't think there is any way to control the volume. Any suggestions?
Barry made the SoundFX source available on his website, so I encourage
you to have a look at it. Here is an excerpt,
containing only the sound generation routines and the routines that translate
other sound files in SoundFX format.