
#include "shared.h"



/**
 * SMPC clock acquisition test.
 * Retrieve Saturn internal clock value.
 * Note: Doesn't uses SMPC interrupt. Instead, SMPCregister is polled during acquisition.
 * 
 * For details about clock value acquisition, see information below (taken from Sega docs).
 * --- ST-169-R1-072694.pdf pages 13-16: ---
 *
 *  Table 2.4 Command Issue Types
 *    Type    Command
 *      A   MSHON, SYSRES, NMIREQ, CKCHG352, CKCHG320
 *      B   SSHON, SSHOFF, SNDON, SNDOFF, CDON, CDOFF, RESENAB, RESDISA
 *      C   SETTIME, SETSMEM
 *      D   INTBACK
 *
 *  Figure 2.4 Type D Command Flow
 *      1. START
 *      2. Waits for SF to be Reset
 *      3. Sets the Command Parameter in IREG
 *      4. Writes Command Code in COMREG
 *      5. Set SF
 *      6. END
 *
 *      1. SMPC Interrupt Processing START
 *      2. Clear Interrupt Flag
 *      3. Look at SR and Receive OREG Result Parameter
 *      4. Depending on Peripheral Status,
 *         Issue Continue or Break to SMPC
 *         (Refer to Chapter 3 for Details)
 *      5. Interrupt Processing END
 *  Note:
 *   A routine similar to the SMPC interrupt routine can be executed by masking the
 *   SMPC routine (SMPC interrupt is not used) and conducting polling after the SMPC
 *   interrupt flag or SF clear is conducted.
**/
#include <time.h>
/* Acquisition timeout value, in msec. */
#define SC_RTC_TIMEOUT 5000
#define SC_RTC_CNTR 2
/* macro BCD_INT : convert bcd to int */
#define BCD_INT(bcd) (((bcd & 0xf0) >> 4) * 10 + (bcd &0x0f))
unsigned long smpc_clock_test(time_t* unix_tm, struct tm * t)
{
    BupDate sat_date;
    time_t unix_time = 0;
    unsigned long saturn_time = 0;
    struct tm time_dummy;
    unsigned long cntr = 0;
    unsigned long cntr_prev = cntr;
    unsigned long cntr_log[SC_RTC_CNTR];
    unsigned char oreg_local[7];
    int i, tmp;
    int cntr_id = 0;
    char * weekday[] = { "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"};

    if(t == NULL) t = &time_dummy;

    for(i=0; i<SC_RTC_CNTR; i++)
    {
        cntr_log[i] = 0;
    }

    /* Waits for SF to be Reset. */
    do { sc_usleep(1000); cntr++; if(cntr > SC_RTC_TIMEOUT) return saturn_time; } while(SF == 1);
    cntr_log[cntr_id++] = cntr-cntr_prev; cntr_prev = cntr;

    /* Sets the Command Parameter in IREG. */
    IREG0 = 0x01;
    IREG1 = 0x08;
    IREG2 = 0xF0;

    /* Writes Command Code in COMREG. */
    COMREG = 0x10;

    /* Set SF. */
    SF = 1;


    /* Wait SF Clear. */
    do { sc_usleep(1000); cntr++; if(cntr > SC_RTC_TIMEOUT) return saturn_time; } while(SF == 1);
    cntr_log[cntr_id++] = cntr-cntr_prev; cntr_prev = cntr;

    _printf(2, 10, COLOR_WHITE, "SR: 0x%02X SF: 0x%02X (%4d %4d)", SR, SF, cntr_log[0], cntr_log[1]);
    _printf(2, 11, COLOR_WHITE, "OREG0,8,31 = %02X %02X %02X", OREG0, OREG8, OREG31);

    /* Get register values from SMPC. */
    oreg_local[0] = OREG1;
    oreg_local[1] = OREG2;
    oreg_local[2] = OREG3;
    oreg_local[3] = OREG4;
    oreg_local[4] = OREG5;
    oreg_local[5] = OREG6;
    oreg_local[6] = OREG7;

    /* The number of years since 1900. */
    tmp = (BCD_INT(oreg_local[0]) * 100 + BCD_INT(oreg_local[1])) - 1900;
    t->tm_year = tmp;

    /* The number of months since January, in the range 0 to 11. */
    tmp = (oreg_local[2] & 0x0F) - 1;
    t->tm_mon  = tmp;

    /* The day of the month, in the range 1 to 31. */
    tmp = BCD_INT(oreg_local[3]);
    t->tm_mday = tmp;

    /* The number of hours past midnight, in the range 0 to 23. */
    tmp = BCD_INT(oreg_local[4]);
    t->tm_hour = tmp;

    /* The number of minutes after the hour, in the range 0 to 59. */
    tmp = BCD_INT(oreg_local[5]);
    t->tm_min  = tmp;

    /* The number of seconds after the minute, normally in the range 0 to 59, but can be up to 60 to allow for leap seconds. */
    tmp = BCD_INT(oreg_local[6]);
    t->tm_sec  = tmp;

    /* No information about daylight saving. */
    t->tm_isdst = -1;

    //tm_wday: will be set by mktime
    //tm_tm_yday: will be set by mktime

    _printf(2, 12, COLOR_WHITE, "OREG1 = 0x%02X YR=%3d", oreg_local[0], t->tm_year);
    _printf(2, 13, COLOR_WHITE, "OREG2 = 0x%02X", oreg_local[1]);
    _printf(2, 14, COLOR_WHITE, "OREG3 = 0x%02X MN=%2d", oreg_local[2], t->tm_mon);
    _printf(2, 15, COLOR_WHITE, "OREG4 = 0x%02X DY=%2d", oreg_local[3], t->tm_mday);
    _printf(2, 16, COLOR_WHITE, "OREG5 = 0x%02X HR=%2d", oreg_local[4], t->tm_hour);
    _printf(2, 17, COLOR_WHITE, "OREG6 = 0x%02X MI=%2d", oreg_local[5], t->tm_min);
    _printf(2, 18, COLOR_WHITE, "OREG7 = 0x%02X SC=%2d", oreg_local[6], t->tm_sec);

    /* Convert date to unix time. */
    unix_time = mktime(t);
    if(unix_tm != NULL) *unix_tm = unix_time;
    _printf(2, 19, COLOR_YELLOW, "Unix time   = %10d", unix_time);

    /* Convert date to Saturn date. */
//typedef struct BupDate {
//	Uint8	year;		/* Year - 1980 */
//	Uint8	month;		/* Month (1~12) */
//	Uint8	day;		/* Day (1~31) */
//	Uint8	time;		/* Hour (0~23) */
//	Uint8	min;		/* Minute (0~59) */
//	Uint8	week;		/* Weekday: (Sunday:0 - Saturday:6) */
//}
    sat_date.year  = t->tm_year - 80;
    sat_date.month = t->tm_mon + 1;
    sat_date.day   = t->tm_mday;
    sat_date.time  = t->tm_hour;
    sat_date.min   = (t->tm_hour >= 60 ? 59 : t->tm_min);
    sat_date.week  = t->tm_wday;

    _printf(2, 20, COLOR_YELLOW, "Time: %4d/%02d/%02d %2d:%02d (%s)  ", 
        sat_date.year + 1980, sat_date.month, sat_date.day, 
        sat_date.time, sat_date.min, weekday[sat_date.week]);

    saturn_time = BUP_SetDate(&sat_date);
    _printf(2, 21, COLOR_YELLOW, "Saturn time = %10d", saturn_time);
    _printf(2, 22, COLOR_YELLOW, "TAYST       = %10d", sc_tim_getcurrent(NULL, NULL));


    return saturn_time;
}



void test_smpc_rtc(void)
{
    /* Test SMPC Real Time Clock. */
    _printf(2, 24, COLOR_WHITE, "Press A or B to test RTC !");
    _printf(2, 25, COLOR_WHITE, "Press C or START to end.");
    unsigned short frame_count = 0;
    for(;;)
    {
        frame_count++; if(frame_count > 100) frame_count = 0;

        sc_check();
        pad_read(&pad);
        if((pad.delta & PAD_A) || (pad.new & PAD_B))
        {
            _printf(2, 8, COLOR_WHITE, "RTC(%3d) = %3d", frame_count, smpc_clock_test(NULL, NULL));
        }
        if(pad.new & PAD_C)
        {
            _printf(2, 22, COLOR_YELLOW, "TAYST       = %10d", sc_tim_getcurrent(NULL, NULL));
        }

        if((pad.new & PAD_UP) || (pad.new & PAD_DOWN) || (pad.new & PAD_LEFT) || (pad.new & PAD_RIGHT))
        {
            break;
        }
        if(pad.new & PAD_START)
        { /* Exit to multiplayer. */
            sc_exit_to_multiplayer();
        }
    }
}

