PC Engine Programmable Sound Generator by Paul Clifford (paul@plasma.demon.co.uk) The PSG provides 6 sound channels, which can be conveniently paired according to the functionality they provide: 0-1 - Waveform playback Frequency modulation (channel 1 muted) 2-3 - Waveform playback only 4-5 - Waveform playback White noise generation Waveform playback is the most common and allows a 32 byte, 5 bit unsigned linear sample to be played back at selected frequencies. Frequency modulation takes this one step further, allowing the playback frequency to be dynamically adjusted according to a specified pattern. White noise is used to simulate percussion instruments and effects such as explosions, by means of a pseudo random square wave. Alternatively, each channel can be individually switched to "Direct D/A" mode whereby the programmer can send data directly to the sound mixer, allowing more complex sound patterns to be generated, such as speech. Inevitably this requires more programming effort and CPU time. $0800 - Channel select Bit 7-3 - (unused) Bit 2-0 - Channel number (only 0-5 valid) This selects the channel used for operations on registers $0802 to $0807. Be aware that the noise register is only valid for channels 4 and 5, and that the LFO registers will affect channels 0 and 1 only, irrespective of what values you set here. $0801 - Global sound balance Bit 7-4 - Volume from left output Bit 3-0 - Volume from right output This alters the overall sound volume once all the channels have been mixed together. $0802 - Fine frequency adjust Bit 7-0 - The lower 8 bits of the 12 bit channel frequency The channel frequency is a 12 bit value used as a divider into the 3.58MHz clock. The exact use depends on what type of sound the channel is being used for at the time. For waveform output, a copy of this value is, in effect, decremented 3,580,000 times a second until zero is reached. When this happens the PSG advances an internal pointer into the channel's waveform buffer by one. Since the buffer is 32 bytes long, this frequency value can be converted into more familiar units as follows: 3580000 frequency (Hz) = --------------------- 32 x (12 bit value) Hence, in the opposite direction we would have: 3580000 12 bit value = --------------------- 32 x frequency (Hz) $0803 - Rough frequency adjust Bit 7-4 - (unused) Bit 3-0 - The upper 4 bits of the 12 bit channel frequency See above for explanation. $0804 - Channel on/off, DDA on/off, channel volume Bit 7 - Channel on (1) or off (0) Bit 6 - DDA output on (1) or off (0) Bit 5 - (unused) Bit 4-0 - Overall channel volume Depending on the setting of bit 6 and 7, a channel can be in one of four states: ch on dda state 0 0 Writes to $0806 will store the value in the waveform buffer and advance the waveform write index. 0 1 The waveform write index is reset to 0, so that further writes to $0806 will start from the beginning of the waveform buffer. 1 0 Waveform output is enabled. The channel output is generated from the waveform buffer, advancing an internal read index at the rate specified by the channel frequency registers ($0802 and $0803). 1 1 Direct D/A mode enabled. Any value written to $0806 is immediately mixed into the overall sound output. The overall channel volume can be used to fade in and out individual channels (see $0805 for altering channel balance). $0805 - Channel balance Bit 7-4 - Volume to left output Bit 3-0 - Volume to right output This allows you to alter the balance of an individual channel. $0806 - Channel sound data Bit 7-5 - (unused) Bit 4-0 - 5 bit unsigned linear sample data A write to this location will have different effects depending on the settings of bit 6 and 7 of register $0804. When DDA is enabled, data written here is mixed in directly with the outputs from the other channels (you also need to turn the channel on if you actually want to hear it though!). This is useful for handling things like speech which are not easy (possible?) to do with the waveform buffer. When the channel is turned off and DDA disabled, data written here is copied into the channel's waveform buffer and the internal write index incremented by one, wrapping round at 32 to 0. For example, to make a sine wave you could write the following hex values into the waveform buffer (shamelessly plundered from the PSG patent): 0f, 12, 15, 17, 19, 1b, 1d, 1e, 1e, 1e, 1d, 1b, 19, 17, 15, 12, 0f, 0c, 09, 07, 05, 03, 01, 00, 00, 00, 01, 03, 05, 07, 09, 0c $0807 - Noise enable, noise frequency Bit 7 - Noise on (1) or off (0) Bit 6-5 - (unused) Bit 4-0 - Noise frequency This register is only effective for channels 4 and 5. The output of white noise is enabled by setting bit 7, at which point the PSG will stop reading data from the channel's waveform buffer and begin generating a rectangular waveform with a frequency defined by the lower 5 bits of the register. There seems to be some confusion in the patent as to how to interpret the frequency value ("... sound is shifted from low frequency to high frequency when ... the lower five bits is varied from 0 to 1F", followed by a formula which seems to imply exactly the opposite). After a little experimenting I found that it is easiest to imagine you are dealing with a 64 byte waveform buffer divided into two parts. The first half randomly contains either all 0 or all 31, and likewise with the second half, so that you have either a complete or a partial cycle of a square wave, ie one of the following: ---- ---- -------- ---- ---- -------- 0 32 64 0 32 64 0 32 64 0 32 64 (A new "waveform" is automatically generated each time the read index wraps back around to zero). The formula for the actual noise frequency should then be: 3580000 frequency (Hz) = -------------------- 64 x (5 bit value) The only slight "fly in the ointment" is that replacing (5 bit value) with 31 - (5 bit value) appears to be more accurate. However, this could explain the extract "... sound is shifted from low frequency to high frequency when ... the lower five bits is varied from 0 to 1F". $0808 - LFO frequency Bit 7-0 - LFO frequency When the LFO is active (see $0809), the channel 1 waveform buffer is assumed to contain frequency modulation data rather than samples (and hence channel 1 is automatically muted). The frequency at which this buffer is read from is defined by multiplying the LFO frequency by the 12 bit frequency ($0802 and $0803) from channel 1. This leads to the following formula: 3580000 frequency (Hz) = ------------------------------------------- 32 x (12 bit frequency) x (LFO frequency) The values in channel 1's waveform buffer are treated as 5 bit signed integers which are shifted left as necessary (see $0809) and added to channel 0's frequency. This result then becomes the new frequency for channel 0 until a new byte of FM data is read, at which point the frequency is recalculated again. $0809 - LFO trigger, LFO control Bit 7 - LFO trigger (0 = LFO on, 1 = LFO off/reset) Bit 6-2 - (unused) Bit 1-0 - LFO control Bit 7 controls whether the low frequency oscillator (LFO) is active or not. When set it causes the LFO to switch off and resets the channel 1 waveform read position to 0. When clear, the LFO is activated, causing the data in channel 1's waveform buffer to be used to frequency modulate the output from channel 0. The process by which this is achieved depends on the value in "LFO control" (the lower two bits): 00 - No frequency modulation is performed, so the output from channel 0 is unchanged. 01 - The FM data is added directly to channel 0's frequency. 10 - The FM data is shifted left by four places then added to the frequency. 11 - The FM data is shifted left by eight places then added to the frequency. For example, if "LFO control" was set to 2 and the value read from the channel 1 waveform buffer was binary 10111 then (-9 << 4) would be added to channel 0's frequency.