2015-07-15 01:50:14 +00:00
|
|
|
#pragma once
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include "APU.h"
|
|
|
|
#include "IMemoryHandler.h"
|
|
|
|
#include "ApuEnvelope.h"
|
|
|
|
|
|
|
|
class TriangleChannel : public ApuLengthCounter
|
|
|
|
{
|
|
|
|
private:
|
2017-04-01 02:14:16 +00:00
|
|
|
const uint8_t _sequence[32] = { 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
|
2015-07-15 01:50:14 +00:00
|
|
|
|
|
|
|
uint8_t _linearCounter = 0;
|
|
|
|
uint8_t _linearCounterReload = 0;
|
|
|
|
bool _linearReloadFlag = false;
|
|
|
|
bool _linearControlFlag = false;
|
|
|
|
|
|
|
|
uint8_t _sequencePosition = 0;
|
|
|
|
|
2015-07-15 03:35:30 +00:00
|
|
|
protected:
|
2016-12-18 04:14:47 +00:00
|
|
|
void Clock() override
|
2015-07-15 03:35:30 +00:00
|
|
|
{
|
|
|
|
//The sequencer is clocked by the timer as long as both the linear counter and the length counter are nonzero.
|
|
|
|
if(_lengthCounter > 0 && _linearCounter > 0) {
|
|
|
|
_sequencePosition = (_sequencePosition + 1) & 0x1F;
|
2016-07-19 21:36:37 +00:00
|
|
|
|
|
|
|
if(_period >= 2 || !EmulationSettings::CheckFlag(EmulationFlags::SilenceTriangleHighFreq)) {
|
|
|
|
//Disabling the triangle channel when period is < 2 removes "pops" in the audio that are caused by the ultrasonic frequencies
|
|
|
|
//This is less "accurate" in terms of emulation, so this is an option (disabled by default)
|
|
|
|
AddOutput(_sequence[_sequencePosition]);
|
|
|
|
}
|
2015-07-15 03:35:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-15 01:50:14 +00:00
|
|
|
public:
|
2016-01-14 06:21:09 +00:00
|
|
|
TriangleChannel(AudioChannel channel, SoundMixer* mixer) : ApuLengthCounter(channel, mixer)
|
2015-07-15 01:50:14 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2016-12-18 04:14:47 +00:00
|
|
|
virtual void Reset(bool softReset) override
|
2015-07-15 03:35:30 +00:00
|
|
|
{
|
2015-07-19 05:30:13 +00:00
|
|
|
ApuLengthCounter::Reset(softReset);
|
2015-07-15 03:35:30 +00:00
|
|
|
|
|
|
|
_linearCounter = 0;
|
|
|
|
_linearCounterReload = 0;
|
|
|
|
_linearReloadFlag = false;
|
|
|
|
_linearControlFlag = false;
|
|
|
|
|
|
|
|
_sequencePosition = 0;
|
|
|
|
}
|
|
|
|
|
2016-12-18 04:14:47 +00:00
|
|
|
virtual void StreamState(bool saving) override
|
2015-07-15 03:35:30 +00:00
|
|
|
{
|
|
|
|
ApuLengthCounter::StreamState(saving);
|
|
|
|
|
2016-06-03 00:20:26 +00:00
|
|
|
Stream(_linearCounter, _linearCounterReload, _linearReloadFlag, _linearControlFlag, _sequencePosition);
|
2015-07-15 03:35:30 +00:00
|
|
|
}
|
|
|
|
|
2016-12-18 04:14:47 +00:00
|
|
|
void GetMemoryRanges(MemoryRanges &ranges) override
|
2015-07-15 01:50:14 +00:00
|
|
|
{
|
2015-07-30 02:10:34 +00:00
|
|
|
ranges.AddHandler(MemoryOperation::Write, 0x4008, 0x400B);
|
2015-07-15 01:50:14 +00:00
|
|
|
}
|
|
|
|
|
2016-12-18 04:14:47 +00:00
|
|
|
void WriteRAM(uint16_t addr, uint8_t value) override
|
2015-07-15 01:50:14 +00:00
|
|
|
{
|
|
|
|
APU::StaticRun();
|
|
|
|
switch(addr & 0x03) {
|
|
|
|
case 0: //4008
|
|
|
|
_linearControlFlag = (value & 0x80) == 0x80;
|
|
|
|
_linearCounterReload = value & 0x7F;
|
|
|
|
|
|
|
|
InitializeLengthCounter(_linearControlFlag);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2: //400A
|
|
|
|
_period &= ~0x00FF;
|
|
|
|
_period |= value;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3: //400B
|
|
|
|
LoadLengthCounter(value >> 3);
|
|
|
|
|
|
|
|
_period &= ~0xFF00;
|
2016-06-01 02:58:22 +00:00
|
|
|
_period |= (value & 0x07) << 8;
|
2015-07-15 01:50:14 +00:00
|
|
|
|
|
|
|
//Side effects Sets the linear counter reload flag
|
|
|
|
_linearReloadFlag = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TickLinearCounter()
|
|
|
|
{
|
|
|
|
if(_linearReloadFlag) {
|
|
|
|
_linearCounter = _linearCounterReload;
|
|
|
|
} else if(_linearCounter > 0) {
|
|
|
|
_linearCounter--;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!_linearControlFlag) {
|
|
|
|
_linearReloadFlag = false;
|
|
|
|
}
|
|
|
|
}
|
2017-08-30 22:31:27 +00:00
|
|
|
|
|
|
|
ApuTriangleState GetState()
|
|
|
|
{
|
|
|
|
ApuTriangleState state;
|
|
|
|
state.Enabled = _enabled;
|
2017-12-21 03:11:36 +00:00
|
|
|
state.Frequency = CPU::GetClockRate(GetNesModel()) / 32.0 / (_period + 1);
|
2017-08-30 22:31:27 +00:00
|
|
|
state.LengthCounter = ApuLengthCounter::GetState();
|
|
|
|
state.OutputVolume = _lastOutput;
|
|
|
|
state.Period = _period;
|
2018-01-02 04:23:18 +00:00
|
|
|
state.Timer = _timer;
|
2017-08-30 22:31:27 +00:00
|
|
|
state.SequencePosition = _sequencePosition;
|
|
|
|
return state;
|
|
|
|
}
|
2015-07-15 01:50:14 +00:00
|
|
|
};
|