BizHawk/waterbox/vb/timer.cpp

234 lines
5.0 KiB
C++

/******************************************************************************/
/* Mednafen Virtual Boy Emulation Module */
/******************************************************************************/
/* timer.cpp:
** Copyright (C) 2010-2016 Mednafen Team
**
** This program 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.
**
** This program 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 this program; if not, write to the Free Software Foundation, Inc.,
** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "vb.h"
#include "timer.h"
namespace MDFN_IEN_VB
{
#define TC_TENABLE 0x01
#define TC_ZSTAT 0x02
#define TC_ZSTATCLR 0x04
#define TC_TIMZINT 0x08
#define TC_TCLKSEL 0x10
static uint8 TimerControl;
static uint16 TimerReloadValue;
static uint16 TimerCounter;
static int32 TimerDivider;
static v810_timestamp_t TimerLastTS;
static bool TimerStatus, TimerStatusShadow;
static bool ReloadPending;
v810_timestamp_t TIMER_Update(v810_timestamp_t timestamp)
{
int32 run_time = timestamp - TimerLastTS;
if (TimerControl & TC_TENABLE)
{
TimerDivider -= run_time;
while (TimerDivider <= 0)
{
if (!TimerCounter || ReloadPending)
{
TimerCounter = TimerReloadValue;
ReloadPending = false;
}
if (TimerCounter)
TimerCounter--;
if (!TimerCounter || TimerStatus)
{
TimerStatusShadow = TimerStatus = true;
}
VBIRQ_Assert(VBIRQ_SOURCE_TIMER, TimerStatusShadow && (TimerControl & TC_TIMZINT));
TimerDivider += (TimerControl & TC_TCLKSEL) ? 500 : 2000;
}
}
TimerLastTS = timestamp;
return ((TimerControl & TC_TENABLE) ? (timestamp + TimerDivider) : VB_EVENT_NONONO);
}
void TIMER_ResetTS(void)
{
TimerLastTS = 0;
}
uint8 TIMER_Read(const v810_timestamp_t &timestamp, uint32 A)
{
uint8 ret = 0;
//if(A <= 0x1C)
//printf("Read: %d, %08x\n", timestamp, A);
TIMER_Update(timestamp);
switch (A & 0xFF)
{
case 0x18:
ret = TimerCounter;
break;
case 0x1C:
ret = TimerCounter >> 8;
break;
case 0x20:
ret = TimerControl | (0xE0 | TC_ZSTATCLR) | (TimerStatus ? TC_ZSTAT : 0);
break;
}
return (ret);
}
void TIMER_Write(const v810_timestamp_t &timestamp, uint32 A, uint8 V)
{
if (A & 0x3)
{
puts("HWCtrl Bogus Write?");
return;
}
TIMER_Update(timestamp);
//if((A & 0xFF) <= 0x1C)
//printf("Write: %d, %08x %02x\n", timestamp, A, V);
switch (A & 0xFF)
{
case 0x18:
TimerReloadValue &= 0xFF00;
TimerReloadValue |= V;
ReloadPending = true;
break;
case 0x1C:
TimerReloadValue &= 0x00FF;
TimerReloadValue |= V << 8;
ReloadPending = true;
break;
case 0x20:
if (V & TC_ZSTATCLR)
{
if ((TimerControl & TC_TENABLE) && TimerCounter == 0)
{
//puts("Faulty Z-Stat-Clr");
}
else
{
TimerStatus = false;
}
TimerStatusShadow = false;
}
if ((V & TC_TENABLE) && !(TimerControl & TC_TENABLE))
{
//TimerCounter = TimerReloadValue;
TimerDivider = (V & TC_TCLKSEL) ? 500 : 2000;
}
TimerControl = V & (0x10 | 0x08 | 0x01);
if (!(TimerControl & TC_TIMZINT))
TimerStatus = TimerStatusShadow = false;
VBIRQ_Assert(VBIRQ_SOURCE_TIMER, TimerStatusShadow && (TimerControl & TC_TIMZINT));
if (TimerControl & TC_TENABLE)
VB_SetEvent(VB_EVENT_TIMER, timestamp + TimerDivider);
break;
}
}
void TIMER_Power(void)
{
TimerLastTS = 0;
TimerCounter = 0xFFFF;
TimerReloadValue = 0;
TimerDivider = 2000; //2150; //2000;
TimerStatus = false;
TimerStatusShadow = false;
TimerControl = 0;
ReloadPending = false;
VBIRQ_Assert(VBIRQ_SOURCE_TIMER, false);
}
uint32 TIMER_GetRegister(const unsigned int id, char *special, const uint32 special_len)
{
uint32 ret = 0xDEADBEEF;
switch (id)
{
case TIMER_GSREG_TCR:
ret = TimerControl;
if (special)
trio_snprintf(special, special_len, "TEnable: %d, TimZInt: %d, TClkSel: %d(%.3f KHz)",
(int)(bool)(ret & TC_TENABLE),
(int)(bool)(ret & TC_TIMZINT),
(int)(bool)(ret & TC_TCLKSEL),
(double)VB_MASTER_CLOCK / ((ret & TC_TCLKSEL) ? 500 : 2000) / 1000);
break;
case TIMER_GSREG_DIVCOUNTER:
ret = TimerDivider;
break;
case TIMER_GSREG_RELOAD_VALUE:
ret = TimerReloadValue;
break;
case TIMER_GSREG_COUNTER:
ret = TimerCounter;
break;
}
return (ret);
}
void TIMER_SetRegister(const unsigned int id, const uint32 value)
{
switch (id)
{
case TIMER_GSREG_TCR:
TimerControl = value & (TC_TENABLE | TC_TIMZINT | TC_TCLKSEL);
break;
case TIMER_GSREG_DIVCOUNTER:
TimerDivider = value % ((TimerControl & TC_TCLKSEL) ? 500 : 2000);
break;
case TIMER_GSREG_RELOAD_VALUE:
TimerReloadValue = value;
break;
case TIMER_GSREG_COUNTER:
TimerCounter = value;
break;
}
}
}