Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
folium-app
GitHub Repository: folium-app/Folium
Path: blob/a-new-beginning/Cherry/Core/AY8910.cpp
2 views
1
/*
2
* Gearcoleco - ColecoVision Emulator
3
* Copyright (C) 2021 Ignacio Sanchez
4
5
* This program is free software: you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation, either version 3 of the License, or
8
* any later version.
9
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
14
15
* You should have received a copy of the GNU General Public License
16
* along with this program. If not, see http://www.gnu.org/licenses/
17
*
18
*/
19
20
#include "AY8910.h"
21
22
AY8910::AY8910()
23
{
24
InitPointer(m_pBuffer);
25
}
26
27
AY8910::~AY8910()
28
{
29
SafeDeleteArray(m_pBuffer);
30
}
31
32
void AY8910::Init(int clockRate)
33
{
34
m_pBuffer = new s16[GC_AUDIO_BUFFER_SIZE];
35
Reset(clockRate);
36
}
37
38
void AY8910::Reset(int clockRate)
39
{
40
m_iClockRate = clockRate;
41
m_iCyclesPerSample = m_iClockRate / GC_AUDIO_SAMPLE_RATE;
42
43
for (int i = 0; i < 16; i++)
44
{
45
m_Registers[i] = 0;
46
}
47
48
m_Registers[7] = 0xFF;
49
50
for (int i = 0; i < 3; i++)
51
{
52
m_TonePeriod[i] = 1;
53
m_ToneCounter[i] = 0;
54
m_Amplitude[i] = 0;
55
m_ToneDisable[i] = true;
56
m_NoiseDisable[i] = true;
57
m_EnvelopeMode[i] = false;
58
m_Sign[i] = false;
59
}
60
61
m_SelectedRegister = 0;
62
m_NoisePeriod = 1;
63
m_NoiseCounter = 0;
64
m_NoiseShift = 1;
65
m_EnvelopePeriod = 0;
66
m_EnvelopeCounter = 0;
67
m_EnvelopeSegment = false;
68
m_EnvelopeStep = 0;
69
m_EnvelopeVolume = 0;
70
m_iCycleCounter = 0;
71
m_iSampleCounter = 0;
72
m_iBufferIndex = 0;
73
74
for (int i = 0; i < GC_AUDIO_BUFFER_SIZE; i++)
75
{
76
m_pBuffer[i] = 0;
77
}
78
79
m_ElapsedCycles = 0;
80
m_CurrentSample = 0;
81
}
82
83
void AY8910::WriteRegister(u8 value)
84
{
85
Sync();
86
87
m_Registers[m_SelectedRegister] = value & kAY8910RegisterMask[m_SelectedRegister];
88
89
switch (m_SelectedRegister)
90
{
91
// Channel A tone period
92
case 0:
93
case 1:
94
{
95
m_TonePeriod[0] = (m_Registers[1] << 8) | m_Registers[0];
96
if (m_TonePeriod[0] == 0)
97
{
98
m_TonePeriod[0] = 1;
99
}
100
break;
101
}
102
// Channel B tone period
103
case 2:
104
case 3:
105
{
106
m_TonePeriod[1] = (m_Registers[3] << 8) | m_Registers[2];
107
if (m_TonePeriod[1] == 0)
108
{
109
m_TonePeriod[1] = 1;
110
}
111
break;
112
}
113
// Channel C tone period
114
case 4:
115
case 5:
116
{
117
m_TonePeriod[2] = (m_Registers[5] << 8) | m_Registers[4];
118
if (m_TonePeriod[2] == 0)
119
{
120
m_TonePeriod[2] = 1;
121
}
122
break;
123
}
124
// Noise period
125
case 6:
126
{
127
m_NoisePeriod = m_Registers[6];
128
if (m_NoisePeriod == 0)
129
{
130
m_NoisePeriod = 1;
131
}
132
break;
133
}
134
// Mixer
135
case 7:
136
{
137
m_ToneDisable[0] = IsSetBit(m_Registers[7], 0);
138
m_ToneDisable[1] = IsSetBit(m_Registers[7], 1);
139
m_ToneDisable[2] = IsSetBit(m_Registers[7], 2);
140
m_NoiseDisable[0] = IsSetBit(m_Registers[7], 3);
141
m_NoiseDisable[1] = IsSetBit(m_Registers[7], 4);
142
m_NoiseDisable[2] = IsSetBit(m_Registers[7], 5);
143
break;
144
}
145
// Channel A amplitude
146
case 8:
147
{
148
m_Amplitude[0] = m_Registers[8] & 0x0F;
149
m_EnvelopeMode[0] = IsSetBit(m_Registers[8], 4);
150
break;
151
}
152
// Channel B amplitude
153
case 9:
154
{
155
m_Amplitude[1] = m_Registers[9] & 0x0F;
156
m_EnvelopeMode[1] = IsSetBit(m_Registers[9], 4);
157
break;
158
}
159
// Channel C amplitude
160
case 10:
161
{
162
m_Amplitude[2] = m_Registers[10] & 0x0F;
163
m_EnvelopeMode[2] = IsSetBit(m_Registers[10], 4);
164
break;
165
}
166
// Envelope period
167
case 11:
168
case 12:
169
{
170
m_EnvelopePeriod = (m_Registers[12] << 8) | m_Registers[11];
171
break;
172
}
173
// Envelope shape
174
case 13:
175
{
176
m_EnvelopeCounter = 0;
177
m_EnvelopeSegment = false;
178
EnvelopeReset();
179
break;
180
}
181
default:
182
{
183
break;
184
}
185
}
186
}
187
188
u8 AY8910::ReadRegister()
189
{
190
return m_Registers[m_SelectedRegister];
191
}
192
193
void AY8910::SelectRegister(u8 reg)
194
{
195
m_SelectedRegister = reg & 0x0F;
196
}
197
198
void AY8910::EnvelopeReset()
199
{
200
m_EnvelopeStep = 0;
201
202
if (m_EnvelopeSegment)
203
{
204
switch (m_Registers[13])
205
{
206
case 8:
207
case 11:
208
case 13:
209
case 14:
210
{
211
m_EnvelopeVolume = 0x0F;
212
break;
213
}
214
default:
215
{
216
m_EnvelopeVolume = 0x00;
217
break;
218
}
219
}
220
}
221
else
222
{
223
m_EnvelopeVolume = IsSetBit(m_Registers[13], 2) ? 0x00 : 0x0F;
224
}
225
}
226
227
void AY8910::Tick(unsigned int clockCycles)
228
{
229
m_ElapsedCycles += clockCycles;
230
}
231
232
void AY8910::Sync()
233
{
234
for (int i = 0; i < m_ElapsedCycles; i++)
235
{
236
m_iCycleCounter ++;
237
if (m_iCycleCounter >= 16)
238
{
239
m_iCycleCounter -= 16;
240
241
for (int i = 0; i < 3; i++)
242
{
243
m_ToneCounter[i]++;
244
if (m_ToneCounter[i] >= m_TonePeriod[i])
245
{
246
m_ToneCounter[i] = 0;
247
m_Sign[i] = !m_Sign[i];
248
}
249
}
250
251
m_NoiseCounter++;
252
if (m_NoiseCounter >= (m_NoisePeriod << 1))
253
{
254
m_NoiseCounter = 0;
255
m_NoiseShift = (m_NoiseShift >> 1) | (((m_NoiseShift ^ (m_NoiseShift >> 3)) & 0x01) << 16);
256
}
257
258
m_EnvelopeCounter++;
259
if (m_EnvelopeCounter >= (m_EnvelopePeriod << 1))
260
{
261
m_EnvelopeCounter = 0;
262
263
if (m_EnvelopeStep)
264
{
265
if (m_EnvelopeSegment)
266
{
267
if ((m_Registers[13] == 10) || (m_Registers[13] == 12))
268
{
269
m_EnvelopeVolume++;
270
}
271
else if ((m_Registers[13] == 8) || (m_Registers[13] == 14))
272
{
273
m_EnvelopeVolume--;
274
}
275
}
276
else
277
{
278
if (IsSetBit(m_Registers[13], 2))
279
{
280
m_EnvelopeVolume++;
281
}
282
else
283
{
284
m_EnvelopeVolume--;
285
}
286
}
287
}
288
289
m_EnvelopeStep++;
290
if (m_EnvelopeStep >= 16)
291
{
292
if ((m_Registers[13] & 0x09) == 0x08)
293
{
294
m_EnvelopeSegment = !m_EnvelopeSegment;
295
}
296
else
297
{
298
m_EnvelopeSegment = true;
299
}
300
EnvelopeReset();
301
}
302
}
303
}
304
305
m_iSampleCounter++;
306
if (m_iSampleCounter >= m_iCyclesPerSample)
307
{
308
m_iSampleCounter -= m_iCyclesPerSample;
309
m_CurrentSample = 0;
310
311
for (int i = 0; i < 3; i++)
312
{
313
// Filter out ultrasonic frequencies
314
if (m_TonePeriod[i] < 8)
315
continue;
316
317
bool toneOutput = !m_ToneDisable[i] && m_Sign[i];
318
bool noiseOutput = !m_NoiseDisable[i] && ((m_NoiseShift & 0x01) == 0x01);
319
320
if (toneOutput || noiseOutput)
321
{
322
m_CurrentSample += m_EnvelopeMode[i] ? kAY8910VolumeTable[m_EnvelopeVolume] : kAY8910VolumeTable[m_Amplitude[i]];
323
}
324
}
325
326
m_pBuffer[m_iBufferIndex] = m_CurrentSample;
327
m_pBuffer[m_iBufferIndex + 1] = m_CurrentSample;
328
m_iBufferIndex += 2;
329
330
if (m_iBufferIndex >= GC_AUDIO_BUFFER_SIZE)
331
{
332
Debug("SGM Audio buffer overflow");
333
m_iBufferIndex = 0;
334
}
335
}
336
}
337
338
m_ElapsedCycles = 0;
339
}
340
341
int AY8910::EndFrame(s16* pSampleBuffer)
342
{
343
Sync();
344
345
int ret = 0;
346
347
if (IsValidPointer(pSampleBuffer))
348
{
349
ret = m_iBufferIndex;
350
351
for (int i = 0; i < m_iBufferIndex; i++)
352
{
353
pSampleBuffer[i] = m_pBuffer[i];
354
}
355
}
356
357
m_iBufferIndex = 0;
358
359
return ret;
360
}
361
362
void AY8910::SaveState(std::ostream& stream)
363
{
364
stream.write(reinterpret_cast<const char*>(m_Registers), sizeof(m_Registers));
365
stream.write(reinterpret_cast<const char*>(&m_SelectedRegister), sizeof(m_SelectedRegister));
366
stream.write(reinterpret_cast<const char*>(m_TonePeriod), sizeof(m_TonePeriod));
367
stream.write(reinterpret_cast<const char*>(m_ToneCounter), sizeof(m_ToneCounter));
368
stream.write(reinterpret_cast<const char*>(m_Amplitude), sizeof(m_Amplitude));
369
stream.write(reinterpret_cast<const char*>(&m_NoisePeriod), sizeof(m_NoisePeriod));
370
stream.write(reinterpret_cast<const char*>(&m_NoiseCounter), sizeof(m_NoiseCounter));
371
stream.write(reinterpret_cast<const char*>(&m_NoiseShift), sizeof(m_NoiseShift));
372
stream.write(reinterpret_cast<const char*>(&m_EnvelopePeriod), sizeof(m_EnvelopePeriod));
373
stream.write(reinterpret_cast<const char*>(&m_EnvelopeCounter), sizeof(m_EnvelopeCounter));
374
stream.write(reinterpret_cast<const char*>(&m_EnvelopeSegment), sizeof(m_EnvelopeSegment));
375
stream.write(reinterpret_cast<const char*>(&m_EnvelopeStep), sizeof(m_EnvelopeStep));
376
stream.write(reinterpret_cast<const char*>(&m_EnvelopeVolume), sizeof(m_EnvelopeVolume));
377
stream.write(reinterpret_cast<const char*>(m_ToneDisable), sizeof(m_ToneDisable));
378
stream.write(reinterpret_cast<const char*>(m_NoiseDisable), sizeof(m_NoiseDisable));
379
stream.write(reinterpret_cast<const char*>(m_EnvelopeMode), sizeof(m_EnvelopeMode));
380
stream.write(reinterpret_cast<const char*>(m_Sign), sizeof(m_Sign));
381
stream.write(reinterpret_cast<const char*>(&m_iCycleCounter), sizeof(m_iCycleCounter));
382
stream.write(reinterpret_cast<const char*>(&m_iSampleCounter), sizeof(m_iSampleCounter));
383
stream.write(reinterpret_cast<const char*>(m_pBuffer), GC_AUDIO_BUFFER_SIZE * sizeof(s16));
384
stream.write(reinterpret_cast<const char*>(&m_iBufferIndex), sizeof(m_iBufferIndex));
385
stream.write(reinterpret_cast<const char*>(&m_ElapsedCycles), sizeof(m_ElapsedCycles));
386
stream.write(reinterpret_cast<const char*>(&m_iClockRate), sizeof(m_iClockRate));
387
stream.write(reinterpret_cast<const char*>(&m_CurrentSample), sizeof(m_CurrentSample));
388
}
389
390
void AY8910::LoadState(std::istream& stream)
391
{
392
stream.read(reinterpret_cast<char*>(m_Registers), sizeof(m_Registers));
393
stream.read(reinterpret_cast<char*>(&m_SelectedRegister), sizeof(m_SelectedRegister));
394
stream.read(reinterpret_cast<char*>(m_TonePeriod), sizeof(m_TonePeriod));
395
stream.read(reinterpret_cast<char*>(m_ToneCounter), sizeof(m_ToneCounter));
396
stream.read(reinterpret_cast<char*>(m_Amplitude), sizeof(m_Amplitude));
397
stream.read(reinterpret_cast<char*>(&m_NoisePeriod), sizeof(m_NoisePeriod));
398
stream.read(reinterpret_cast<char*>(&m_NoiseCounter), sizeof(m_NoiseCounter));
399
stream.read(reinterpret_cast<char*>(&m_NoiseShift), sizeof(m_NoiseShift));
400
stream.read(reinterpret_cast<char*>(&m_EnvelopePeriod), sizeof(m_EnvelopePeriod));
401
stream.read(reinterpret_cast<char*>(&m_EnvelopeCounter), sizeof(m_EnvelopeCounter));
402
stream.read(reinterpret_cast<char*>(&m_EnvelopeSegment), sizeof(m_EnvelopeSegment));
403
stream.read(reinterpret_cast<char*>(&m_EnvelopeStep), sizeof(m_EnvelopeStep));
404
stream.read(reinterpret_cast<char*>(&m_EnvelopeVolume), sizeof(m_EnvelopeVolume));
405
stream.read(reinterpret_cast<char*>(m_ToneDisable), sizeof(m_ToneDisable));
406
stream.read(reinterpret_cast<char*>(m_NoiseDisable), sizeof(m_NoiseDisable));
407
stream.read(reinterpret_cast<char*>(m_EnvelopeMode), sizeof(m_EnvelopeMode));
408
stream.read(reinterpret_cast<char*>(m_Sign), sizeof(m_Sign));
409
stream.read(reinterpret_cast<char*>(&m_iCycleCounter), sizeof(m_iCycleCounter));
410
stream.read(reinterpret_cast<char*>(&m_iSampleCounter), sizeof(m_iSampleCounter));
411
stream.read(reinterpret_cast<char*>(m_pBuffer), GC_AUDIO_BUFFER_SIZE * sizeof(s16));
412
stream.read(reinterpret_cast<char*>(&m_iBufferIndex), sizeof(m_iBufferIndex));
413
stream.read(reinterpret_cast<char*>(&m_ElapsedCycles), sizeof(m_ElapsedCycles));
414
stream.read(reinterpret_cast<char*>(&m_iClockRate), sizeof(m_iClockRate));
415
stream.read(reinterpret_cast<char*>(&m_CurrentSample), sizeof(m_CurrentSample));
416
}
417
418