]> Shamusworld >> Repos - thunder/blob - src/psg.cpp
5d6e6693101fabae7981911dd16dd0826e39600a
[thunder] / src / psg.cpp
1 //
2 // PSG handler
3 //
4 // This emulates the Rolling Thunder PSG (Namco CUS30)
5 //
6 // by James Hammons
7 // (C) 2014 Underground Software
8 //
9 // JLH = James Hammons <jlhamm@acm.org>
10 //
11 // Who  When        What
12 // ---  ----------  -----------------------------------------------------------
13 // JLH  04/21/2014  Created this file.
14 //
15 //
16 // Notes:
17 // ------
18 // The emulator creates signed 16-bit samples. Make sure there's enough room in
19 // your buffer for them!
20 //
21
22 #include "psg.h"
23 #include <stdio.h>
24 #include <string.h>
25
26
27 struct Voice
28 {
29         uint8_t leftVolume;
30         uint8_t rightVolume;
31         uint8_t waveform;
32         uint32_t frequency;
33         uint32_t counter;
34         bool noise;
35         uint32_t noiseSeed;
36 };
37
38 static Voice voice[8];
39 static uint8_t memory[0x200];
40 //static 
41 static uint32_t sampleRate = 44100;
42 static uint32_t divisor;
43
44
45 void InitPSG(uint32_t s/*=44100*/)
46 {
47         sampleRate = s;
48         divisor = (uint32_t)((float)s / 0.735); // Voodoo constant
49
50         // Noise generation will fail if the noise seeds aren't primed...
51         for(int i=0; i<8; i++)
52                 voice[i].noiseSeed = 1;
53 }
54
55
56 //
57 // Note that it doesn't wipe out the buffer passed in, if you want it wiped,
58 // you have to wipe it yourself. :-)
59 //
60 void UpdatePSG(uint8_t * buffer, int count)
61 {
62 /*
63 if F == 44100, then counter++ for each sample.
64 if F == 88200, then counter += 2 for each sample.
65 if F == 22050, then counter += 0.5 for each sample.
66 */
67         // Recast buffer as int16, we're doing 16-bit sound here
68         int16_t * p = (int16_t *)buffer;
69
70         for(int i=0; i<8; i++)
71         {
72                 if (!voice[i].noise)
73                 {
74                         if ((voice[i].leftVolume == 0) || (voice[i].frequency == 0))
75                                 continue;
76
77                         for(int j=0; j<count; j++)
78                         {
79 //                              uint8_t pos = (voice[i].counter / (sampleRate * 2)) & 0x1F;
80 // Where does 60000 come from? IDK. This would probably change depending on
81 // the playback rate (currently, 44100). N.B.: 58800 is a hair too high in pitch!
82 // 44100 / 60000 = 0.735
83 // 192000 / 60000 = 3.2
84 //                              uint8_t pos = (voice[i].counter / (60000)) & 0x1F;
85 //                              uint8_t pos = (voice[i].counter / (65536)) & 0x1F;
86                                 uint8_t pos = (voice[i].counter / divisor) & 0x1F;
87                                 uint8_t sample = ((pos & 0x01) == 0
88                                         ? memory[(voice[i].waveform * 16) + (pos / 2)] >> 4
89                                         : memory[(voice[i].waveform * 16) + (pos / 2)]) & 0x0F;
90 //                              p[j] += (((memory[(voice[i].waveform * 32) + pos] & 0x0F) - 8)
91 //                                      * voice[i].leftVolume) << 5;
92                                 p[j] += ((sample - 8) * voice[i].leftVolume) << 5;
93                                 voice[i].counter += voice[i].frequency;
94                         }
95                 }
96                 else
97                 {
98                         if ((voice[i].leftVolume == 0) || ((voice[i].frequency & 0xFF) == 0))
99                                 continue;
100
101                         // The hold stuff here is VOODOO
102                         // Need to figure out what's really going on here, what the clock
103                         // rate of this chip is. Also, some freqs can be > 255 according to
104                         // Rolling Thunder...
105                         int16_t sample = (7 * voice[i].leftVolume) << 4;
106                         int16_t noiseSign = 1;
107                         int16_t hold = 1 << 1;
108
109                         for(int j=0; j<count; j++)
110                         {
111                                 p[j] += sample * noiseSign;
112
113                                 if (hold)
114                                 {
115                                         hold--;
116                                         continue;
117                                 }
118
119                                 hold = 1 << 1;
120
121                                 voice[i].counter += (voice[i].frequency & 0xFF) << 4;
122                                 int c = voice[i].counter >> 12;
123                                 voice[i].counter &= 0xFFF;
124
125                                 for(; c>0; c--)
126                                 {
127                                         if ((voice[i].noiseSeed + 1) & 0x02)
128                                                 noiseSign *= -1;
129
130                                         if (voice[i].noiseSeed & 0x01)
131                                                 voice[i].noiseSeed ^= 0x28000;
132
133                                         voice[i].noiseSeed >>= 1;
134                                 }
135                         }
136                 }
137         }
138 }
139
140
141 void WritePSG(uint16_t address, uint8_t data)
142 {
143         if ((address >= 0x100) && (address <= 0x13F))
144         {
145                 uint8_t channel = (address - 0x100) / 8;
146                 uint8_t knob = (address - 0x100) - (channel * 8);
147
148                 if (channel < 8)
149                 {
150                         switch (knob)
151                         {
152                         case 0:
153                                 voice[channel].leftVolume = data & 0x0F;
154                                 break;
155                         case 1:
156                                 voice[channel].waveform = data >> 4;
157                                 voice[channel].frequency = ((data & 0x0F) << 16) 
158                                         | (voice[channel].frequency & 0x0FFFF);
159 #if 0
160 printf("PSG: Setting waveform on channel %i to %i...\n", channel, voice[channel].waveform);
161 #endif
162                                 break;
163                         case 2:
164                                 voice[channel].frequency = (data << 8)
165                                         | (voice[channel].frequency & 0xF00FF);
166                                 break;
167                         case 3:
168                                 voice[channel].frequency = data
169                                         | (voice[channel].frequency & 0xFFF00);
170                                 break;
171                         case 4:
172                                 voice[channel].rightVolume = data & 0x0F;
173                                 // Noise switch is channel # + 1 (wraps to zero)
174                                 voice[(channel + 1) & 0x07].noise = (data & 0x80 ? true : false);
175 #if 0
176 if (voice[(channel + 1) & 0x07].noise)
177 {
178         uint8_t ch = (channel + 1) & 0x07;
179         printf("PSG: Setting noise on channel %i, vol=%i, freq=%i...\n", ch, voice[ch].leftVolume, voice[ch].frequency);
180 }
181 #endif
182 #if 0
183 if (data & 0x0F)
184 {
185         printf("PSG: Setting right volume on channel %i: vol=%i...\n", channel, voice[channel].rightVolume);
186 }
187 #endif
188                                 break;
189                         }
190                 }
191
192 //              return;
193         }
194 #if 0
195         else
196                 printf("PSG: Write to $%03X of $%02X...\n", address, data);
197 #endif
198
199 //if (address < 0x100)
200 //      printf("PSG: Waveform byte[$%02X] = $%02X...\n", address, data);
201
202         memory[address & 0x01FF] = data;
203 }
204
205
206 uint8_t ReadPSG(uint16_t address)
207 {
208         return memory[address & 0x01FF];
209 }
210