openMSX
SN76489.cc
Go to the documentation of this file.
1 #include "SN76489.hh"
2 #include "DeviceConfig.hh"
3 #include "Math.hh"
4 #include "outer.hh"
5 #include "serialize.hh"
6 #include "unreachable.hh"
7 #include <algorithm>
8 #include <cassert>
9 #include <cmath>
10 
11 using std::string;
12 
13 namespace openmsx {
14 
15 // The SN76489 divides the clock input by 8, but all users of the clock apply
16 // another divider of 2.
17 static const float NATIVE_FREQ_FLOAT = (3579545.0f / 8) / 2;
18 static const int NATIVE_FREQ_INT = lrintf(NATIVE_FREQ_FLOAT);
19 
20 // NoiseShifter:
21 
22 inline void SN76489::NoiseShifter::initState(unsigned pattern_,
23  unsigned period_)
24 {
25  pattern = pattern_;
26  period = period_;
27  stepsBehind = 0;
28 
29  // Start with only the top bit set.
30  unsigned allOnes = Math::floodRight(pattern_);
31  random = allOnes - (allOnes >> 1);
32 }
33 
34 inline unsigned SN76489::NoiseShifter::getOutput() const
35 {
36  return ~random & 1;
37 }
38 
40 {
41  random = (random >> 1) ^ (-(random & 1) & pattern);
42 }
43 
44 inline void SN76489::NoiseShifter::queueAdvance(unsigned steps)
45 {
46  stepsBehind += steps;
47  stepsBehind %= period;
48 }
49 
50 void SN76489::NoiseShifter::catchUp()
51 {
52  for (; stepsBehind; stepsBehind--) {
53  advance();
54  }
55 }
56 
57 template<typename Archive>
58 void SN76489::NoiseShifter::serialize(Archive& ar, unsigned /*version*/)
59 {
60  // Make sure there are no queued steps, so we don't have to serialize them.
61  // If we're loading, initState() already set stepsBehind to 0.
62  if (!ar.isLoader()) {
63  catchUp();
64  }
65  assert(stepsBehind == 0);
66 
67  // Don't serialize the pattern and noise period; they are initialized by
68  // the chip class.
69  ar.serialize("random", random);
70 }
71 
72 // Main class:
73 
75  : ResampledSoundDevice(config.getMotherBoard(), "SN76489", "DCSG", 4)
76  , debuggable(config.getMotherBoard(), getName())
77 {
78  setInputRate(NATIVE_FREQ_INT);
79 
80  initVolumeTable(32768);
81  initState();
82 
83  registerSound(config);
84 }
85 
87 {
89 }
90 
91 void SN76489::initVolumeTable(int volume)
92 {
93  float out = volume;
94  // 2dB per step -> 0.2f, sqrt for amplitude -> 0.5f
95  float factor = powf(0.1f, 0.2f * 0.5f);
96  for (int i = 0; i < 15; i++) {
97  volTable[i] = lrintf(out);
98  out *= factor;
99  }
100  volTable[15] = 0;
101 }
102 
103 void SN76489::initState()
104 {
105  registerLatch = 0; // TODO: 3 for Sega.
106 
107  // The Sega integrated versions start zeroed (max volume), while discrete
108  // chips seem to start with random values (for lack of a reset pin).
109  // For the user's comfort, we init to silence instead.
110  for (unsigned chan = 0; chan < 4; chan++) {
111  regs[chan * 2] = 0;
112  regs[chan * 2 + 1] = 0xF;
113  counters[chan] = 0;
114  outputs[chan] = 0;
115  }
116 
117  initNoise();
118 }
119 
120 void SN76489::initNoise()
121 {
122  // Note: These are the noise patterns for the SN76489A.
123  // Other chip variants have different noise patterns.
124  unsigned pattern, period;
125  if (regs[6] & 0x4) {
126  // "White" noise: pseudo-random bit sequence.
127  pattern = 0x6000;
128  period = (1 << 15) - 1;
129  } else {
130  // "Periodic" noise: produces a square wave with a short duty cycle.
131  period = 15;
132  pattern = 1 << (period - 1);
133  }
134  noiseShifter.initState(pattern, period);
135 }
136 
137 void SN76489::reset(EmuTime::param time)
138 {
139  updateStream(time);
140  initState();
141 }
142 
143 void SN76489::write(byte value, EmuTime::param time)
144 {
145  if (value & 0x80) {
146  registerLatch = (value & 0x70) >> 4;
147  }
148  auto& reg = regs[registerLatch];
149 
150  word data;
151  switch (registerLatch) {
152  case 0:
153  case 2:
154  case 4:
155  // Tone period.
156  if (value & 0x80) {
157  data = (reg & 0x3F0) | (value & 0x0F);
158  } else {
159  data = (reg & 0x00F) | ((value & 0x3F) << 4);
160  }
161  break;
162  case 6:
163  // Noise control.
164  data = value & 0x07;
165  break;
166  case 1:
167  case 3:
168  case 5:
169  case 7:
170  // Attenuation.
171  data = value & 0x0F;
172  break;
173  default:
174  UNREACHABLE;
175  }
176 
177  // TODO: Warn about access while chip is not ready.
178 
179  writeRegister(registerLatch, data, time);
180 }
181 
182 word SN76489::peekRegister(unsigned reg, EmuTime::param /*time*/) const
183 {
184  // Note: None of the register values will change unless a register is
185  // written, so we don't need to sync here.
186  return regs[reg];
187 }
188 
189 void SN76489::writeRegister(unsigned reg, word value, EmuTime::param time)
190 {
191  if (reg == 6 || regs[reg] != value) {
192  updateStream(time);
193  regs[reg] = value;
194  if (reg == 6) {
195  // Every write to register 6 resets the noise shift register.
196  initNoise();
197  }
198  }
199 }
200 
201 /*
202  * Implementation note for noise mode 3:
203  *
204  * In this mode, the shift register is advanced when the tone generator #3
205  * output flips to 1. We emulate this by synthesizing the noise channel using
206  * that generator before synthesizing its tone channel, but without committing
207  * the updated state. This ensures that the noise channel and the third tone
208  * channel are in phase, but do end up in their own separate mixing buffers.
209  */
210 
211 template <bool NOISE> void SN76489::synthesizeChannel(
212  float*& buffer, unsigned num, unsigned generator)
213 {
214  unsigned period;
215  if (generator == 3) {
216  // Noise channel using its own generator.
217  period = 16 << (regs[6] & 3);
218  } else {
219  // Tone or noise channel using a tone generator.
220  period = regs[2 * generator];
221  if (period == 0) {
222  // TODO: Sega variants have a non-flipping output for period 0.
223  period = 0x400;
224  }
225  }
226 
227  auto output = outputs[generator];
228  unsigned counter = counters[generator];
229 
230  unsigned channel = NOISE ? 3 : generator;
231  int volume = volTable[regs[2 * channel + 1]];
232  if (volume == 0) {
233  // Channel is silent, don't synthesize it.
234  buffer = nullptr;
235  }
236  if (buffer) {
237  // Synthesize channel.
238  if (NOISE) {
239  noiseShifter.catchUp();
240  }
241  auto* buf = buffer;
242  unsigned remaining = num;
243  while (remaining != 0) {
244  if (counter == 0) {
245  output ^= 1;
246  counter = period;
247  if (NOISE && output) {
248  noiseShifter.advance();
249  }
250  }
251  unsigned ticks = std::min(counter, remaining);
252  if (NOISE ? noiseShifter.getOutput() : output) {
253  addFill(buf, volume, ticks);
254  } else {
255  buf += ticks;
256  }
257  counter -= ticks;
258  remaining -= ticks;
259  }
260  } else {
261  // Advance state without synthesis.
262  if (counter >= num) {
263  counter -= num;
264  } else {
265  unsigned remaining = num - counter;
266  output ^= 1; // partial cycle
267  unsigned cycles = (remaining - 1) / period;
268  if (NOISE) {
269  noiseShifter.queueAdvance((cycles + output) / 2);
270  }
271  output ^= cycles & 1; // full cycles
272  remaining -= cycles * period;
273  counter = period - remaining;
274  }
275  }
276 
277  if (!NOISE || generator == 3) {
278  outputs[generator] = output;
279  counters[generator] = counter;
280  }
281 }
282 
283 void SN76489::generateChannels(float** buffers, unsigned num)
284 {
285  // Channel 3: noise.
286  if ((regs[6] & 3) == 3) {
287  // Use the tone generator #3 (channel 2) output.
288  synthesizeChannel<true>(buffers[3], num, 2);
289  // Assume the noise phase counter and output bit keep updating even
290  // if they are currently not driving the noise shift register.
291  float* noBuffer = nullptr;
292  synthesizeChannel<false>(noBuffer, num, 3);
293  } else {
294  // Use the channel 3 generator output.
295  synthesizeChannel<true>(buffers[3], num, 3);
296  }
297 
298  // Channels 0, 1, 2: tone.
299  for (unsigned channel = 0; channel < 3; channel++) {
300  synthesizeChannel<false>(buffers[channel], num, channel);
301  }
302 }
303 
304 template<typename Archive>
305 void SN76489::serialize(Archive& ar, unsigned version)
306 {
307  // Don't serialize volTable since it holds computed constants, not state.
308 
309  ar.serialize("regs", regs);
310  ar.serialize("registerLatch", registerLatch);
311  ar.serialize("counters", counters);
312  ar.serialize("outputs", outputs);
313 
314  if (ar.isLoader()) {
315  // Initialize the computed NoiseShifter members, based on the
316  // register contents we just loaded, before we load the shift
317  // register state.
318  initNoise();
319  }
320  // Serialize the noise shift register state as part of the chip: there is
321  // no need to reflect our class structure in the serialization.
322  noiseShifter.serialize(ar, version);
323 }
325 
326 // Debuggable
327 
328 // The frequency registers are 10 bits wide, so we have to split them over
329 // two debuggable entries.
330 static const byte SN76489_DEBUG_MAP[][2] = {
331  {0, 0}, {0, 1}, {1, 0},
332  {2, 0}, {2, 1}, {3, 0},
333  {4, 0}, {4, 1}, {5, 0},
334  {6, 0}, {7, 0}
335 };
336 
337 SN76489::Debuggable::Debuggable(MSXMotherBoard& motherBoard_, const string& name_)
339  motherBoard_, name_ + " regs",
340  "SN76489 regs - note the period regs are split over two entries", 11)
341 {
342 }
343 
344 byte SN76489::Debuggable::read(unsigned address, EmuTime::param time)
345 {
346  byte reg = SN76489_DEBUG_MAP[address][0];
347  byte hi = SN76489_DEBUG_MAP[address][1];
348 
349  auto& sn76489 = OUTER(SN76489, debuggable);
350  word data = sn76489.peekRegister(reg, time);
351  return hi ? (data >> 4) : (data & 0xF);
352 }
353 
354 void SN76489::Debuggable::write(unsigned address, byte value, EmuTime::param time)
355 {
356  byte reg = SN76489_DEBUG_MAP[address][0];
357  byte hi = SN76489_DEBUG_MAP[address][1];
358 
359  auto& sn76489 = OUTER(SN76489, debuggable);
360  word data;
361  if (reg == 0 || reg == 2 || reg == 4) {
362  data = sn76489.peekRegister(reg, time);
363  if (hi) {
364  data = ((value & 0x3F) << 4) | (data & 0x0F);
365  } else {
366  data = (data & 0x3F0) | (value & 0x0F);
367  }
368  } else {
369  data = value & 0x0F;
370  }
371  sn76489.writeRegister(reg, data, time);
372 }
373 
374 } // namespace openmsx
static void addFill(float *&buffer, float value, unsigned num)
Adds a number of samples that all have the same value.
Definition: SoundDevice.cc:45
vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:269
void unregisterSound()
Unregisters this sound device with the Mixer.
Definition: SoundDevice.cc:130
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
void reset(EmuTime::param time)
Definition: SN76489.cc:137
void advance(octet_iterator &it, distance_type n, octet_iterator end)
void updateStream(EmuTime::param time)
Definition: SoundDevice.cc:135
constexpr auto data(C &c) -> decltype(c.data())
Definition: span.hh:69
void setInputRate(unsigned sampleRate)
Definition: SoundDevice.hh:108
This class implements the Texas Instruments SN76489 sound chip.
Definition: SN76489.hh:24
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
void serialize(Archive &ar, unsigned version)
Definition: SN76489.cc:305
void write(byte value, EmuTime::param time)
Definition: SN76489.cc:143
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:840
string getName(KeyCode keyCode)
Translate key code to key name.
Definition: Keys.cc:589
void generateChannels(float **buffers, unsigned num) override
Abstract method to generate the actual sound data.
Definition: SN76489.cc:283
#define OUTER(type, member)
Definition: outer.hh:38
SN76489(const DeviceConfig &config)
Definition: SN76489.cc:74
void serialize(Archive &ar, T &t, unsigned version)
T floodRight(T x)
Returns the smallest number of the form 2^n-1 that is greater or equal to the given number...
Definition: Math.hh:185
void registerSound(const DeviceConfig &config)
Registers this sound device with the Mixer.
Definition: SoundDevice.cc:89
#define UNREACHABLE
Definition: unreachable.hh:38