1 #pragma once 2 #include "stdafx.h" 3 #include "SquareChannel.h" 4 #include "BaseExpansionAudio.h" 5 #include "CPU.h" 6 #include "Console.h" 7 #include "MemoryManager.h" 8 9 class MMC5Square : public SquareChannel 10 { 11 int8_t _currentOutput; 12 13 private: InitializeSweep(uint8_t regValue)14 virtual void InitializeSweep(uint8_t regValue) override 15 { 16 //"$5001 has no effect. The MMC5 pulse channels will not sweep, as they have no sweep unit." 17 } 18 19 public: MMC5Square(shared_ptr<Console> console)20 MMC5Square(shared_ptr<Console> console) : SquareChannel(AudioChannel::MMC5, console, nullptr, false) 21 { 22 _currentOutput = 0; 23 _isMmc5Square = true; 24 Reset(false); 25 } 26 GetOutput()27 int8_t GetOutput() 28 { 29 return _currentOutput; 30 } 31 RunChannel()32 void RunChannel() 33 { 34 if(_timer == 0) { 35 _dutyPos = (_dutyPos - 1) & 0x07; 36 //"Frequency values less than 8 do not silence the MMC5 pulse channels; they can output ultrasonic frequencies." 37 _currentOutput = _dutySequences[_duty][_dutyPos] * GetVolume(); 38 _timer = _period; 39 } else { 40 _timer--; 41 } 42 } 43 }; 44 45 class MMC5Audio : public BaseExpansionAudio 46 { 47 private: 48 MMC5Square _square1; 49 MMC5Square _square2; 50 int16_t _audioCounter; 51 int16_t _lastOutput; 52 53 bool _pcmReadMode; 54 bool _pcmIrqEnabled; 55 uint8_t _pcmOutput; 56 57 protected: StreamState(bool saving)58 void StreamState(bool saving) override 59 { 60 BaseExpansionAudio::StreamState(saving); 61 62 SnapshotInfo square1{ &_square1 }; 63 SnapshotInfo square2{ &_square2 }; 64 Stream(square1, square2, _audioCounter, _lastOutput, _pcmReadMode, _pcmIrqEnabled, _pcmOutput); 65 } 66 ClockAudio()67 void ClockAudio() override 68 { 69 _audioCounter--; 70 _square1.RunChannel(); 71 _square2.RunChannel(); 72 if(_audioCounter <= 0) { 73 //~240hz envelope/length counter 74 _audioCounter = _console->GetCpu()->GetClockRate(_console->GetModel()) / 240; 75 _square1.TickLengthCounter(); 76 _square1.TickEnvelope(); 77 _square2.TickLengthCounter(); 78 _square2.TickEnvelope(); 79 } 80 81 //"The sound output of the square channels are equivalent in volume to the corresponding APU channels" 82 //"The polarity of all MMC5 channels is reversed compared to the APU." 83 int16_t summedOutput = -(_square1.GetOutput() + _square2.GetOutput() + _pcmOutput); 84 if(summedOutput != _lastOutput) { 85 _console->GetApu()->AddExpansionAudioDelta(AudioChannel::MMC5, summedOutput - _lastOutput); 86 _lastOutput = summedOutput; 87 } 88 89 _square1.ReloadCounter(); 90 _square2.ReloadCounter(); 91 } 92 93 public: MMC5Audio(shared_ptr<Console> console)94 MMC5Audio(shared_ptr<Console> console) : BaseExpansionAudio(console), _square1(console), _square2(console) 95 { 96 _audioCounter = 0; 97 _lastOutput = 0; 98 _pcmReadMode = false; 99 _pcmIrqEnabled = false; 100 _pcmOutput = 0; 101 } 102 ReadRegister(uint16_t addr)103 uint8_t ReadRegister(uint16_t addr) 104 { 105 switch(addr) { 106 case 0x5010: 107 //TODO: PCM IRQ 108 return 0; 109 110 case 0x5015: 111 uint8_t status = 0; 112 status |= _square1.GetStatus() ? 0x01 : 0x00; 113 status |= _square2.GetStatus() ? 0x02 : 0x00; 114 return status; 115 } 116 117 return _console->GetMemoryManager()->GetOpenBus(); 118 } 119 WriteRegister(uint16_t addr,uint8_t value)120 void WriteRegister(uint16_t addr, uint8_t value) 121 { 122 switch(addr) { 123 case 0x5000: case 0x5001: case 0x5002: case 0x5003: 124 _square1.WriteRAM(addr, value); 125 break; 126 127 case 0x5004: case 0x5005: case 0x5006: case 0x5007: 128 _square2.WriteRAM(addr, value); 129 break; 130 131 case 0x5010: 132 //TODO: Read mode & PCM IRQs are not implemented 133 _pcmReadMode = (value & 0x01) == 0x01; 134 _pcmIrqEnabled = (value & 0x80) == 0x80; 135 break; 136 137 case 0x5011: 138 if(!_pcmReadMode) { 139 if(value != 0) { 140 _pcmOutput = value; 141 } 142 } 143 break; 144 145 case 0x5015: 146 _square1.SetEnabled((value & 0x01) == 0x01); 147 _square2.SetEnabled((value & 0x02) == 0x02); 148 break; 149 } 150 } 151 };