Skip to content

Commit

Permalink
Core\APU: emulate PSG DAC enable/disable (fixes #64).
Browse files Browse the repository at this point in the history
  • Loading branch information
fleroviux committed Jan 31, 2021
1 parent db1c3c4 commit 645700c
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 10 deletions.
18 changes: 15 additions & 3 deletions source/emulator/core/hw/apu/channel/channel_noise.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ void NoiseChannel::Reset() {
frequency_ratio = 0;
width = 0;
length_enable = false;
dac_enable = false;
enabled = false;

lfsr = 0;
sample = 0;
Expand All @@ -34,6 +36,9 @@ void NoiseChannel::Reset() {

void NoiseChannel::Generate(int cycles_late) {
if (length_enable && sequencer.length <= 0) {
// TODO: enabled should be unset immediately when length becomes zero.
// Updating it here is too late.
enabled = false;
sample = 0;
scheduler.Add(GetSynthesisInterval(7, 15) - cycles_late, event_cb);
return;
Expand All @@ -53,6 +58,8 @@ void NoiseChannel::Generate(int cycles_late) {

sample *= sequencer.envelope.current_volume;

if (!dac_enable) sample = 0;

/* Skip samples that will never be sampled by the audio mixer. */
for (int i = 0; i < skip_count; i++) {
carry = lfsr & 1;
Expand Down Expand Up @@ -122,6 +129,10 @@ void NoiseChannel::Write(int offset, std::uint8_t value) {
envelope.divider = value & 7;
envelope.direction = Envelope::Direction((value >> 3) & 1);
envelope.initial_volume = value >> 4;
dac_enable = (value >> 3) != 0;
if (!dac_enable) {
enabled = false;
}
break;
}

Expand All @@ -136,9 +147,10 @@ void NoiseChannel::Write(int offset, std::uint8_t value) {
length_enable = value & 0x40;

if (value & 0x80) {
/* TODO: are these the correct initialization values? */
const std::uint16_t lfsr_init[] = { 0x4000, 0x0040 };

constexpr std::uint16_t lfsr_init[] = { 0x4000, 0x0040 };
if (dac_enable) {
enabled = true;
}
sequencer.Restart();
lfsr = lfsr_init[width];
}
Expand Down
3 changes: 3 additions & 0 deletions source/emulator/core/hw/apu/channel/channel_noise.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class NoiseChannel {
NoiseChannel(Scheduler& scheduler, BIAS& bias);

void Reset();
bool IsEnabled() { return enabled; }

void Generate(int cycles_late);
auto Read (int offset) -> std::uint8_t;
Expand Down Expand Up @@ -53,6 +54,8 @@ class NoiseChannel {
int frequency_ratio;
int width;
bool length_enable;
bool dac_enable;
bool enabled;

BIAS& bias;
int skip_count;
Expand Down
19 changes: 18 additions & 1 deletion source/emulator/core/hw/apu/channel/channel_quad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,17 @@ void QuadChannel::Reset() {
sample = 0;
wave_duty = 0;
length_enable = false;
dac_enable = false;
enabled = false;

scheduler.Add(GetSynthesisIntervalFromFrequency(0), event_cb);
}

void QuadChannel::Generate(int cycles_late) {
if ((length_enable && sequencer.length <= 0) || sequencer.sweep.channel_disabled) {
// TODO: enabled should be unset immediately when length becomes zero.
// Updating it here is too late.
enabled = false;
sample = 0;
scheduler.Add(GetSynthesisIntervalFromFrequency(0) - cycles_late, event_cb);
return;
Expand All @@ -39,7 +45,11 @@ void QuadChannel::Generate(int cycles_late) {
{ +8, +8, +8, +8, +8, +8, -8, -8 }
};

sample = std::int8_t(pattern[wave_duty][phase] * sequencer.envelope.current_volume);
if (dac_enable) {
sample = std::int8_t(pattern[wave_duty][phase] * sequencer.envelope.current_volume);
} else {
sample = 0;
}
phase = (phase + 1) % 8;

scheduler.Add(GetSynthesisIntervalFromFrequency(sequencer.sweep.current_freq) - cycles_late, event_cb);
Expand Down Expand Up @@ -102,6 +112,10 @@ void QuadChannel::Write(int offset, std::uint8_t value) {
envelope.divider = value & 7;
envelope.direction = Envelope::Direction((value >> 3) & 1);
envelope.initial_volume = value >> 4;
dac_enable = (value >> 3) != 0;
if (!dac_enable) {
enabled = false;
}
break;
}

Expand All @@ -117,6 +131,9 @@ void QuadChannel::Write(int offset, std::uint8_t value) {
length_enable = value & 0x40;

if (value & 0x80) {
if (dac_enable) {
enabled = true;
}
phase = 0;
sequencer.Restart();
}
Expand Down
10 changes: 7 additions & 3 deletions source/emulator/core/hw/apu/channel/channel_quad.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class QuadChannel {
QuadChannel(Scheduler& scheduler);

void Reset();
bool IsEnabled() { return enabled; }

void Generate(int cycles_late);
auto Read (int offset) -> std::uint8_t;
Expand All @@ -37,12 +38,15 @@ class QuadChannel {
}

Scheduler& scheduler;
int phase;
int wave_duty;
bool length_enable;
std::function<void(int)> event_cb = [this](int cycles_late) {
this->Generate(cycles_late);
};

int phase;
int wave_duty;
bool length_enable;
bool dac_enable;
bool enabled;
};

} // namespace nba::core
4 changes: 2 additions & 2 deletions source/emulator/core/hw/apu/channel/channel_wave.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class WaveChannel {
WaveChannel(Scheduler& scheduler);

void Reset();
bool IsEnabled() { return enabled; }

void Generate(int cycles_late);
auto Read (int offset) -> std::uint8_t;
Expand All @@ -42,12 +43,11 @@ class WaveChannel {
return 8 * (2048 - frequency);
}

Scheduler& scheduler;
std::function<void(int)> event_cb = [this](int cycles_late) {
this->Generate(cycles_late);
};

Scheduler& scheduler;

bool enabled;
bool force_volume;
int volume;
Expand Down
3 changes: 2 additions & 1 deletion source/emulator/core/hw/apu/registers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ auto SoundControl::Read(int address) -> std::uint8_t {
(dma[DMA_B].enable[SIDE_LEFT ] ? 32 : 0) |
(dma[DMA_B].timer_id ? 64 : 0);
case 4:
/* FIXME: emulate bits 0-3 PSG ON/OFF state. */
// FIXME: emulate bits 0-3 PSG ON/OFF state.
return 0b1111 | (master_enable ? 128 : 0);

default: return 0;
Expand Down Expand Up @@ -99,6 +99,7 @@ void SoundControl::Write(int address, std::uint8_t value) {

break;
case 4:
// TODO: reset PSG registers to zero on disable.
master_enable = value & 128;
break;
}
Expand Down

0 comments on commit 645700c

Please sign in to comment.