mirror of
https://github.com/0ldsk00l/nestopia.git
synced 2025-04-02 10:31:51 -04:00
284 lines
5.7 KiB
C++
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
|