104 lines
3.9 KiB
C#
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;
|
|
}
|
|
}
|
|
} |