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)
253 lines
7.2 KiB
C++
253 lines
7.2 KiB
C++
#include "pch.h"
|
||
#include "Gameboy/APU/GbWaveChannel.h"
|
||
#include "Gameboy/APU/GbApu.h"
|
||
#include "Gameboy/Gameboy.h"
|
||
#include "Shared/Emulator.h"
|
||
#include "Shared/EmuSettings.h"
|
||
|
||
GbWaveChannel::GbWaveChannel(GbApu* apu, Gameboy* gameboy)
|
||
{
|
||
_gameboy = gameboy;
|
||
_apu = apu;
|
||
|
||
//"When the Game Boy is switched on (before the internal boot ROM executes),
|
||
//the values in the wave table depend on the model. On the DMG, they are somewhat
|
||
//random, though the particular pattern is generally the same for each individual Game Boy unit.
|
||
//The game R-Type doesn't initialize wave RAM and thus relies on these."
|
||
|
||
//Note: On CGB, the boot rom initalizes wave ram to 00, FF, 00, FF, etc. (so these values will be overwritten)
|
||
if(_gameboy->GetEmulator()->GetSettings()->GetGameboyConfig().RamPowerOnState == RamState::Random) {
|
||
//If random ram is turned on, randomize it completely instead
|
||
_gameboy->InitializeRam(_state.Ram, 0x10);
|
||
} else {
|
||
//Otherwise, use a preset to ensure the audio is still audible
|
||
constexpr uint8_t gbWaveRamDefault[0x10] = { 0x84, 0x40, 0x43, 0xAA, 0x2D, 0x78, 0x92, 0x3C, 0x60, 0x59, 0x59, 0xB0, 0x34, 0xB8, 0x2E, 0xDA };
|
||
memcpy(_state.Ram, gbWaveRamDefault, 0x10);
|
||
}
|
||
}
|
||
|
||
GbWaveState& GbWaveChannel::GetState()
|
||
{
|
||
return _state;
|
||
}
|
||
|
||
bool GbWaveChannel::Enabled()
|
||
{
|
||
return _state.Enabled;
|
||
}
|
||
|
||
void GbWaveChannel::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 GbWaveChannel::ResetLengthCounter()
|
||
{
|
||
_state.Length = 0;
|
||
}
|
||
|
||
void GbWaveChannel::ClockLengthCounter()
|
||
{
|
||
if(_state.LengthEnabled && _state.Length > 0) {
|
||
_state.Length--;
|
||
if(_state.Length == 0) {
|
||
//"Length becoming 0 should clear status"
|
||
_state.Enabled = false;
|
||
_state.Output = 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
uint8_t GbWaveChannel::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 GbWaveChannel::GetOutput()
|
||
{
|
||
//"If a DAC is enabled, the digital range $0 to $F is linearly translated to the analog range -1 to 1,
|
||
//in arbitrary units. Importantly, the slope is negative: <20>digital 0<> maps to <20>analog 1<>, not <20>analog -1<>."
|
||
|
||
//Return -7 to 7 "analog" range (higher digital value = lower analog value)
|
||
return (7 - (int8_t)_state.Output) * (double)_dac.GetDacVolume() / 100;
|
||
}
|
||
|
||
void GbWaveChannel::UpdateOutput()
|
||
{
|
||
if(!_state.Enabled) {
|
||
return;
|
||
}
|
||
|
||
if(_state.Volume) {
|
||
_state.Output = _state.SampleBuffer >> (_state.Volume - 1);
|
||
} else {
|
||
_state.Output = 0;
|
||
}
|
||
}
|
||
|
||
void GbWaveChannel::Exec(uint32_t clocksToRun)
|
||
{
|
||
_dac.Exec(clocksToRun, _state.DacEnabled);
|
||
|
||
if(!_state.Enabled) {
|
||
return;
|
||
}
|
||
|
||
_state.Timer -= clocksToRun;
|
||
_allowRamAccess = false;
|
||
|
||
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) & 0x1F;
|
||
|
||
//then a sample is read into the sample buffer from this NEW position.
|
||
if(_state.Position & 0x01) {
|
||
_state.SampleBuffer = _state.Ram[_state.Position >> 1] & 0x0F;
|
||
} else {
|
||
_state.SampleBuffer = _state.Ram[_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.
|
||
UpdateOutput();
|
||
|
||
_allowRamAccess = true;
|
||
}
|
||
}
|
||
|
||
uint8_t GbWaveChannel::Read(uint16_t addr)
|
||
{
|
||
constexpr uint8_t openBusBits[5] = { 0x7F, 0xFF, 0x9F, 0xFF, 0xBF };
|
||
|
||
uint8_t value = 0;
|
||
switch(addr) {
|
||
case 0: value = _state.DacEnabled ? 0x80 : 0; break;
|
||
case 2: value = _state.Volume << 5; break;
|
||
case 4: value = _state.LengthEnabled ? 0x40 : 0; break;
|
||
}
|
||
|
||
return value | openBusBits[addr];
|
||
}
|
||
|
||
void GbWaveChannel::Write(uint16_t addr, uint8_t value)
|
||
{
|
||
switch(addr) {
|
||
case 0:
|
||
_state.DacEnabled = (value & 0x80) != 0;
|
||
_state.Enabled &= _state.DacEnabled;
|
||
break;
|
||
|
||
case 1:
|
||
_state.Length = 256 - value;
|
||
break;
|
||
|
||
case 2:
|
||
_state.Volume = (value & 0x60) >> 5;
|
||
|
||
//"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
|
||
if(_state.Enabled && _state.Timer <= 2) {
|
||
//Triggering the wave channel on DMG while the channel is reading a sample will corrupt the wave ram
|
||
TriggerWaveRamCorruption();
|
||
}
|
||
|
||
//Channel is enabled, if DAC is enabled
|
||
_state.Enabled = _state.DacEnabled;
|
||
|
||
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;
|
||
}
|
||
|
||
_apu->ProcessLengthEnableFlag(value, _state.Length, _state.LengthEnabled, _state.Enabled);
|
||
|
||
if(!_state.Enabled && prevEnabled) {
|
||
_state.Output = 0;
|
||
UpdateOutput();
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
void GbWaveChannel::WriteRam(uint16_t addr, uint8_t value)
|
||
{
|
||
if(!_state.Enabled) {
|
||
_state.Ram[addr & 0x0F] = value;
|
||
} else if(_allowRamAccess || _gameboy->IsCgb()) {
|
||
//"On monochrome consoles, wave RAM can only be accessed on the same cycle that CH3 does. Otherwise, reads return $FF, and writes are ignored."
|
||
//On CGB, the wave RAM can be accessed at any time, but will read/write the value the channel is currently accessing
|
||
_state.Ram[_state.Position >> 1] = value;
|
||
}
|
||
}
|
||
|
||
uint8_t GbWaveChannel::ReadRam(uint16_t addr)
|
||
{
|
||
if(!_state.Enabled) {
|
||
return _state.Ram[addr & 0x0F];
|
||
} else if(_allowRamAccess || _gameboy->IsCgb()) {
|
||
//"On monochrome consoles, wave RAM can only be accessed on the same cycle that CH3 does. Otherwise, reads return $FF, and writes are ignored."
|
||
//On CGB, the wave RAM can be accessed at any time, but will read/write the value the channel is currently accessing
|
||
return _state.Ram[_state.Position >> 1];
|
||
} else {
|
||
return 0xFF;
|
||
}
|
||
}
|
||
|
||
void GbWaveChannel::TriggerWaveRamCorruption()
|
||
{
|
||
if(_gameboy->IsCgb()) {
|
||
return;
|
||
}
|
||
|
||
uint8_t pos = ((_state.Position + 1) & 0x1F) >> 1;
|
||
if(pos < 4) {
|
||
//"If the channel was reading one of the first four bytes, the only first byte will be rewritten with the byte being read."
|
||
_state.Ram[0] = _state.Ram[pos];
|
||
} else {
|
||
//Otherwise, "the first FOUR bytes of wave RAM will be rewritten with the four aligned bytes that the read was from"
|
||
memcpy(_state.Ram, _state.Ram + (pos & 0x0C), 4);
|
||
}
|
||
}
|
||
|
||
void GbWaveChannel::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, 0x10); SV(_allowRamAccess);
|
||
SV(_dac);
|
||
}
|