2015-07-14 21:50:14 -04:00
# pragma once
# include "stdafx.h"
# include "IMemoryHandler.h"
# include "CPU.h"
2015-07-21 23:05:27 -04:00
# include "EmulationSettings.h"
2015-07-14 21:50:14 -04:00
enum class FrameType
{
None = 0 ,
QuarterFrame = 1 ,
HalfFrame = 2 ,
} ;
2015-07-14 23:35:30 -04:00
class ApuFrameCounter : public IMemoryHandler , public Snapshotable
2015-07-14 21:50:14 -04:00
{
private :
2015-07-21 23:05:27 -04:00
const vector < vector < int32_t > > _stepCyclesNtsc = { { { 7457 , 14913 , 22371 , 29828 , 29829 , 29830 } ,
{ 7457 , 14913 , 22371 , 29829 , 37281 , 37282 } } } ;
const vector < vector < int32_t > > _stepCyclesPal = { { { 8313 , 16627 , 24939 , 33252 , 33253 , 33254 } ,
{ 8313 , 16627 , 24939 , 33253 , 41565 , 41566 } } } ;
2015-07-14 21:50:14 -04:00
const vector < vector < FrameType > > _frameType = { { { FrameType : : QuarterFrame , FrameType : : HalfFrame , FrameType : : QuarterFrame , FrameType : : None , FrameType : : HalfFrame , FrameType : : None } ,
{ FrameType : : QuarterFrame , FrameType : : HalfFrame , FrameType : : QuarterFrame , FrameType : : None , FrameType : : HalfFrame , FrameType : : None } } } ;
2015-07-21 23:05:27 -04:00
vector < vector < int32_t > > _stepCycles ;
NesModel _nesModel ;
2015-07-19 01:30:13 -04:00
int32_t _nextIrqCycle ;
int32_t _previousCycle ;
uint32_t _currentStep ;
uint32_t _stepMode ; //0: 4-step mode, 1: 5-step mode
bool _inhibitIRQ ;
2016-05-18 20:42:26 -04:00
uint8_t _blockFrameCounterTick ;
int16_t _newValue ;
int8_t _writeDelayCounter ;
2015-07-14 21:50:14 -04:00
2015-07-14 23:35:30 -04:00
void ( * _callback ) ( FrameType ) ;
2015-07-14 21:50:14 -04:00
public :
ApuFrameCounter ( void ( * frameCounterTickCallback ) ( FrameType ) )
{
_callback = frameCounterTickCallback ;
2015-07-19 01:30:13 -04:00
Reset ( false ) ;
2015-07-14 21:50:14 -04:00
}
2015-07-19 01:30:13 -04:00
void Reset ( bool softReset )
2015-07-14 23:35:30 -04:00
{
_nextIrqCycle = 29828 ;
2015-07-19 01:30:13 -04:00
2017-03-10 21:29:48 -05:00
_previousCycle = 0 ;
2015-07-19 01:30:13 -04:00
//"After reset: APU mode in $4017 was unchanged", so we need to keep whatever value _stepMode has for soft resets
if ( ! softReset ) {
_stepMode = 0 ;
}
2015-07-14 23:35:30 -04:00
_currentStep = 0 ;
2017-03-10 21:29:48 -05:00
//"After reset or power-up, APU acts as if $4017 were written with $00 from 9 to 12 clocks before first instruction begins."
//This is emulated in the CPU::Reset function
//Reset acts as if $00 was written to $4017
_newValue = 0 ;
_writeDelayCounter = 3 ;
2015-07-14 23:35:30 -04:00
_inhibitIRQ = false ;
2016-05-18 20:42:26 -04:00
_blockFrameCounterTick = 0 ;
2015-07-14 23:35:30 -04:00
}
2016-12-17 23:14:47 -05:00
void StreamState ( bool saving ) override
2015-07-14 23:35:30 -04:00
{
2016-06-02 20:20:26 -04:00
Stream ( _nextIrqCycle , _previousCycle , _currentStep , _stepMode , _inhibitIRQ , _nesModel , _blockFrameCounterTick , _writeDelayCounter , _newValue ) ;
2015-07-21 23:05:27 -04:00
if ( ! saving ) {
SetNesModel ( _nesModel ) ;
}
}
void SetNesModel ( NesModel model )
{
if ( _nesModel ! = model | | _stepCycles . size ( ) = = 0 ) {
_nesModel = model ;
_stepCycles . clear ( ) ;
2016-01-30 19:33:32 -05:00
switch ( model ) {
case NesModel : : NTSC :
case NesModel : : Dendy :
_stepCycles . push_back ( _stepCyclesNtsc [ 0 ] ) ;
_stepCycles . push_back ( _stepCyclesNtsc [ 1 ] ) ;
break ;
case NesModel : : PAL :
_stepCycles . push_back ( _stepCyclesPal [ 0 ] ) ;
_stepCycles . push_back ( _stepCyclesPal [ 1 ] ) ;
break ;
}
2015-07-21 23:05:27 -04:00
}
}
2015-07-14 23:35:30 -04:00
2015-07-19 01:30:13 -04:00
uint32_t Run ( int32_t & cyclesToRun )
2015-07-14 21:50:14 -04:00
{
uint32_t cyclesRan ;
2016-05-18 20:42:26 -04:00
2015-07-14 21:50:14 -04:00
if ( _previousCycle + cyclesToRun > = _stepCycles [ _stepMode ] [ _currentStep ] ) {
if ( ! _inhibitIRQ & & _stepMode = = 0 & & _currentStep > = 3 ) {
//Set irq on the last 3 cycles for 4-step mode
CPU : : SetIRQSource ( IRQSource : : FrameCounter ) ;
_nextIrqCycle + + ;
}
FrameType type = _frameType [ _stepMode ] [ _currentStep ] ;
2016-05-18 20:42:26 -04:00
if ( type ! = FrameType : : None & & ! _blockFrameCounterTick ) {
2015-07-14 21:50:14 -04:00
_callback ( type ) ;
2016-05-18 20:42:26 -04:00
//Do not allow writes to 4017 to clock the frame counter for the next cycle (i.e this odd cycle + the following even cycle)
_blockFrameCounterTick = 2 ;
2015-07-14 21:50:14 -04:00
}
2016-06-01 20:11:28 -04:00
if ( _stepCycles [ _stepMode ] [ _currentStep ] < _previousCycle ) {
//This can happen when switching from PAL to NTSC, which can cause a freeze (endless loop in APU)
cyclesRan = 0 ;
} else {
cyclesRan = _stepCycles [ _stepMode ] [ _currentStep ] - _previousCycle ;
}
2015-07-14 21:50:14 -04:00
cyclesToRun - = cyclesRan ;
_currentStep + + ;
if ( _currentStep = = 6 ) {
_currentStep = 0 ;
_previousCycle = 0 ;
if ( _stepMode = = 0 & & ! _inhibitIRQ ) {
_nextIrqCycle = 29828 ;
}
} else {
_previousCycle + = cyclesRan ;
}
} else {
cyclesRan = cyclesToRun ;
cyclesToRun = 0 ;
_previousCycle + = cyclesRan ;
}
2016-05-18 20:42:26 -04:00
if ( _newValue > = 0 ) {
_writeDelayCounter - - ;
if ( _writeDelayCounter = = 0 ) {
//Apply new value after the appropriate number of cycles has elapsed
_stepMode = ( ( _newValue & 0x80 ) = = 0x80 ) ? 1 : 0 ;
if ( ! _inhibitIRQ & & _stepMode = = 0 ) {
_nextIrqCycle = 29828 ;
} else {
_nextIrqCycle = - 1 ;
}
_writeDelayCounter = - 1 ;
_currentStep = 0 ;
_previousCycle = 0 ;
_newValue = - 1 ;
if ( _stepMode & & ! _blockFrameCounterTick ) {
//"Writing to $4017 with bit 7 set will immediately generate a clock for both the quarter frame and the half frame units, regardless of what the sequencer is doing."
_callback ( FrameType : : HalfFrame ) ;
_blockFrameCounterTick = 2 ;
}
}
}
if ( _blockFrameCounterTick > 0 ) {
_blockFrameCounterTick - - ;
}
2015-07-14 21:50:14 -04:00
return cyclesRan ;
}
bool IrqPending ( uint32_t cyclesToRun )
{
2016-05-18 20:42:26 -04:00
if ( _newValue > = 0 | | _blockFrameCounterTick > 0 ) {
return true ;
}
if ( _previousCycle + ( int32_t ) cyclesToRun > = _stepCycles [ _stepMode ] [ _currentStep ] ) {
return true ;
}
2015-07-14 21:50:14 -04:00
if ( _nextIrqCycle ! = - 1 ) {
if ( _previousCycle + cyclesToRun > = ( uint32_t ) _nextIrqCycle ) {
return true ;
}
}
return false ;
}
2016-12-17 23:14:47 -05:00
void GetMemoryRanges ( MemoryRanges & ranges ) override
2015-07-14 21:50:14 -04:00
{
2015-07-29 22:10:34 -04:00
ranges . AddHandler ( MemoryOperation : : Write , 0x4017 ) ;
2015-07-14 21:50:14 -04:00
}
2016-12-17 23:14:47 -05:00
uint8_t ReadRAM ( uint16_t addr ) override
2015-07-14 21:50:14 -04:00
{
return 0 ;
}
2016-12-17 23:14:47 -05:00
void WriteRAM ( uint16_t addr , uint8_t value ) override
2015-07-14 21:50:14 -04:00
{
APU : : StaticRun ( ) ;
2016-05-18 20:42:26 -04:00
_newValue = value ;
2015-07-14 21:50:14 -04:00
2015-07-19 01:30:13 -04:00
//Reset sequence after $4017 is written to
2015-07-19 22:09:24 -04:00
if ( CPU : : GetCycleCount ( ) & 0x01 ) {
2015-07-19 01:30:13 -04:00
//"If the write occurs during an APU cycle, the effects occur 3 CPU cycles after the $4017 write cycle"
2016-05-18 20:42:26 -04:00
_writeDelayCounter = 3 ;
2015-07-19 01:30:13 -04:00
} else {
//"If the write occurs between APU cycles, the effects occur 4 CPU cycles after the write cycle. "
2016-05-18 20:42:26 -04:00
_writeDelayCounter = 4 ;
2015-07-19 01:30:13 -04:00
}
2015-07-14 21:50:14 -04:00
2016-05-18 20:42:26 -04:00
_inhibitIRQ = ( value & 0x40 ) = = 0x40 ;
if ( _inhibitIRQ ) {
CPU : : ClearIRQSource ( IRQSource : : FrameCounter ) ;
2015-07-14 21:50:14 -04:00
}
}
} ;