22static constexpr auto NATIVE_FREQ_INT = unsigned(
cstd::round((3579545.0 / 8) / 2));
24static constexpr auto volTable = [] {
25 std::array<float, 16> result = {};
27 double factor = cstd::pow<5, 3>(0.1, 0.2 * 0.5);
29 for (
auto i :
xrange(15)) {
40inline void SN76489::NoiseShifter::initState(
unsigned pattern_,
49 random = allOnes - (allOnes >> 1);
52inline unsigned SN76489::NoiseShifter::getOutput()
const
59 random = (random >> 1) ^ ((random & 1) ? pattern : 0);
62inline void SN76489::NoiseShifter::queueAdvance(
unsigned steps)
65 stepsBehind %= period;
68void SN76489::NoiseShifter::catchUp()
70 for (; stepsBehind; stepsBehind--) {
75template<
typename Archive>
80 if constexpr (!Archive::IS_LOADER) {
83 assert(stepsBehind == 0);
87 ar.serialize(
"random", random);
94 , debuggable(config.getMotherBoard(), getName())
97 std::cout <<
"volTable:";
98 for (
const auto&
e : volTable) std::cout <<
' ' <<
e;
111void SN76489::initState()
118 for (
auto chan :
xrange(4)) {
119 regs[chan * 2 + 0] = 0x0;
120 regs[chan * 2 + 1] = 0xF;
128void SN76489::initNoise()
132 unsigned period = (regs[6] & 0x4)
135 unsigned pattern = (regs[6] & 0x4)
137 : (1 << (period - 1));
138 noiseShifter.initState(pattern, period);
150 registerLatch = (value & 0x70) >> 4;
152 auto& reg = regs[registerLatch];
154 auto data = [&]() ->
word {
155 switch (registerLatch) {
161 return word((reg & 0x3F0) | ((value & 0x0F) << 0));
163 return word((reg & 0x00F) | ((value & 0x3F) << 4));
181 writeRegister(registerLatch, data, time);
184word SN76489::peekRegister(
unsigned reg, EmuTime::param )
const
191void SN76489::writeRegister(
unsigned reg,
word value, EmuTime::param time)
193 if (reg == 6 || regs[reg] != value) {
213template<
bool NOISE>
void SN76489::synthesizeChannel(
214 float*& buffer,
unsigned num,
unsigned generator)
216 unsigned period = [&] {
217 if (generator == 3) {
219 return unsigned(16 << (regs[6] & 3));
222 unsigned p = regs[2 * generator];
231 auto output = outputs[generator];
232 unsigned counter = counters[generator];
234 unsigned channel = NOISE ? 3 : generator;
235 auto volume = volTable[regs[2 * channel + 1]];
236 if (volume == 0.0f) {
242 if constexpr (NOISE) {
243 noiseShifter.catchUp();
246 unsigned remaining = num;
247 while (remaining != 0) {
251 if (NOISE && output) {
252 noiseShifter.advance();
255 unsigned ticks =
std::min(counter, remaining);
256 if (NOISE ? noiseShifter.getOutput() : output) {
266 if (counter >= num) {
269 unsigned remaining = num - counter;
271 unsigned cycles = (remaining - 1) / period;
272 if constexpr (NOISE) {
273 noiseShifter.queueAdvance((cycles + output) / 2);
275 output ^= cycles & 1;
276 remaining -= cycles * period;
277 counter = period - remaining;
281 if (!NOISE || generator == 3) {
282 outputs[generator] = output;
283 counters[generator] = narrow_cast<word>(counter);
290 if ((regs[6] & 3) == 3) {
292 synthesizeChannel<true>(buffers[3], num, 2);
295 float* noBuffer =
nullptr;
296 synthesizeChannel<false>(noBuffer, num, 3);
299 synthesizeChannel<true>(buffers[3], num, 3);
303 for (
auto channel :
xrange(3)) {
304 synthesizeChannel<false>(buffers[channel], num, channel);
308template<
typename Archive>
311 ar.serialize(
"regs", regs,
312 "registerLatch", registerLatch,
313 "counters", counters,
316 if constexpr (Archive::IS_LOADER) {
324 noiseShifter.serialize(ar, version);
332static constexpr std::array SN76489_DEBUG_MAP = {
333 std::array<byte, 2>{0, 0},
334 std::array<byte, 2>{0, 1},
335 std::array<byte, 2>{1, 0},
336 std::array<byte, 2>{2, 0},
337 std::array<byte, 2>{2, 1},
338 std::array<byte, 2>{3, 0},
339 std::array<byte, 2>{4, 0},
340 std::array<byte, 2>{4, 1},
341 std::array<byte, 2>{5, 0},
342 std::array<byte, 2>{6, 0},
343 std::array<byte, 2>{7, 0},
346SN76489::Debuggable::Debuggable(MSXMotherBoard& motherBoard_,
const std::string& name_)
348 motherBoard_, name_ +
" regs",
349 "SN76489 regs - note the period regs are split over two entries", 11)
353byte SN76489::Debuggable::read(
unsigned address, EmuTime::param time)
355 auto [reg, hi] = SN76489_DEBUG_MAP[address];
358 word data = sn76489.peekRegister(reg, time);
359 return hi ? narrow_cast<byte>(data >> 4)
363void SN76489::Debuggable::write(
unsigned address,
byte value, EmuTime::param time)
365 auto reg = SN76489_DEBUG_MAP[address][0];
366 auto hi = SN76489_DEBUG_MAP[address][1];
369 auto data = [&]() ->
word {
370 if (reg ==
one_of(0, 2, 4)) {
371 word d = sn76489.peekRegister(reg, time);
373 return word(((value & 0x3F) << 4) | (d & 0x0F));
375 return word((d & 0x3F0) | (value & 0x0F));
381 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)
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
This file implemented 3 utility functions:
uint8_t byte
8 bit unsigned integer
uint16_t word
16 bit unsigned integer
void serialize(Archive &ar, T &t, unsigned version)
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)