mirror of
https://github.com/SourMesen/Mesen2.git
synced 2025-04-02 10:21:44 -04:00
-Super Mario Land 2 - Pops in menu are fixed by immediately updating APU output after a write -Perfect Dark - Low voice volume is fixed by having the correct output when the channels are disabled (but DAC is still enabled) -Daiku no Gen - Low voice volume is fixed by keeping square channel output to digital 0 (= analog 1) until its first tick after being enabled (the game does not let the channel tick at all while the voice sample is playing)
226 lines
5.3 KiB
C++
226 lines
5.3 KiB
C++
#include "pch.h"
|
|
#include "GBA/APU/GbaWaveChannel.h"
|
|
#include "GBA/APU/GbaApu.h"
|
|
#include "GBA/GbaConsole.h"
|
|
#include "Shared/Emulator.h"
|
|
#include "Shared/EmuSettings.h"
|
|
|
|
GbaWaveChannel::GbaWaveChannel(GbaApu* apu, GbaConsole* console)
|
|
{
|
|
_console = console;
|
|
_apu = apu;
|
|
|
|
_console->InitializeRam(_state.Ram, 0x20);
|
|
}
|
|
|
|
GbaWaveState& GbaWaveChannel::GetState()
|
|
{
|
|
return _state;
|
|
}
|
|
|
|
bool GbaWaveChannel::Enabled()
|
|
{
|
|
return _state.Enabled;
|
|
}
|
|
|
|
void GbaWaveChannel::Disable()
|
|
{
|
|
uint16_t len = _state.Length;
|
|
uint8_t ram[0x10];
|
|
memcpy(ram, _state.Ram, sizeof(ram));
|
|
_state = {};
|
|
_state.Length = len;
|
|
memcpy(_state.Ram, ram, sizeof(ram));
|
|
_state.Timer = 2048 * 2;
|
|
}
|
|
|
|
void GbaWaveChannel::ResetLengthCounter()
|
|
{
|
|
_state.Length = 0;
|
|
}
|
|
|
|
void GbaWaveChannel::ClockLengthCounter()
|
|
{
|
|
if(_state.LengthEnabled && _state.Length > 0) {
|
|
_state.Length--;
|
|
if(_state.Length == 0) {
|
|
//"Length becoming 0 should clear status"
|
|
_state.Enabled = false;
|
|
_apu->UpdateEnabledChannels();
|
|
_state.Output = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
uint8_t GbaWaveChannel::GetRawOutput()
|
|
{
|
|
//"Stopping channel 3 manually using the NR30 register affects PCM34 instantly" (SameSuite's channel_3_stop_delay test)
|
|
return _state.Enabled ? _state.Output : 0;
|
|
}
|
|
|
|
double GbaWaveChannel::GetOutput()
|
|
{
|
|
return _state.Output;
|
|
}
|
|
|
|
void GbaWaveChannel::UpdateOutput()
|
|
{
|
|
if(!_state.Enabled) {
|
|
return;
|
|
}
|
|
|
|
if(_state.OverrideVolume) {
|
|
_state.Output = _state.SampleBuffer * 3 / 4;
|
|
} else if(_state.Volume) {
|
|
_state.Output = _state.SampleBuffer >> (_state.Volume - 1);
|
|
} else {
|
|
_state.Output = 0;
|
|
}
|
|
}
|
|
|
|
void GbaWaveChannel::Exec(uint32_t clocksToRun)
|
|
{
|
|
if(!_state.Enabled) {
|
|
return;
|
|
}
|
|
|
|
bool needUpdate = false;
|
|
do {
|
|
uint32_t minTimer = std::min<uint32_t>(clocksToRun, _state.Timer);
|
|
_state.Timer -= minTimer;
|
|
|
|
if(_state.Timer == 0) {
|
|
//The wave channel's frequency timer period is set to (2048-frequency)*2.
|
|
_state.Timer = (2048 - _state.Frequency) * 2;
|
|
|
|
//When the timer generates a clock, the position counter is advanced one sample in the wave table,
|
|
//looping back to the beginning when it goes past the end,
|
|
_state.Position = (_state.Position + 1) & (_state.DoubleLength ? 0x3F : 0x1F);
|
|
uint8_t bank = _state.DoubleLength ? 0 : (_state.SelectedBank << 4);
|
|
|
|
//then a sample is read into the sample buffer from this NEW position.
|
|
if(_state.Position & 0x01) {
|
|
_state.SampleBuffer = _state.Ram[bank | (_state.Position >> 1)] & 0x0F;
|
|
} else {
|
|
_state.SampleBuffer = _state.Ram[bank | (_state.Position >> 1)] >> 4;
|
|
}
|
|
|
|
//The DAC receives the current value from the upper/lower nibble of the sample buffer, shifted right by the volume control.
|
|
needUpdate = true;
|
|
}
|
|
clocksToRun -= minTimer;
|
|
} while(clocksToRun);
|
|
|
|
if(needUpdate) {
|
|
UpdateOutput();
|
|
}
|
|
}
|
|
|
|
uint8_t GbaWaveChannel::Read(uint16_t addr)
|
|
{
|
|
switch(addr) {
|
|
case 0: return (
|
|
(_state.DacEnabled ? 0x80 : 0) |
|
|
(_state.SelectedBank ? 0x40 : 0) |
|
|
(_state.DoubleLength ? 0x20 : 0)
|
|
);
|
|
|
|
case 2: return (
|
|
(_state.OverrideVolume ? 0x80 : 0) |
|
|
(_state.Volume << 5)
|
|
);
|
|
|
|
case 4: return _state.LengthEnabled ? 0x40 : 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void GbaWaveChannel::Write(uint16_t addr, uint8_t value)
|
|
{
|
|
switch(addr) {
|
|
case 0:
|
|
_state.DacEnabled = value & 0x80;
|
|
_state.SelectedBank = (value & 0x40) >> 6;
|
|
_state.DoubleLength = value & 0x20;
|
|
_state.Enabled &= _state.DacEnabled;
|
|
_apu->UpdateEnabledChannels();
|
|
break;
|
|
|
|
case 1:
|
|
_state.Length = 256 - value;
|
|
break;
|
|
|
|
case 2:
|
|
_state.Volume = (value & 0x60) >> 5;
|
|
_state.OverrideVolume = value & 0x80;
|
|
|
|
//"Modifying the channel 3 shift while the channel is playing affects PCM34 instantly" (SameSuite's channel_3_shift_delay test)
|
|
UpdateOutput();
|
|
break;
|
|
|
|
case 3:
|
|
_state.Frequency = (_state.Frequency & 0x700) | value;
|
|
break;
|
|
|
|
case 4: {
|
|
bool prevEnabled = _state.Enabled;
|
|
_state.Frequency = (_state.Frequency & 0xFF) | ((value & 0x07) << 8);
|
|
|
|
if(value & 0x80) {
|
|
//Start playback
|
|
//Channel is enabled, if DAC is enabled
|
|
_state.Enabled = _state.DacEnabled;
|
|
_apu->UpdateEnabledChannels();
|
|
|
|
if(_state.Enabled) {
|
|
UpdateOutput();
|
|
}
|
|
|
|
//Frequency timer is reloaded with period.
|
|
_state.Timer = (2048 - _state.Frequency) * 2 + 6;
|
|
|
|
//If length counter is zero, it is set to 64 (256 for wave channel).
|
|
if(_state.Length == 0) {
|
|
_state.Length = 256;
|
|
_state.LengthEnabled = false;
|
|
}
|
|
|
|
//Wave channel's position is set to 0 but sample buffer is NOT refilled.
|
|
_state.Position = 0;
|
|
}
|
|
|
|
_state.LengthEnabled = (value & 0x40);
|
|
|
|
if(!_state.Enabled && prevEnabled) {
|
|
_state.Output = 0;
|
|
UpdateOutput();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GbaWaveChannel::WriteRam(uint16_t addr, uint8_t value)
|
|
{
|
|
//TODOGBA wave data is stored in a shift register on GBA
|
|
uint8_t bank = (_state.SelectedBank ^ 1) << 4;
|
|
_state.Ram[bank | (addr & 0x0F)] = value;
|
|
}
|
|
|
|
uint8_t GbaWaveChannel::ReadRam(uint16_t addr)
|
|
{
|
|
//TODOGBA wave data is stored in a shift register on GBA
|
|
uint8_t bank = (_state.SelectedBank ^ 1) << 4;
|
|
return _state.Ram[bank | (addr & 0x0F)];
|
|
}
|
|
|
|
void GbaWaveChannel::Serialize(Serializer& s)
|
|
{
|
|
SV(_state.DacEnabled); SV(_state.SampleBuffer); SV(_state.Position); SV(_state.Volume); SV(_state.Frequency);
|
|
SV(_state.Length); SV(_state.LengthEnabled); SV(_state.Enabled); SV(_state.Timer); SV(_state.Output);
|
|
SVArray(_state.Ram, 0x20);
|
|
SV(_state.DoubleLength);
|
|
SV(_state.SelectedBank);
|
|
SV(_state.OverrideVolume);
|
|
}
|