Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 22 additions & 11 deletions Core/NES/Mappers/Audio/Mmc5Audio.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ class Mmc5Square : public SquareChannel
}

public:
Mmc5Square(NesConsole* console) : SquareChannel(AudioChannel::MMC5, console, false)
Mmc5Square(NesConsole* console)
: SquareChannel(AudioChannel::MMC5, console, false)
{
_currentOutput = 0;
_isMmc5Square = true;
Expand Down Expand Up @@ -59,7 +60,8 @@ class Mmc5Audio : public ISerializable

bool _pcmReadMode = false;
bool _pcmIrqEnabled = false;
uint8_t _pcmOutput = 0;
bool _pcmIrqPending = false;
uint8_t _pcmOutput;

protected:
void Serialize(Serializer& s) override
Expand All @@ -70,6 +72,7 @@ class Mmc5Audio : public ISerializable
SV(_lastOutput);
SV(_pcmReadMode);
SV(_pcmIrqEnabled);
SV(_pcmIrqPending);
SV(_pcmOutput);
}

Expand Down Expand Up @@ -104,7 +107,8 @@ class Mmc5Audio : public ISerializable
_square2.ReloadLengthCounter();
}

Mmc5Audio(NesConsole* console) : _square1(console), _square2(console)
Mmc5Audio(NesConsole* console)
: _square1(console), _square2(console)
{
_console = console;
_apu = console->GetApu();
Expand All @@ -117,10 +121,15 @@ class Mmc5Audio : public ISerializable
}

__forceinline bool GetPcmReadMode() { return _pcmReadMode; }
__forceinline bool GetPcmIRQLine() { return _pcmIrqEnabled && _pcmIrqPending; }

void HandlePcmRead(uint16_t addr, uint8_t value)
//$5011 write
//+ "Write-by-read writes to this register in PCM read-mode."
void DacWrite(uint8_t value)
{
if((addr & 0xC000) == 0x8000 && value) {
if(value == 0) {
_pcmIrqPending = true;
} else {
_pcmOutput = value;
}
}
Expand All @@ -129,8 +138,12 @@ class Mmc5Audio : public ISerializable
{
switch(addr) {
case 0x5010:
//TODO: PCM IRQ
return 0;
if(_pcmIrqPending) {
_pcmIrqPending = false;
return 0x80;
} else {
return 0x00;
}

case 0x5015:
uint8_t status = 0;
Expand Down Expand Up @@ -160,16 +173,13 @@ class Mmc5Audio : public ISerializable
break;

case 0x5010:
//TODO: PCM IRQs
_pcmReadMode = (value & 0x01) == 0x01;
_pcmIrqEnabled = (value & 0x80) == 0x80;
break;

case 0x5011:
if(!_pcmReadMode) {
if(value != 0) {
_pcmOutput = value;
}
DacWrite(value);
}
break;

Expand Down Expand Up @@ -234,6 +244,7 @@ class Mmc5Audio : public ISerializable
entries.push_back(MapperStateEntry("$5010-$5011", "PCM"));
entries.push_back(MapperStateEntry("$5010.0", "PCM Read Mode", _pcmReadMode));
entries.push_back(MapperStateEntry("$5010.7", "PCM IRQ Enabled", _pcmIrqEnabled));
entries.push_back(MapperStateEntry("$5010.7", "PCM IRQ Pending", _pcmIrqPending));
entries.push_back(MapperStateEntry("$5011", "PCM Output", _pcmOutput, MapperStateValueType::Number8));
}
};
31 changes: 19 additions & 12 deletions Core/NES/Mappers/Nintendo/MMC5.h
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,10 @@ class MMC5 : public BaseMapper, public IExtModeMapperDebug
WriteRegister(0x5117, 0xFF);

UpdateChrBanks(true);

//The same MMC5A chip, tested with repeated power cycles on different days of the week and different moon phases, etc,
//has been observed to power on with a DAC voltage equivalent to DAC value $EF or $FF.
_audio->DacWrite((GetPowerOnByte() & 0x01) ? 0xFF : 0xEF);
}

virtual ~MMC5()
Expand Down Expand Up @@ -439,11 +443,20 @@ class MMC5 : public BaseMapper, public IExtModeMapperDebug
}
}

void UpdateIrqState()
{
if((_irqEnabled && _irqPending) || _audio->GetPcmIRQLine()) {
_console->GetCpu()->SetIrqSource(IRQSource::External);
} else {
_console->GetCpu()->ClearIrqSource(IRQSource::External);
}
}

uint8_t ReadRam(uint16_t addr) override
{
uint8_t value = BaseMapper::InternalRead(addr);
if(_audio->GetPcmReadMode()) {
_audio->HandlePcmRead(addr, value);
if(_audio->GetPcmReadMode() && (addr & 0xC000) == 0x8000) {
_audio->DacWrite(value);
}
return value;
}
Expand All @@ -469,9 +482,7 @@ class MMC5 : public BaseMapper, public IExtModeMapperDebug
_scanlineCounter++;
if(_irqCounterTarget == _scanlineCounter) {
_irqPending = true;
if(_irqEnabled) {
_console->GetCpu()->SetIrqSource(IRQSource::External);
}
UpdateIrqState();
}
}
} else if(addr >= 0x2000 && addr <= 0x2FFF) {
Expand Down Expand Up @@ -641,11 +652,7 @@ class MMC5 : public BaseMapper, public IExtModeMapperDebug
case 0x5203: _irqCounterTarget = value; break;
case 0x5204:
_irqEnabled = (value & 0x80) == 0x80;
if(!_irqEnabled) {
_console->GetCpu()->ClearIrqSource(IRQSource::External);
} else if(_irqEnabled && _irqPending) {
_console->GetCpu()->SetIrqSource(IRQSource::External);
}
UpdateIrqState();
break;
case 0x5205: _multiplierValue1 = value; break;
case 0x5206: _multiplierValue2 = value; break;
Expand All @@ -666,7 +673,7 @@ class MMC5 : public BaseMapper, public IExtModeMapperDebug
case 0x5204: {
uint8_t value = (_ppuInFrame ? 0x40 : 0x00) | (_irqPending ? 0x80 : 0x00);
_irqPending = false;
_console->GetCpu()->ClearIrqSource(IRQSource::External);
UpdateIrqState();
return value;
}

Expand All @@ -680,7 +687,7 @@ class MMC5 : public BaseMapper, public IExtModeMapperDebug
_lastPpuReadAddr = 0;
_scanlineCounter = 0;
_irqPending = false;
_console->GetCpu()->ClearIrqSource(IRQSource::External);
UpdateIrqState();
return DebugReadRam(addr);
}

Expand Down
Loading