BizHawk/BizHawk.Emulation/Sound/YM2612.Timers.cs

104 lines
3.9 KiB
C#

using System;
namespace BizHawk.Emulation.Sound
{
// The master clock on the genesis is 53,693,175 MCLK / sec (NTSC)
// 53,203,424 MCLK / sec (PAL)
// 7,670,454 68K cycles / sec (7 MCLK divisor)
// 3,579,545 Z80 cycles / sec (15 MCLK divisor)
// YM2612 is fed by EXT CLOCK: 7,670,454 ECLK / sec (NTSC)
// (Same clock on 68000) 7,600,489 ECLK / sec (PAL)
// YM2612 has /6 divisor on the EXT CLOCK.
// YM2612 takes 24 cycles to generate a sample. 6*24 = 144. This is where the /144 divisor comes from.
// YM2612 native output rate is 7670454 / 144 = 53267 hz (NTSC), 52781 hz (PAL)
// Timer A ticks at the native output rate (53267 times per second for NTSC).
// Timer B ticks down with a /16 divisor. (3329 times per second for NTSC).
// Ergo, Timer A ticks every 67.2 Z80 cycles. Timer B ticks every 1075.2 Z80 cycles.
public partial class YM2612
{
const float timerAZ80Factor = 67.2f;
const float timerBZ80Factor = 1075.2f;
int TimerAPeriod, TimerBPeriod;
bool TimerATripped, TimerBTripped;
int TimerAResetClock, TimerBResetClock;
int TimerALastReset, TimerBLastReset;
byte TimerControl27;
bool TimerALoad { get { return (TimerControl27 & 1) != 0; } }
bool TimerBLoad { get { return (TimerControl27 & 2) != 0; } }
bool TimerAEnable { get { return (TimerControl27 & 4) != 0; } }
bool TimerBEnable { get { return (TimerControl27 & 8) != 0; } }
bool TimerAReset { get { return (TimerControl27 & 16) != 0; } }
bool TimerBReset { get { return (TimerControl27 & 32) != 0; } }
void InitTimers()
{
TimerAResetClock = 68812;
TimerBResetClock = 275200;
}
void UpdateTimers(int clock)
{
int elapsedCyclesSinceLastTimerAReset = clock - TimerALastReset;
if (elapsedCyclesSinceLastTimerAReset > TimerAResetClock)
{
if (TimerAEnable)
TimerATripped = true;
int numTimesTripped = elapsedCyclesSinceLastTimerAReset / TimerAResetClock;
TimerALastReset += (TimerAResetClock * numTimesTripped);
}
int elapsedCyclesSinceLastTimerBReset = clock - TimerBLastReset;
if (elapsedCyclesSinceLastTimerBReset > TimerBResetClock)
{
if (TimerBEnable)
TimerBTripped = true;
int numTimesTripped = elapsedCyclesSinceLastTimerBReset / TimerBResetClock;
TimerBLastReset += (TimerBResetClock * numTimesTripped);
}
}
void WriteTimerA_MSB_24(byte value, int clock)
{
TimerAPeriod = (value << 2) | (TimerAPeriod & 3);
TimerAResetClock = (int)((1024 - TimerAPeriod) * timerAZ80Factor);
}
void WriteTimerA_LSB_25(byte value, int clock)
{
TimerAPeriod = (TimerAPeriod & 0x3FC) | (value & 3);
TimerAResetClock = (int)((1024 - TimerAPeriod) * timerAZ80Factor);
}
void WriteTimerB_26(byte value, int clock)
{
TimerBPeriod = value;
TimerBResetClock = (int)((256 - TimerBPeriod) * timerBZ80Factor);
}
void WriteTimerControl_27(byte value, int clock)
{
bool lagALoad = TimerALoad;
bool lagBLoad = TimerBLoad;
TimerControl27 = value;
if (!lagALoad && TimerALoad)
TimerALastReset = clock;
if (!lagBLoad && TimerBLoad)
TimerBLastReset = clock;
if (TimerAReset) TimerATripped = false;
if (TimerBReset) TimerBTripped = false;
}
}
}