snes9x2005/source/srtc.c

410 lines
12 KiB
C

#include "../copyright"
#include <string.h>
#include "snes9x.h"
#include "srtc.h"
#include "memmap.h"
/*** The format of the rtc_data structure is:
Index Description Range (nibble)
----- -------------- ---------------------------------------
0 Seconds low 0-9
1 Seconds high 0-5
2 Minutes low 0-9
3 Minutes high 0-5
4 Hour low 0-9
5 Hour high 0-2
6 Day low 0-9
7 Day high 0-3
8 Month 1-C (0xC is December, 12th month)
9 Year ones 0-9
A Year tens 0-9
B Year High 9-B (9=19xx, A=20xx, B=21xx)
C Day of week 0-6 (0=Sunday, 1=Monday,...,6=Saturday)
***/
SRTC_DATA rtc;
static int32_t month_keys[12] = { 1, 4, 4, 0, 2, 5, 0, 3, 6, 1, 4, 6 };
/*********************************************************************************************
* Note, if you are doing a save state for this game:
*
* On save:
*
* Call S9xUpdateSrtcTime and save the rtc data structure.
*
* On load:
*
* restore the rtc data structure
* rtc.system_timestamp = time (NULL);
*********************************************************************************************/
void S9xResetSRTC(void)
{
rtc.index = -1;
rtc.mode = MODE_READ;
}
void S9xHardResetSRTC(void)
{
memset(&rtc, 0, sizeof(rtc));
rtc.index = -1;
rtc.mode = MODE_READ;
rtc.count_enable = false;
rtc.needs_init = true;
rtc.system_timestamp = time(NULL); /* Get system timestamp */
}
/**********************************************************************************************/
/* S9xSRTCComputeDayOfWeek(void) */
/* Return 0-6 for Sunday-Saturday */
/**********************************************************************************************/
uint32_t S9xSRTCComputeDayOfWeek(void)
{
uint32_t year = rtc.data[10] * 10 + rtc.data[9];
uint32_t month = rtc.data[8];
uint32_t day = rtc.data[7] * 10 + rtc.data[6];
uint32_t day_of_week;
year += (rtc.data[11] - 9) * 100;
/* Range check the month for valid array indices */
if (month > 12)
month = 1;
day_of_week = year + (year / 4) + month_keys[month - 1] + day - 1;
if ((year % 4 == 0) && (month <= 2))
day_of_week--;
day_of_week %= 7;
return day_of_week;
}
/**********************************************************************************************/
/* S9xSRTCDaysInMonth() */
/* Return the number of days in a specific month for a certain year */
/**********************************************************************************************/
int32_t S9xSRTCDaysInMmonth(int32_t month, int32_t year)
{
switch(month)
{
case 2:
if((year % 4 == 0)) /* DKJM2 only uses 199x - 22xx */
return 29;
return 28;
case 4:
case 6:
case 9:
case 11:
return 30;
default:
return 31;
}
}
#define MINUTETICKS 60
#define HOURTICKS (60 * MINUTETICKS)
#define DAYTICKS (24 * HOURTICKS)
/**********************************************************************************************/
/* S9xUpdateSrtcTime() */
/* Advance the S-RTC time if counting is enabled */
/**********************************************************************************************/
void S9xUpdateSrtcTime(void)
{
time_t cur_systime;
int32_t time_diff;
/* Keep track of game time by computing the number of seconds that pass on the system */
/* clock and adding the same number of seconds to the S-RTC clock structure. */
/* Note: Dai Kaijyu Monogatari II only allows dates in the range 1996-21xx. */
if (rtc.count_enable && !rtc.needs_init)
{
cur_systime = time(NULL);
time_diff = (int32_t)(cur_systime - rtc.system_timestamp);
rtc.system_timestamp = cur_systime;
if (time_diff > 0)
{
int32_t seconds;
int32_t minutes;
int32_t hours;
int32_t days;
int32_t month;
int32_t year;
int32_t temp_days;
int32_t year_hundreds;
int32_t year_tens;
int32_t year_ones;
if (time_diff > DAYTICKS)
{
days = time_diff / DAYTICKS;
time_diff = time_diff - days * DAYTICKS;
}
else
days = 0;
if (time_diff > HOURTICKS)
{
hours = time_diff / HOURTICKS;
time_diff = time_diff - hours * HOURTICKS;
}
else
hours = 0;
if (time_diff > MINUTETICKS)
{
minutes = time_diff / MINUTETICKS;
time_diff = time_diff - minutes * MINUTETICKS;
}
else
minutes = 0;
if (time_diff > 0)
seconds = time_diff;
else
seconds = 0;
seconds += (rtc.data[1] * 10 + rtc.data[0]);
if (seconds >= 60)
{
seconds -= 60;
minutes += 1;
}
minutes += (rtc.data[3] * 10 + rtc.data[2]);
if (minutes >= 60)
{
minutes -= 60;
hours += 1;
}
hours += (rtc.data[5] * 10 + rtc.data[4]);
if (hours >= 24)
{
hours -= 24;
days += 1;
}
if (days > 0)
{
year = rtc.data[10] * 10 + rtc.data[9];
year += (1000 + rtc.data[11] * 100);
month = rtc.data[8];
days += (rtc.data[7] * 10 + rtc.data[6]);
while (days > (temp_days = S9xSRTCDaysInMmonth(month, year)))
{
days -= temp_days;
month += 1;
if (month > 12)
{
year += 1;
month = 1;
}
}
year_tens = year % 100;
year_ones = year_tens % 10;
year_tens /= 10;
year_hundreds = (year - 1000) / 100;
rtc.data[6] = days % 10;
rtc.data[7] = days / 10;
rtc.data[8] = month;
rtc.data[9] = year_ones;
rtc.data[10] = year_tens;
rtc.data[11] = year_hundreds;
rtc.data[12] = S9xSRTCComputeDayOfWeek();
}
rtc.data[0] = seconds % 10;
rtc.data[1] = seconds / 10;
rtc.data[2] = minutes % 10;
rtc.data[3] = minutes / 10;
rtc.data[4] = hours % 10;
rtc.data[5] = hours / 10;
return;
}
}
}
/**********************************************************************************************/
/* S9xSetSRTC(void) */
/* This function sends data to the S-RTC used in Dai Kaijyu Monogatari II */
/**********************************************************************************************/
void S9xSetSRTC(uint8_t data, uint16_t Address)
{
(void) Address;
data &= 0x0F; /* Data is only 4-bits, mask out unused bits. */
if (data >= 0xD) /* It's an RTC command */
{
switch (data)
{
case 0xD:
rtc.mode = MODE_READ;
rtc.index = -1;
break;
case 0xE:
rtc.mode = MODE_COMMAND;
break;
default:
break;
}
return;
}
if (rtc.mode == MODE_LOAD_RTC)
{
if ((rtc.index >= 0) || (rtc.index < MAX_RTC_INDEX))
{
rtc.data[rtc.index++] = data;
if (rtc.index == MAX_RTC_INDEX) /* We have all the data for the RTC load */
{
rtc.system_timestamp = time(NULL); /* Get local system time */
rtc.data[rtc.index++] = S9xSRTCComputeDayOfWeek(); /* Get the day of the week */
/* Start RTC counting again */
rtc.count_enable = true;
rtc.needs_init = false;
}
return;
}
}
else if (rtc.mode == MODE_COMMAND)
{
switch (data)
{
case COMMAND_CLEAR_RTC:
rtc.count_enable = false; /* Disable RTC counter */
memset(rtc.data, 0, MAX_RTC_INDEX + 1);
rtc.index = -1;
rtc.mode = MODE_COMMAND_DONE;
break;
case COMMAND_LOAD_RTC:
rtc.count_enable = false; /* Disable RTC counter */
rtc.index = 0; /* Setup for writing */
rtc.mode = MODE_LOAD_RTC;
break;
default:
rtc.mode = MODE_COMMAND_DONE; /* unrecognized command - need to implement. */
}
return;
}
}
/**********************************************************************************************/
/* S9xGetSRTC() */
/* This function retrieves data from the S-RTC */
/**********************************************************************************************/
uint8_t S9xGetSRTC(uint16_t Address)
{
(void) Address;
if (rtc.mode == MODE_READ)
{
if (rtc.index < 0)
{
S9xUpdateSrtcTime(); /* Only update it if the game reads it */
rtc.index++;
return 0x0f; /* Send start marker. */
}
else if (rtc.index > MAX_RTC_INDEX)
{
rtc.index = -1; /* Setup for next set of reads */
return 0x0f; /* Data done marker. */
}
else
return rtc.data[rtc.index++]; /* Feed out the data */
}
else
return 0x0;
}
void S9xSRTCPreSaveState()
{
if (Settings.SRTC)
{
int32_t s;
S9xUpdateSrtcTime();
s = Memory.SRAMSize ? (1 << (Memory.SRAMSize + 3)) * 128 : 0;
if (s > 0x20000)
s = 0x20000;
Memory.SRAM [s + 0] = rtc.needs_init;
Memory.SRAM [s + 1] = rtc.count_enable;
/* memmove converted: Different mallocs [Neb] */
memcpy(&Memory.SRAM [s + 2], rtc.data, MAX_RTC_INDEX + 1);
Memory.SRAM [s + 3 + MAX_RTC_INDEX] = rtc.index;
Memory.SRAM [s + 4 + MAX_RTC_INDEX] = rtc.mode;
#ifdef MSB_FIRST
Memory.SRAM [s + 5 + MAX_RTC_INDEX] = (uint8_t)(rtc.system_timestamp >> 0);
Memory.SRAM [s + 6 + MAX_RTC_INDEX] = (uint8_t)(rtc.system_timestamp >> 8);
Memory.SRAM [s + 7 + MAX_RTC_INDEX] = (uint8_t)(rtc.system_timestamp >> 16);
Memory.SRAM [s + 8 + MAX_RTC_INDEX] = (uint8_t)(rtc.system_timestamp >> 24);
Memory.SRAM [s + 9 + MAX_RTC_INDEX] = (uint8_t)(rtc.system_timestamp >> 32);
Memory.SRAM [s + 10 + MAX_RTC_INDEX] = (uint8_t)(rtc.system_timestamp >> 40);
Memory.SRAM [s + 11 + MAX_RTC_INDEX] = (uint8_t)(rtc.system_timestamp >> 48);
Memory.SRAM [s + 12 + MAX_RTC_INDEX] = (uint8_t)(rtc.system_timestamp >> 56);
#else
/* memmove converted: Different mallocs [Neb] */
memcpy(&Memory.SRAM [s + 5 + MAX_RTC_INDEX], &rtc.system_timestamp, 8);
#endif
}
}
void S9xSRTCPostLoadState()
{
if (Settings.SRTC)
{
int32_t s = Memory.SRAMSize ? (1 << (Memory.SRAMSize + 3)) * 128 : 0;
if (s > 0x20000)
s = 0x20000;
rtc.needs_init = Memory.SRAM [s + 0];
rtc.count_enable = Memory.SRAM [s + 1];
/* memmove converted: Different mallocs [Neb] */
memcpy(rtc.data, &Memory.SRAM [s + 2], MAX_RTC_INDEX + 1);
rtc.index = Memory.SRAM [s + 3 + MAX_RTC_INDEX];
rtc.mode = Memory.SRAM [s + 4 + MAX_RTC_INDEX];
#ifdef MSB_FIRST
rtc.system_timestamp |= (Memory.SRAM [s + 5 + MAX_RTC_INDEX] << 0);
rtc.system_timestamp |= (Memory.SRAM [s + 6 + MAX_RTC_INDEX] << 8);
rtc.system_timestamp |= (Memory.SRAM [s + 7 + MAX_RTC_INDEX] << 16);
rtc.system_timestamp |= (Memory.SRAM [s + 8 + MAX_RTC_INDEX] << 24);
rtc.system_timestamp |= (Memory.SRAM [s + 9 + MAX_RTC_INDEX] << 32);
rtc.system_timestamp |= (Memory.SRAM [s + 10 + MAX_RTC_INDEX] << 40);
rtc.system_timestamp |= (Memory.SRAM [s + 11 + MAX_RTC_INDEX] << 48);
rtc.system_timestamp |= (Memory.SRAM [s + 12 + MAX_RTC_INDEX] << 56);
#else
/* memmove converted: Different mallocs [Neb] */
memcpy(&rtc.system_timestamp, &Memory.SRAM [s + 5 + MAX_RTC_INDEX], 8);
#endif
S9xUpdateSrtcTime();
}
}