]> Shamusworld >> Repos - thunder/blob - src/psg.cpp
Added save states; updated application icon.
[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
19 // in your buffer for them!
20 //
21
22 #include "psg.h"
23 #include <stdio.h>
24 #include <string.h>
25 #include "fileio.h"
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 uint32_t sampleRate = 44100;
41 static uint32_t divisor;
42
43 void PSGSaveState(FILE * file)
44 {
45         size_t ignored = fwrite(memory, 1, 0x200, file);
46         ignored = fwrite(&voice, 1, sizeof(voice), file);
47 }
48
49 void PSGLoadState(FILE * file)
50 {
51         size_t ignored = fread(memory, 1, 0x200, file);
52         ignored = fread(&voice, 1, sizeof(voice), file);
53 }
54
55 void InitPSG(uint32_t s/*=44100*/)
56 {
57         sampleRate = s;
58         divisor = (uint32_t)((float)s / 0.735); // Voodoo constant
59
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;
63 }
64
65 //
66 // Note that it doesn't wipe out the buffer passed in; if you want it wiped,
67 // you have to wipe it yourself.  :-)
68 //
69 void UpdatePSG(uint8_t * buffer, int count)
70 {
71         // Recast buffer as int16, we're doing 16-bit sound here
72         int16_t * p = (int16_t *)buffer;
73
74         for(int i=0; i<8; i++)
75         {
76                 if (!voice[i].noise)
77                 {
78                         if ((voice[i].leftVolume == 0) || (voice[i].frequency == 0))
79                                 continue;
80
81                         for(int j=0; j<count; j++)
82                         {
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;
89                         }
90                 }
91                 else
92                 {
93                         if ((voice[i].leftVolume == 0) || ((voice[i].frequency & 0xFF) == 0))
94                                 continue;
95
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
99                         // Rolling Thunder...
100                         int16_t sample = (7 * voice[i].leftVolume) << 4;
101                         int16_t noiseSign = 1;
102                         int16_t hold = 1 << 1;
103
104                         for(int j=0; j<count; j++)
105                         {
106                                 p[j] += sample * noiseSign;
107
108                                 if (hold)
109                                 {
110                                         hold--;
111                                         continue;
112                                 }
113
114                                 hold = 1 << 1;
115
116                                 voice[i].counter += (voice[i].frequency & 0xFF) << 4;
117                                 int c = voice[i].counter >> 12;
118                                 voice[i].counter &= 0xFFF;
119
120                                 for(; c>0; c--)
121                                 {
122                                         if ((voice[i].noiseSeed + 1) & 0x02)
123                                                 noiseSign *= -1;
124
125                                         if (voice[i].noiseSeed & 0x01)
126                                                 voice[i].noiseSeed ^= 0x28000;
127
128                                         voice[i].noiseSeed >>= 1;
129                                 }
130                         }
131                 }
132         }
133 }
134
135 void WritePSG(uint16_t address, uint8_t data)
136 {
137         if ((address >= 0x100) && (address <= 0x13F))
138         {
139                 uint8_t channel = (address - 0x100) / 8;
140                 uint8_t knob = (address - 0x100) - (channel * 8);
141
142                 if (channel < 8)
143                 {
144                         switch (knob)
145                         {
146                         case 0:
147                                 voice[channel].leftVolume = data & 0x0F;
148                                 break;
149                         case 1:
150                                 voice[channel].waveform = data >> 4;
151                                 voice[channel].frequency = ((data & 0x0F) << 16)
152                                         | (voice[channel].frequency & 0x0FFFF);
153                                 break;
154                         case 2:
155                                 voice[channel].frequency = (data << 8)
156                                         | (voice[channel].frequency & 0xF00FF);
157                                 break;
158                         case 3:
159                                 voice[channel].frequency = data
160                                         | (voice[channel].frequency & 0xFFF00);
161                                 break;
162                         case 4:
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);
166                                 break;
167                         }
168                 }
169         }
170
171         memory[address & 0x01FF] = data;
172 }
173
174 uint8_t ReadPSG(uint16_t address)
175 {
176         return memory[address & 0x01FF];
177 }