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