4 // This emulates the Rolling Thunder PSG (Namco CUS30)
7 // (C) 2014 Underground Software
9 // JLH = James Hammons <jlhamm@acm.org>
12 // --- ---------- -----------------------------------------------------------
13 // JLH 04/21/2014 Created this file.
18 // The emulator creates signed 16-bit samples. Make sure there's enough room
19 // in your buffer for them!
38 static Voice voice[8];
39 static uint8_t memory[0x200];
40 static uint32_t sampleRate = 44100;
41 static uint32_t divisor;
43 void PSGSaveState(FILE * file)
45 size_t ignored = fwrite(memory, 1, 0x200, file);
46 ignored = fwrite(&voice, 1, sizeof(voice), file);
49 void PSGLoadState(FILE * file)
51 size_t ignored = fread(memory, 1, 0x200, file);
52 ignored = fread(&voice, 1, sizeof(voice), file);
55 void InitPSG(uint32_t s/*=44100*/)
58 divisor = (uint32_t)((float)s / 0.735); // Voodoo constant
60 // Noise generation will fail if the noise seeds aren't primed...
61 for(int i=0; i<8; i++)
62 voice[i].noiseSeed = 1;
66 // Note that it doesn't wipe out the buffer passed in; if you want it wiped,
67 // you have to wipe it yourself. :-)
69 void UpdatePSG(uint8_t * buffer, int count)
71 // Recast buffer as int16, we're doing 16-bit sound here
72 int16_t * p = (int16_t *)buffer;
74 for(int i=0; i<8; i++)
78 if ((voice[i].leftVolume == 0) || (voice[i].frequency == 0))
81 for(int j=0; j<count; j++)
83 uint8_t pos = (voice[i].counter / divisor) & 0x1F;
84 uint8_t sample = ((pos & 0x01) == 0
85 ? memory[(voice[i].waveform * 16) + (pos / 2)] >> 4
86 : memory[(voice[i].waveform * 16) + (pos / 2)]) & 0x0F;
87 p[j] += ((sample - 8) * voice[i].leftVolume) << 5;
88 voice[i].counter += voice[i].frequency;
93 if ((voice[i].leftVolume == 0) || ((voice[i].frequency & 0xFF) == 0))
96 // The hold stuff here is VOODOO
97 // Need to figure out what's really going on here, what the clock
98 // rate of this chip is. Also, some freqs can be > 255 according to
100 int16_t sample = (7 * voice[i].leftVolume) << 4;
101 int16_t noiseSign = 1;
102 int16_t hold = 1 << 1;
104 for(int j=0; j<count; j++)
106 p[j] += sample * noiseSign;
116 voice[i].counter += (voice[i].frequency & 0xFF) << 4;
117 int c = voice[i].counter >> 12;
118 voice[i].counter &= 0xFFF;
122 if ((voice[i].noiseSeed + 1) & 0x02)
125 if (voice[i].noiseSeed & 0x01)
126 voice[i].noiseSeed ^= 0x28000;
128 voice[i].noiseSeed >>= 1;
135 void WritePSG(uint16_t address, uint8_t data)
137 if ((address >= 0x100) && (address <= 0x13F))
139 uint8_t channel = (address - 0x100) / 8;
140 uint8_t knob = (address - 0x100) - (channel * 8);
147 voice[channel].leftVolume = data & 0x0F;
150 voice[channel].waveform = data >> 4;
151 voice[channel].frequency = ((data & 0x0F) << 16)
152 | (voice[channel].frequency & 0x0FFFF);
155 voice[channel].frequency = (data << 8)
156 | (voice[channel].frequency & 0xF00FF);
159 voice[channel].frequency = data
160 | (voice[channel].frequency & 0xFFF00);
163 voice[channel].rightVolume = data & 0x0F;
164 // Noise switch is channel # + 1 (wraps to zero)
165 voice[(channel + 1) & 0x07].noise = (data & 0x80 ? true : false);
171 memory[address & 0x01FF] = data;
174 uint8_t ReadPSG(uint16_t address)
176 return memory[address & 0x01FF];