25static constexpr auto NATIVE_FREQ_INT = unsigned(
cstd::round((3579545.0 / 8) / 2));
27static constexpr auto volTable = [] {
28 std::array<float, 16> result = {};
30 double factor = cstd::pow<5, 3>(0.1, 0.2 * 0.5);
32 for (
auto i :
xrange(15)) {
43inline void SN76489::NoiseShifter::initState(
unsigned pattern_,
52 random = allOnes - (allOnes >> 1);
55inline unsigned SN76489::NoiseShifter::getOutput()
const
60inline void SN76489::NoiseShifter::advance()
62 random = (random >> 1) ^ ((random & 1) ? pattern : 0);
65inline void SN76489::NoiseShifter::queueAdvance(
unsigned steps)
68 stepsBehind %= period;
71void SN76489::NoiseShifter::catchUp()
73 for (; stepsBehind; stepsBehind--) {
78template<
typename Archive>
79void SN76489::NoiseShifter::serialize(Archive& ar,
unsigned )
83 if constexpr (!Archive::IS_LOADER) {
86 assert(stepsBehind == 0);
90 ar.serialize(
"random", random);
97 , debuggable(config.getMotherBoard(), getName())
100 std::cout <<
"volTable:";
101 for (
const auto& e : volTable) std::cout <<
' ' << e;
114void SN76489::initState()
121 for (
auto chan :
xrange(4)) {
122 regs[chan * 2 + 0] = 0x0;
123 regs[chan * 2 + 1] = 0xF;
131void SN76489::initNoise()
135 unsigned period = (regs[6] & 0x4)
138 unsigned pattern = (regs[6] & 0x4)
140 : (1 << (period - 1));
141 noiseShifter.initState(pattern, period);
153 registerLatch = (value & 0x70) >> 4;
155 const auto& reg = regs[registerLatch];
157 auto data = [&]() ->
word {
158 switch (registerLatch) {
164 return word((reg & 0x3F0) | ((value & 0x0F) << 0));
166 return word((reg & 0x00F) | ((value & 0x3F) << 4));
184 writeRegister(registerLatch, data, time);
187word SN76489::peekRegister(
unsigned reg, EmuTime::param )
const
194void SN76489::writeRegister(
unsigned reg,
word value, EmuTime::param time)
196 if (reg == 6 || regs[reg] != value) {
216template<
bool NOISE>
void SN76489::synthesizeChannel(
217 float*& buffer,
unsigned num,
unsigned generator)
219 unsigned period = [&] {
220 if (generator == 3) {
222 return unsigned(16 << (regs[6] & 3));
225 unsigned p = regs[2 * generator];
234 auto output = outputs[generator];
235 unsigned counter = counters[generator];
237 unsigned channel = NOISE ? 3 : generator;
238 auto volume = volTable[regs[2 * channel + 1]];
239 if (volume == 0.0f) {
245 if constexpr (NOISE) {
246 noiseShifter.catchUp();
249 unsigned remaining = num;
250 while (remaining != 0) {
254 if (NOISE && output) {
255 noiseShifter.advance();
258 unsigned ticks = std::min(counter, remaining);
259 if (NOISE ? noiseShifter.getOutput() : output) {
269 if (counter >= num) {
272 unsigned remaining = num - counter;
274 unsigned cycles = (remaining - 1) / period;
275 if constexpr (NOISE) {
276 noiseShifter.queueAdvance((cycles + output) / 2);
278 output ^= cycles & 1;
279 remaining -= cycles * period;
280 counter = period - remaining;
284 if (!NOISE || generator == 3) {
285 outputs[generator] = output;
286 counters[generator] = narrow_cast<word>(counter);
293 if ((regs[6] & 3) == 3) {
295 synthesizeChannel<true>(buffers[3], num, 2);
298 float* noBuffer =
nullptr;
299 synthesizeChannel<false>(noBuffer, num, 3);
302 synthesizeChannel<true>(buffers[3], num, 3);
306 for (
auto channel :
xrange(3)) {
307 synthesizeChannel<false>(buffers[channel], num, channel);
311template<
typename Archive>
314 ar.serialize(
"regs", regs,
315 "registerLatch", registerLatch,
316 "counters", counters,
319 if constexpr (Archive::IS_LOADER) {
327 noiseShifter.serialize(ar, version);
335static constexpr std::array SN76489_DEBUG_MAP = {
336 std::array<byte, 2>{0, 0},
337 std::array<byte, 2>{0, 1},
338 std::array<byte, 2>{1, 0},
339 std::array<byte, 2>{2, 0},
340 std::array<byte, 2>{2, 1},
341 std::array<byte, 2>{3, 0},
342 std::array<byte, 2>{4, 0},
343 std::array<byte, 2>{4, 1},
344 std::array<byte, 2>{5, 0},
345 std::array<byte, 2>{6, 0},
346 std::array<byte, 2>{7, 0},
351 motherBoard_, name_ +
" regs",
352 "SN76489 regs - note the period regs are split over two entries", 11)
356byte SN76489::Debuggable::read(
unsigned address, EmuTime::param time)
358 auto [reg, hi] = SN76489_DEBUG_MAP[address];
360 const auto& sn76489 =
OUTER(SN76489, debuggable);
361 word data = sn76489.peekRegister(reg, time);
362 return hi ? narrow_cast<byte>(data >> 4)
366void SN76489::Debuggable::write(
unsigned address,
byte value, EmuTime::param time)
368 auto reg = SN76489_DEBUG_MAP[address][0];
369 auto hi = SN76489_DEBUG_MAP[address][1];
371 auto& sn76489 =
OUTER(SN76489, debuggable);
372 auto data = [&]() -> word {
373 if (reg ==
one_of(0, 2, 4)) {
374 word d = sn76489.peekRegister(reg, time);
376 return word(((value & 0x3F) << 4) | (d & 0x0F));
378 return word((d & 0x3F0) | (value & 0x0F));
384 sn76489.writeRegister(reg, data, time);
This class implements the Texas Instruments SN76489 sound chip.
void serialize(Archive &ar, unsigned version)
void reset(EmuTime::param time)
void write(byte value, EmuTime::param time)
SN76489(const DeviceConfig &config)
void generateChannels(std::span< float * > buffers, unsigned num) override
Abstract method to generate the actual sound data.
void updateStream(EmuTime::param time)
static void addFill(float *&buffer, float value, unsigned num)
Adds a number of samples that all have the same value.
void unregisterSound()
Unregisters this sound device with the Mixer.
void registerSound(const DeviceConfig &config)
Registers this sound device with the Mixer.
constexpr auto floodRight(std::unsigned_integral auto x) noexcept
Returns the smallest number of the form 2^n-1 that is greater or equal to the given number.
constexpr double round(double x)
This file implemented 3 utility functions:
uint8_t byte
8 bit unsigned integer
uint16_t word
16 bit unsigned integer
constexpr void fill(ForwardRange &&range, const T &value)
void advance(octet_iterator &it, distance_type n, octet_iterator end)
constexpr To narrow_cast(From &&from) noexcept
#define OUTER(type, member)
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
constexpr auto xrange(T e)