nestopia/source/core/NstTimer.hpp
2012-09-02 13:13:55 -04:00

284 lines
5.7 KiB
C++

////////////////////////////////////////////////////////////////////////////////////////
//
// Nestopia - NES/Famicom emulator written in C++
//
// Copyright (C) 2003-2008 Martin Freij
//
// This file is part of Nestopia.
//
// Nestopia is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// Nestopia is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Nestopia; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
////////////////////////////////////////////////////////////////////////////////////////
#ifndef NST_TIMER_H
#define NST_TIMER_H
#ifndef NST_CPU_H
#include "NstCpu.hpp"
#endif
#ifndef NST_PPU_H
#include "NstPpu.hpp"
#endif
#ifdef NST_PRAGMA_ONCE
#pragma once
#endif
namespace Nes
{
namespace Core
{
namespace Timer
{
template<typename Unit,uint Divider=1>
class M2
{
public:
explicit M2(Cpu&);
template<typename Param>
M2(Cpu&,Param&);
template<typename Param>
M2(Cpu&,const Param&);
void Reset(bool,bool);
void VSync();
private:
enum
{
IRQ_SETUP = 2
};
NES_DECL_HOOK( Signaled );
Cycle count;
ibool connected;
Cpu& cpu;
public:
Unit unit;
bool Connect(bool connect)
{
connected = connect;
return connect;
}
bool Connected() const
{
return connected;
}
void Update()
{
M2::NES_DO_HOOK( Signaled );
}
void ClearIRQ() const
{
cpu.ClearIRQ();
}
};
template<typename Unit,uint Divider>
M2<Unit,Divider>::M2(Cpu& c)
: count(0), connected(false), cpu(c)
{
}
template<typename Unit,uint Divider>
template<typename Param>
M2<Unit,Divider>::M2(Cpu& c,Param& p)
: count(0), connected(false), cpu(c), unit(p)
{
}
template<typename Unit,uint Divider>
template<typename Param>
M2<Unit,Divider>::M2(Cpu& c,const Param& p)
: count(0), connected(false), cpu(c), unit(p)
{
}
template<typename Unit,uint Divider>
void M2<Unit,Divider>::Reset(const bool hard,const bool connect)
{
count = 0;
connected = connect;
unit.Reset( hard );
cpu.AddHook( Hook(this,&M2::Hook_Signaled) );
}
NES_HOOK_T(template<typename Unit NST_COMMA uint Divider>,M2<Unit NST_COMMA Divider>,Signaled)
{
NST_COMPILE_ASSERT( Divider <= 8 );
while (count <= cpu.GetCycles())
{
if (connected && unit.Clock())
cpu.DoIRQ( Cpu::IRQ_EXT, count + cpu.GetClock(IRQ_SETUP) );
count += cpu.GetClock(Divider);
}
}
template<typename Unit,uint Divider>
void M2<Unit,Divider>::VSync()
{
NST_VERIFY( count == 0 || count >= cpu.GetFrameCycles());
count = (count > cpu.GetFrameCycles() ? count - cpu.GetFrameCycles() : 0);
}
template<typename Unit,uint Hold,uint Delay>
class A12;
template<>
class A12<void,0,0>
{
protected:
template<uint Hold>
class Filter
{
Cycle clock;
Cycle hold;
public:
Filter()
: clock(0), hold(0) {}
void Reset(const Ppu& ppu)
{
clock = 0;
hold = ppu.GetClock(Hold);
}
bool Clock(Cycle cycle)
{
Cycle next = clock;
clock = cycle + hold;
return cycle >= next;
}
void VSync(const Cpu& cpu)
{
clock = (clock > cpu.GetFrameCycles() ? clock - cpu.GetFrameCycles() : 0);
}
};
};
template<>
class A12<void,0,0>::Filter<0>
{
public:
void Reset(const Ppu&) const
{
}
bool Clock(Cycle) const
{
return true;
}
void VSync(const Cpu&) const
{
}
};
template<typename Unit,uint Hold=0,uint Delay=0>
class A12 : A12<void,0,0>
{
public:
void Reset(bool,bool=true);
private:
NES_DECL_LINE( Signaled );
uint line;
Cpu& cpu;
Ppu& ppu;
Filter<Hold> filter;
public:
Unit unit;
A12(Cpu& c,Ppu& p)
: line(0), cpu(c), ppu(p) {}
template<typename Param>
A12(Cpu& c,Ppu& p,Param& a)
: cpu(c), ppu(p), unit(a) {}
void Connect(bool connect)
{
line = ppu.SetAddressLineHook( Io::Line(connect ? this : NULL,connect ? &A12<Unit,Hold,Delay>::Line_Signaled : NULL) ) & 0x1000;
}
bool Connected() const
{
return ppu.GetAddressLineHook();
}
void Update() const
{
ppu.Update();
}
void ClearIRQ() const
{
cpu.ClearIRQ();
}
void VSync()
{
filter.VSync( cpu );
}
};
template<typename Unit,uint Hold,uint Delay>
void A12<Unit,Hold,Delay>::Reset(bool hard,bool connect)
{
filter.Reset( ppu );
unit.Reset( hard );
Connect( connect );
ppu.EnableCpuSynchronization();
}
NES_LINE_T(template<typename Unit NST_COMMA uint Hold NST_COMMA uint Delay>,A12<Unit NST_COMMA Hold NST_COMMA Delay>,Signaled)
{
NST_COMPILE_ASSERT( Delay <= 8 );
uint prev = line;
line = address & 0x1000;
if (line > prev && filter.Clock(cycle) && unit.Clock())
cpu.DoIRQ( Cpu::IRQ_EXT, cycle + (Delay ? cpu.GetClock(Delay) : 0) );
}
}
}
}
#endif