2014-06-22 22:15:35 -04:00
# include "stdafx.h"
# include "APU.h"
# include "CPU.h"
2015-07-14 21:50:14 -04:00
# include "SquareChannel.h"
# include "TriangleChannel.h"
# include "NoiseChannel.h"
# include "DeltaModulationChannel.h"
# include "ApuFrameCounter.h"
2015-07-17 20:58:57 -04:00
# include "EmulationSettings.h"
2016-01-14 01:21:09 -05:00
# include "SoundMixer.h"
2017-04-29 08:29:56 -04:00
# include "MemoryManager.h"
2014-06-22 22:15:35 -04:00
2018-07-01 15:21:05 -04:00
APU : : APU ( shared_ptr < Console > console )
2014-06-22 22:15:35 -04:00
{
2016-01-23 00:52:06 -05:00
_nesModel = NesModel : : Auto ;
2017-04-29 21:39:57 -04:00
_apuEnabled = true ;
2018-07-01 15:21:05 -04:00
_console = console ;
_mixer = _console - > GetSoundMixer ( ) ;
2018-07-13 22:19:26 -04:00
_settings = _console - > GetSettings ( ) ;
2014-06-22 22:15:35 -04:00
2018-07-01 15:21:05 -04:00
_squareChannel [ 0 ] . reset ( new SquareChannel ( AudioChannel : : Square1 , _console , _mixer . get ( ) , true ) ) ;
_squareChannel [ 1 ] . reset ( new SquareChannel ( AudioChannel : : Square2 , _console , _mixer . get ( ) , false ) ) ;
_triangleChannel . reset ( new TriangleChannel ( AudioChannel : : Triangle , _console , _mixer . get ( ) ) ) ;
_noiseChannel . reset ( new NoiseChannel ( AudioChannel : : Noise , _console , _mixer . get ( ) ) ) ;
_deltaModulationChannel . reset ( new DeltaModulationChannel ( AudioChannel : : DMC , _console , _mixer . get ( ) ) ) ;
_frameCounter . reset ( new ApuFrameCounter ( _console ) ) ;
2014-06-23 13:52:53 -04:00
2018-07-01 15:21:05 -04:00
_console - > GetMemoryManager ( ) - > RegisterIODevice ( _squareChannel [ 0 ] . get ( ) ) ;
_console - > GetMemoryManager ( ) - > RegisterIODevice ( _squareChannel [ 1 ] . get ( ) ) ;
_console - > GetMemoryManager ( ) - > RegisterIODevice ( _frameCounter . get ( ) ) ;
_console - > GetMemoryManager ( ) - > RegisterIODevice ( _triangleChannel . get ( ) ) ;
_console - > GetMemoryManager ( ) - > RegisterIODevice ( _noiseChannel . get ( ) ) ;
_console - > GetMemoryManager ( ) - > RegisterIODevice ( _deltaModulationChannel . get ( ) ) ;
2015-07-19 01:30:13 -04:00
Reset ( false ) ;
2014-06-23 13:52:53 -04:00
}
APU : : ~ APU ( )
{
}
2015-07-21 23:05:27 -04:00
void APU : : SetNesModel ( NesModel model , bool forceInit )
{
if ( _nesModel ! = model | | forceInit ) {
//Finish the current apu frame before switching model
Run ( ) ;
_nesModel = model ;
_squareChannel [ 0 ] - > SetNesModel ( model ) ;
_squareChannel [ 1 ] - > SetNesModel ( model ) ;
_triangleChannel - > SetNesModel ( model ) ;
_noiseChannel - > SetNesModel ( model ) ;
_deltaModulationChannel - > SetNesModel ( model ) ;
_frameCounter - > SetNesModel ( model ) ;
2016-01-14 01:21:09 -05:00
_mixer - > SetNesModel ( model ) ;
2015-07-21 23:05:27 -04:00
}
}
2015-07-14 21:50:14 -04:00
void APU : : FrameCounterTick ( FrameType type )
2014-06-22 22:15:35 -04:00
{
2015-07-14 21:50:14 -04:00
//Quarter & half frame clock envelope & linear counter
2018-07-01 15:21:05 -04:00
_squareChannel [ 0 ] - > TickEnvelope ( ) ;
_squareChannel [ 1 ] - > TickEnvelope ( ) ;
_triangleChannel - > TickLinearCounter ( ) ;
_noiseChannel - > TickEnvelope ( ) ;
2015-07-14 21:50:14 -04:00
if ( type = = FrameType : : HalfFrame ) {
//Half frames clock length counter & sweep
2018-07-01 15:21:05 -04:00
_squareChannel [ 0 ] - > TickLengthCounter ( ) ;
_squareChannel [ 1 ] - > TickLengthCounter ( ) ;
_triangleChannel - > TickLengthCounter ( ) ;
_noiseChannel - > TickLengthCounter ( ) ;
2015-07-14 21:50:14 -04:00
2018-07-01 15:21:05 -04:00
_squareChannel [ 0 ] - > TickSweep ( ) ;
_squareChannel [ 1 ] - > TickSweep ( ) ;
2015-07-14 21:50:14 -04:00
}
2014-06-22 22:15:35 -04:00
}
2018-12-24 23:31:32 -05:00
uint8_t APU : : GetStatus ( )
2014-06-22 22:15:35 -04:00
{
2015-07-14 21:50:14 -04:00
uint8_t status = 0 ;
status | = _squareChannel [ 0 ] - > GetStatus ( ) ? 0x01 : 0x00 ;
status | = _squareChannel [ 1 ] - > GetStatus ( ) ? 0x02 : 0x00 ;
status | = _triangleChannel - > GetStatus ( ) ? 0x04 : 0x00 ;
status | = _noiseChannel - > GetStatus ( ) ? 0x08 : 0x00 ;
status | = _deltaModulationChannel - > GetStatus ( ) ? 0x10 : 0x00 ;
2018-07-01 15:21:05 -04:00
status | = _console - > GetCpu ( ) - > HasIrqSource ( IRQSource : : FrameCounter ) ? 0x40 : 0x00 ;
status | = _console - > GetCpu ( ) - > HasIrqSource ( IRQSource : : DMC ) ? 0x80 : 0x00 ;
2015-07-14 21:50:14 -04:00
2018-12-24 23:31:32 -05:00
return status ;
}
uint8_t APU : : ReadRAM ( uint16_t addr )
{
2018-12-25 22:40:41 -05:00
//$4015 read
Run ( ) ;
2018-12-24 23:31:32 -05:00
uint8_t status = GetStatus ( ) ;
2015-07-14 21:50:14 -04:00
//Reading $4015 clears the Frame Counter interrupt flag.
2018-07-01 15:21:05 -04:00
_console - > GetCpu ( ) - > ClearIrqSource ( IRQSource : : FrameCounter ) ;
2014-06-22 22:15:35 -04:00
2015-07-14 21:50:14 -04:00
return status ;
2014-06-22 22:15:35 -04:00
}
2018-12-24 23:31:32 -05:00
uint8_t APU : : PeekRAM ( uint16_t addr )
{
2018-12-25 22:40:41 -05:00
if ( _console - > GetEmulationThreadId ( ) = = std : : this_thread : : get_id ( ) ) {
//Only run the APU (to catch up) if we're running this in the emulation thread (not 100% accurate, but we can't run the APU from any other thread without locking)
Run ( ) ;
}
2018-12-24 23:31:32 -05:00
return GetStatus ( ) ;
}
2014-06-22 22:15:35 -04:00
void APU : : WriteRAM ( uint16_t addr , uint8_t value )
{
2015-07-14 21:50:14 -04:00
//$4015 write
Run ( ) ;
2015-07-19 01:30:13 -04:00
//Writing to $4015 clears the DMC interrupt flag.
//This needs to be done before setting the enabled flag for the DMC (because doing so can trigger an IRQ)
2018-07-01 15:21:05 -04:00
_console - > GetCpu ( ) - > ClearIrqSource ( IRQSource : : DMC ) ;
2015-07-19 01:30:13 -04:00
2015-07-14 21:50:14 -04:00
_squareChannel [ 0 ] - > SetEnabled ( ( value & 0x01 ) = = 0x01 ) ;
_squareChannel [ 1 ] - > SetEnabled ( ( value & 0x02 ) = = 0x02 ) ;
_triangleChannel - > SetEnabled ( ( value & 0x04 ) = = 0x04 ) ;
_noiseChannel - > SetEnabled ( ( value & 0x08 ) = = 0x08 ) ;
_deltaModulationChannel - > SetEnabled ( ( value & 0x10 ) = = 0x10 ) ;
}
void APU : : GetMemoryRanges ( MemoryRanges & ranges )
{
2015-07-29 22:10:34 -04:00
ranges . AddHandler ( MemoryOperation : : Read , 0x4015 ) ;
ranges . AddHandler ( MemoryOperation : : Write , 0x4015 ) ;
2015-07-14 21:50:14 -04:00
}
void APU : : Run ( )
{
//Update framecounter and all channels
//This is called:
//-At the end of a frame
//-Before APU registers are read/written to
//-When a DMC or FrameCounter interrupt needs to be fired
2015-07-19 01:30:13 -04:00
int32_t cyclesToRun = _currentCycle - _previousCycle ;
2015-07-16 16:55:16 -04:00
2016-05-18 20:51:54 -04:00
while ( cyclesToRun > 0 ) {
2015-07-16 16:55:16 -04:00
_previousCycle + = _frameCounter - > Run ( cyclesToRun ) ;
2015-07-14 21:50:14 -04:00
2016-01-02 17:35:30 -05:00
//Reload counters set by writes to 4003/4008/400B/400F after running the frame counter to allow the length counter to be clocked first
//This fixes the test "len_reload_timing" (tests 4 & 5)
_squareChannel [ 0 ] - > ReloadCounter ( ) ;
_squareChannel [ 1 ] - > ReloadCounter ( ) ;
_noiseChannel - > ReloadCounter ( ) ;
_triangleChannel - > ReloadCounter ( ) ;
2015-07-16 16:55:16 -04:00
_squareChannel [ 0 ] - > Run ( _previousCycle ) ;
_squareChannel [ 1 ] - > Run ( _previousCycle ) ;
_noiseChannel - > Run ( _previousCycle ) ;
_triangleChannel - > Run ( _previousCycle ) ;
_deltaModulationChannel - > Run ( _previousCycle ) ;
}
2015-07-14 21:50:14 -04:00
}
2018-07-01 15:21:05 -04:00
void APU : : SetNeedToRun ( )
2015-07-14 21:50:14 -04:00
{
2018-07-01 15:21:05 -04:00
_needToRun = true ;
2015-07-14 21:50:14 -04:00
}
2015-07-19 01:30:13 -04:00
bool APU : : NeedToRun ( uint32_t currentCycle )
2015-07-14 21:50:14 -04:00
{
2018-07-01 15:21:05 -04:00
if ( _needToRun | | _deltaModulationChannel - > NeedToRun ( ) ) {
//Need to run whenever we alter the length counters
2015-07-25 11:59:18 -04:00
//Need to run every cycle when DMC is running to get accurate emulation (CPU stalling, interaction with sprite DMA, etc.)
2018-07-01 15:21:05 -04:00
_needToRun = false ;
2015-07-25 11:59:18 -04:00
return true ;
}
2015-07-14 21:50:14 -04:00
uint32_t cyclesToRun = currentCycle - _previousCycle ;
2019-01-25 20:39:22 -05:00
return _frameCounter - > NeedToRun ( cyclesToRun ) | | _deltaModulationChannel - > IrqPending ( cyclesToRun ) ;
2014-06-22 22:15:35 -04:00
}
2015-07-16 16:55:16 -04:00
void APU : : Exec ( )
2015-07-14 21:50:14 -04:00
{
2015-07-16 16:55:16 -04:00
_currentCycle + + ;
2016-01-30 14:57:50 -05:00
if ( _currentCycle = = SoundMixer : : CycleLength - 1 ) {
2016-01-21 00:53:02 -05:00
EndFrame ( ) ;
2015-07-19 01:30:13 -04:00
} else if ( NeedToRun ( _currentCycle ) ) {
2015-07-16 16:55:16 -04:00
Run ( ) ;
2014-06-25 17:34:16 -04:00
}
2014-06-22 22:15:35 -04:00
}
2014-06-25 21:52:37 -04:00
2016-01-21 00:53:02 -05:00
void APU : : EndFrame ( )
{
Run ( ) ;
_squareChannel [ 0 ] - > EndFrame ( ) ;
_squareChannel [ 1 ] - > EndFrame ( ) ;
_triangleChannel - > EndFrame ( ) ;
_noiseChannel - > EndFrame ( ) ;
_deltaModulationChannel - > EndFrame ( ) ;
_mixer - > PlayAudioBuffer ( _currentCycle ) ;
_currentCycle = 0 ;
_previousCycle = 0 ;
}
2018-07-01 15:21:05 -04:00
void APU : : ProcessCpuClock ( )
{
if ( _apuEnabled ) {
2018-07-13 22:19:26 -04:00
if ( _settings - > GetOverclockRate ( ) = = 100 | | ! _settings - > GetOverclockAdjustApu ( ) ) {
2018-07-01 15:21:05 -04:00
Exec ( ) ;
} else {
2018-07-13 22:19:26 -04:00
_cyclesNeeded + = 1.0 / ( ( double ) _settings - > GetOverclockRate ( ) / 100.0 ) ;
2018-07-01 15:21:05 -04:00
while ( _cyclesNeeded > = 1.0 ) {
Exec ( ) ;
_cyclesNeeded - - ;
}
}
}
}
2015-07-19 01:30:13 -04:00
void APU : : Reset ( bool softReset )
2015-07-14 23:35:30 -04:00
{
2017-04-29 21:39:57 -04:00
_apuEnabled = true ;
2016-06-12 18:11:31 -04:00
_cyclesNeeded = 0 ;
2015-07-16 16:55:16 -04:00
_currentCycle = 0 ;
2015-07-14 23:35:30 -04:00
_previousCycle = 0 ;
2015-07-19 01:30:13 -04:00
_squareChannel [ 0 ] - > Reset ( softReset ) ;
_squareChannel [ 1 ] - > Reset ( softReset ) ;
_triangleChannel - > Reset ( softReset ) ;
_noiseChannel - > Reset ( softReset ) ;
_deltaModulationChannel - > Reset ( softReset ) ;
_frameCounter - > Reset ( softReset ) ;
2015-07-14 23:35:30 -04:00
}
2014-06-25 21:52:37 -04:00
void APU : : StreamState ( bool saving )
{
2016-01-21 00:53:02 -05:00
if ( saving ) {
//End the APU frame - makes it simpler to restore sound after a state reload
EndFrame ( ) ;
} else {
_previousCycle = 0 ;
_currentCycle = 0 ;
}
2016-06-11 16:08:16 -04:00
SnapshotInfo squareChannel0 { _squareChannel [ 0 ] . get ( ) } ;
SnapshotInfo squareChannel1 { _squareChannel [ 1 ] . get ( ) } ;
SnapshotInfo triangleChannel { _triangleChannel . get ( ) } ;
SnapshotInfo noiseChannel { _noiseChannel . get ( ) } ;
SnapshotInfo deltaModulationChannel { _deltaModulationChannel . get ( ) } ;
SnapshotInfo frameCounter { _frameCounter . get ( ) } ;
SnapshotInfo mixer { _mixer . get ( ) } ;
2016-06-12 18:11:31 -04:00
Stream ( _nesModel , squareChannel0 , squareChannel1 , triangleChannel , noiseChannel , deltaModulationChannel , frameCounter , mixer , _cyclesNeeded ) ;
2016-01-30 14:57:50 -05:00
}
2016-06-12 11:28:45 -04:00
void APU : : AddExpansionAudioDelta ( AudioChannel channel , int16_t delta )
2016-01-30 14:57:50 -05:00
{
2018-07-01 15:21:05 -04:00
_mixer - > AddDelta ( channel , _currentCycle , delta ) ;
2017-04-29 21:39:57 -04:00
}
void APU : : SetApuStatus ( bool enabled )
{
_apuEnabled = enabled ;
}
bool APU : : IsApuEnabled ( )
{
//Adding extra lines before/after NMI temporarely turns off the APU
//This appears to result in less side-effects than spreading out the APU's
//load over the entire PPU frame, like what was done before.
//This is most likely due to the timing of the Frame Counter & DMC IRQs.
return _apuEnabled ;
2017-08-30 18:31:27 -04:00
}
2018-07-01 15:21:05 -04:00
void APU : : FillDmcReadBuffer ( )
2018-06-17 12:47:57 -04:00
{
2018-07-01 15:21:05 -04:00
_deltaModulationChannel - > FillReadBuffer ( ) ;
2018-06-17 12:47:57 -04:00
}
2017-08-30 18:31:27 -04:00
ApuState APU : : GetState ( )
{
ApuState state ;
state . Dmc = _deltaModulationChannel - > GetState ( ) ;
state . FrameCounter = _frameCounter - > GetState ( ) ;
state . Noise = _noiseChannel - > GetState ( ) ;
state . Square1 = _squareChannel [ 0 ] - > GetState ( ) ;
state . Square2 = _squareChannel [ 1 ] - > GetState ( ) ;
state . Triangle = _triangleChannel - > GetState ( ) ;
return state ;
2018-07-01 15:21:05 -04:00
}