mirror of
https://github.com/libretro/snes9x2005.git
synced 2024-11-23 16:29:43 +00:00
480 lines
13 KiB
C
480 lines
13 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()
|
|
{
|
|
rtc.index = -1;
|
|
rtc.mode = MODE_READ;
|
|
}
|
|
|
|
void S9xHardResetSRTC()
|
|
{
|
|
memset(&rtc, 0, sizeof(rtc));
|
|
rtc.index = -1;
|
|
rtc.mode = MODE_READ;
|
|
rtc.count_enable = false;
|
|
rtc.needs_init = true;
|
|
|
|
// Get system timestamp
|
|
rtc.system_timestamp = time(NULL);
|
|
}
|
|
|
|
/**********************************************************************************************/
|
|
/* S9xSRTCComputeDayOfWeek() */
|
|
/* Return 0-6 for Sunday-Saturday */
|
|
/**********************************************************************************************/
|
|
uint32_t S9xSRTCComputeDayOfWeek()
|
|
{
|
|
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 indicies
|
|
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)
|
|
{
|
|
int32_t mdays;
|
|
|
|
switch (month)
|
|
{
|
|
case 2:
|
|
if ((year % 4 == 0)) // DKJM2 only uses 199x - 22xx
|
|
mdays = 29;
|
|
else
|
|
mdays = 28;
|
|
break;
|
|
|
|
case 4:
|
|
case 6:
|
|
case 9:
|
|
case 11:
|
|
mdays = 30;
|
|
break;
|
|
|
|
default: // months 1,3,5,7,8,10,12
|
|
mdays = 31;
|
|
break;
|
|
}
|
|
|
|
return mdays;
|
|
}
|
|
|
|
|
|
#define DAYTICKS (60*60*24)
|
|
#define HOURTICKS (60*60)
|
|
#define MINUTETICKS 60
|
|
|
|
|
|
/**********************************************************************************************/
|
|
/* S9xUpdateSrtcTime() */
|
|
/* Advance the S-RTC time if counting is enabled */
|
|
/**********************************************************************************************/
|
|
void S9xUpdateSrtcTime()
|
|
{
|
|
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.
|
|
// I originally tried using mktime and localtime library functions to keep track
|
|
// of time but some of the GNU time functions fail when the year goes to 2099
|
|
// (and maybe less) and this would have caused a bug with DKJM2 so I'm doing
|
|
// it this way to get around that problem.
|
|
|
|
// Note: Dai Kaijyu Monogatari II only allows dates in the range 1996-21xx.
|
|
|
|
if (rtc.count_enable && !rtc.needs_init)
|
|
{
|
|
cur_systime = time(NULL);
|
|
|
|
// This method assumes one time_t clock tick is one second
|
|
// which should work on PCs and GNU systems.
|
|
// If your tick interval is different adjust the
|
|
// DAYTICK, HOURTICK, and MINUTETICK defines
|
|
|
|
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() */
|
|
/* This function sends data to the S-RTC used in Dai Kaijyu Monogatari II */
|
|
/**********************************************************************************************/
|
|
void S9xSetSRTC(uint8_t data, uint16_t 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:
|
|
// Ignore the write if it's an 0xF ???
|
|
// Probably should switch back to read mode -- but this
|
|
// sequence never occurs in DKJM2
|
|
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
|
|
|
|
// Get the day of the week
|
|
rtc.data[rtc.index++] = S9xSRTCComputeDayOfWeek();
|
|
|
|
// Start RTC counting again
|
|
rtc.count_enable = true;
|
|
rtc.needs_init = false;
|
|
}
|
|
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// Attempting to write too much data
|
|
// error(); // ignore??
|
|
}
|
|
}
|
|
else if (rtc.mode == MODE_COMMAND)
|
|
{
|
|
switch (data)
|
|
{
|
|
case COMMAND_CLEAR_RTC:
|
|
// Disable RTC counter
|
|
rtc.count_enable = false;
|
|
|
|
memset(rtc.data, 0, MAX_RTC_INDEX + 1);
|
|
rtc.index = -1;
|
|
rtc.mode = MODE_COMMAND_DONE;
|
|
break;
|
|
|
|
case COMMAND_LOAD_RTC:
|
|
// Disable RTC counter
|
|
rtc.count_enable = false;
|
|
|
|
rtc.index = 0; // Setup for writing
|
|
rtc.mode = MODE_LOAD_RTC;
|
|
break;
|
|
|
|
default:
|
|
rtc.mode = MODE_COMMAND_DONE;
|
|
// unrecognized command - need to implement.
|
|
}
|
|
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if (rtc.mode == MODE_READ)
|
|
{
|
|
// Attempting to write while in read mode. Ignore.
|
|
}
|
|
|
|
if (rtc.mode == MODE_COMMAND_DONE)
|
|
{
|
|
// Maybe this isn't an error. Maybe we should kick off
|
|
// a new E command. But is this valid?
|
|
}
|
|
}
|
|
}
|
|
|
|
/**********************************************************************************************/
|
|
/* S9xGetSRTC() */
|
|
/* This function retrieves data from the S-RTC */
|
|
/**********************************************************************************************/
|
|
uint8_t S9xGetSRTC(uint16_t 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
|
|
{
|
|
// Feed out the data
|
|
return rtc.data[rtc.index++];
|
|
}
|
|
}
|
|
else
|
|
return 0x0;
|
|
}
|
|
|
|
void S9xSRTCPreSaveState()
|
|
{
|
|
if (Settings.SRTC)
|
|
{
|
|
S9xUpdateSrtcTime();
|
|
|
|
int32_t 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();
|
|
}
|
|
}
|