/*

  machine/model3.c: functions emulating various aspects of the Model 3 hardware

*/

#include "driver.h"
#include <time.h>

static int m3_step;

/******************************************************************/
/* Real3D TAP Port                                                */
/******************************************************************/

/*
 * TAP Finite State Machine
 *
 * Y are states and X are outgoing paths. Constructed from information on page
 * 167 of the 3D-RAM manual.
 */

#define NEXT(new_state) fsm[state][new_state]

static INT32  state;  // current state
static INT32  fsm[][2] =  {
                            {  1,  0 },  // 0  Test-Logic/Reset
                            {  1,  2 },  // 1  Run-Test/Idle
                            {  3,  9 },  // 2  Select-DR-Scan
                            {  4,  5 },  // 3  Capture-DR
                            {  4,  5 },  // 4  Shift-DR
                            {  6,  8 },  // 5  Exit1-DR
                            {  6,  7 },  // 6  Pause-DR
                            {  4,  8 },  // 7  Exit2-DR
                            {  1,  2 },  // 8  Update-DR
                            { 10,  0 },  // 9  Select-IR-Scan
                            { 11, 12 },  // 10 Capture-IR
                            { 11, 12 },  // 11 Shift-IR
                            { 13, 15 },  // 12 Exit1-IR
                            { 13, 14 },  // 13 Pause-IR
                            { 11, 15 },  // 14 Exit2-IR
                            {  1,  2 }   // 15 Update-IR
                        };

/*
 * TAP Registers
 */

static UINT64   current_instruction;    // latched IR (not always equal to IR)
static UINT64   ir;                     // instruction register (46 bits)

static UINT8    id_data[32];            // ASIC ID code data buffer
static INT32      id_size;                // size of ID data in bits
//static INT32      ptr;                    // current bit ptr for data

static int      tdo;                    // bit shifted out to TDO

/*
 * insert_bit():
 *
 * Inserts a bit into an arbitrarily long bit field. Bit 0 is assumed to be
 * the MSB of the first byte in the buffer.
 */

static void insert_bit(UINT8 *buf, INT32 bit_num, INT32 bit)
{
    INT32 bit_in_byte;

    bit_in_byte = 7 - (bit_num & 7);

    buf[bit_num / 8] &= ~(1 << bit_in_byte);
    buf[bit_num / 8] |= (bit << bit_in_byte);
}

/*
 * insert_id():
 *
 * Inserts a 32-bit ID code into the ID bit field.
 */

static void insert_id(UINT32 id, INT32 start_bit)
{
    INT32 i;

    for (i = 31; i >= 0; i--)
        insert_bit(id_data, start_bit++, (id >> i) & 1);
}

/*
 * shift():
 *
 * Shifts the data buffer right (towards LSB at byte 0) by 1 bit. The size of
 * the number of bits must be specified. The bit shifted out of the LSB is
 * returned.
 */

static int shift(UINT8 *data, INT32 num_bits)
{
    INT32     i;
    int    shift_out, shift_in;

    /*
     * This loop takes care of all the fully-filled bytes
     */

    shift_in = 0;
	shift_out = 0;
    for (i = 0; i < num_bits / 8; i++)
    {
        shift_out = data[i] & 1;
        data[i] >>= 1;
        data[i] |= (shift_in << 7);
        shift_in = shift_out;   // carry over to next element's MSB
    }

    /*
     * Take care of the last partial byte (if there is one)
     */

    if ((num_bits & 7) != 0)
    {
        shift_out = (data[i] >> (8 - (num_bits & 7))) & 1;
        data[i] >>= 1;
        data[i] |= (shift_in << 7);
    }

    return shift_out;
}

/*
 * int tap_read(void);
 *
 * Reads TDO.
 *
 * Returns:
 *      TDO.
 */

int model3_tap_read(void)
{
    return tdo;
}

/*
 * void tap_write(int tck, int tms, int tdi, int trst);
 *
 * Writes to the TAP. State changes only occur on the rising edge of the clock
 * (tck = 1.)
 *
 * Parameters:
 *      tck  = Clock.
 *      tms  = Test mode select.
 *      tdi  = Serial data input. Must be 0 or 1 only!
 *      trst = Reset.
 */

void model3_tap_write(int tck, int tms, int tdi, int trst)
{
    if (!tck)
        return;

    state = NEXT(tms);

    switch (state)
    {
    case 3:     // Capture-DR

        /*
         * Read ASIC IDs.
         *
         * The ID Sequence is:
         *  - Jupiter
         *  - Mercury
         *  - Venus
         *  - Earth
         *  - Mars
         *  - Mars (again)
         *
         * Note that different Model 3 steps have different chip
         * revisions, hence the different IDs returned below.
         *
         * On Step 1.5 and 1.0, instruction 0x0C631F8C7FFE is used to retrieve
         * the ID codes but Step 2.0 is a little weirder. It seems to use this
         * and either the state of the TAP after reset or other instructions
         * to read the IDs as well. This can be emulated in one of 2 ways:
         * Ignore the instruction and always load up the data or load the
         * data on TAP reset and when the instruction is issued.
         */

        if (m3_step == 0x10)
        {
            insert_id(0x116C7057, 1 + 0 * 32);
            insert_id(0x216C3057, 1 + 1 * 32);
            insert_id(0x116C4057, 1 + 2 * 32);
            insert_id(0x216C5057, 1 + 3 * 32);
            insert_id(0x116C6057, 1 + 4 * 32 + 1);
            insert_id(0x116C6057, 1 + 5 * 32 + 1);
        }
        else if (m3_step == 0x15)
        {
            insert_id(0x316C7057, 1 + 0 * 32);
            insert_id(0x316C3057, 1 + 1 * 32);
            insert_id(0x216C4057, 1 + 2 * 32);      // Lost World may to use 0x016C4057
            insert_id(0x316C5057, 1 + 3 * 32);
            insert_id(0x216C6057, 1 + 4 * 32 + 1);
            insert_id(0x216C6057, 1 + 5 * 32 + 1);
        }
        else if (m3_step >= 0x20)
        {
            insert_id(0x416C7057, 1 + 0 * 32);
            insert_id(0x416C3057, 1 + 1 * 32);
            insert_id(0x316C4057, 1 + 2 * 32);
            insert_id(0x416C5057, 1 + 3 * 32);
            insert_id(0x316C6057, 1 + 4 * 32 + 1);
            insert_id(0x316C6057, 1 + 5 * 32 + 1);
        }

        break;

    case 4:     // Shift-DR

        tdo = shift(id_data, id_size);
        break;

    case 10:    // Capture-IR

        /*
         * Load lower 2 bits with 01 as per IEEE 1149.1-1990
         */

        ir = 1;
        break;

    case 11:    // Shift-IR

        /*
         * Shift IR towards output and load in new data from TDI
         */

        tdo = ir & 1;   // shift LSB to output
        ir >>= 1;
        ir |= ((UINT64) tdi << 45);
        break;

    case 15:    // Update-IR

        /*
         * Latch IR (technically, this should occur on the falling edge of
         * TCK)
         */

        ir &= U64(0x3fffffffffff);
        current_instruction = ir;
        break;

    default:
        break;
    }
}


/*
 * void tap_reset(void);
 *
 * Resets the TAP (simulating a power up or SCAN_RST signal.)
 */

void model3_tap_reset(void)
{
    id_size = 197;  // 197 bits

    state = 0;  // test-logic/reset
}

/*
 * void model3_machine_init(int step)
 *
 * step = BCD hardware rev.  0x10 for 1.0, 0x15 for 1.5, 0x20 for 2.0, etc.
 *
 */

void model3_machine_init(int step)
{
	m3_step = step;
}

/*****************************************************************************/
/* Epson RTC-72421 */

static UINT8 rtc_get_reg(int reg)
{
	time_t current_time;
	struct tm* tms;

	time( &current_time );
	tms = localtime( &current_time );

	switch(reg)
	{
		case 0:		// 1-second digit
			return (tms->tm_sec % 10) & 0xf;

		case 1:		// 10-seconds digit
			return (tms->tm_sec / 10) & 0x7;

		case 2:		// 1-minute digit
			return (tms->tm_min % 10) & 0xf;

		case 3:		// 10-minute digit
			return (tms->tm_min / 10) & 0x7;

		case 4:		// 1-hour digit
			return (tms->tm_hour % 10) & 0xf;

		case 5:		// 10-hours digit
			return (tms->tm_hour / 10) & 0x7;

		case 6:		// 1-day digit (days in month)
			return (tms->tm_mday % 10) & 0xf;

		case 7:		// 10-days digit
			return (tms->tm_mday / 10) & 0x3;

		case 8:		// 1-month digit
			return ((tms->tm_mon + 1) % 10) & 0xf;

		case 9:		// 10-months digit
			return ((tms->tm_mon + 1) / 10) & 0x1;

		case 10:	// 1-year digit
			return (tms->tm_year % 10) & 0xf;

		case 11:	// 10-years digit
			return ((tms->tm_year % 100) / 10) & 0xf;

		case 12:	// day of the week
			return tms->tm_wday & 0x7;

		case 13:
			return 0;

		case 14:
			return 0;

		case 15:
			return 0;

		default:
			osd_die("RTC-72421: Unknown reg %02X\n", reg);
			return 0;
	}
}

READ32_HANDLER(rtc72421_r)
{
	int reg = offset;
	UINT32 data;
	data = rtc_get_reg(reg) << 24;
	data |= 0x30000;	/* these bits are set to pass the battery voltage test */
	return data;
}

WRITE32_HANDLER(rtc72421_w)
{
}

