#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include "insignia.h"
#include "host_def.h"
/*
 * Name:		cmos.c
 *
 * Sccs ID:		@(#)cmos.c	1.38 07/11/95
 *
 * Purpose:		Unknown
 * 
 * (c)Copyright Insignia Solutions Ltd., 1990. All rights reserved.
 * 
 */

/*
 * O/S include files.
 */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>

/*
 * SoftPC include files
 */

#include "xt.h"
#include "cmos.h"
#include "cmosbios.h"
#include "ios.h"
#include "spcfile.h"
#include "error.h"
#include "config.h"
#include "timeval.h"
#include "ica.h"
#include "timer.h"
#include "tmstrobe.h"
#include "gfi.h"
#include "sas.h"
#include "debug.h"
#include "quick_ev.h"

#include <nt_eoi.h>


half_word(*bin2bcd)(int x);
half_word(*_24to12)(half_word x);
int (*bcd2bin)(int x);
int (*_12to24)(int x);

boolean  data_mode_yes;
boolean  twenty4_hour_clock;
int cmos_index = 0;


typedef struct _HOST_TIME{
   int Year;
   int Month;
   int Day;
   int Hour;
   int Minute;
   int Second;
   int WeekDay;
} HOSTTIME, *PHOSTTIME;

HOSTTIME   HostTime;      /* The host time */
PHOSTTIME  ht = &HostTime;


IU32 rtc_period_mSeconds = 976;

half_word cmos[CMOS_SIZE] = {
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* Timing info + alarms */
	REG_A_INIT,
	REG_B_INIT,
	REG_C_INIT,
	REG_D_INIT,
	DIAG_INIT,
	SHUT_INIT,
	FLOP_INIT,
        DISK_INIT,
	DISK_INIT,
	CMOS_RESVD,
	EQUIP_INIT,
	BM_LO_INIT, BM_HI_INIT,
	EXP_LO, EXP_HI,
	DISK_EXTEND, DISK2_EXTEND,
	CMOS_RESVD, CMOS_RESVD, CMOS_RESVD, CMOS_RESVD,	/* 0x1b - 0x1e */
	CMOS_RESVD, CMOS_RESVD, CMOS_RESVD, CMOS_RESVD,	/* 0x1f - 0x22 */
	CMOS_RESVD, CMOS_RESVD, CMOS_RESVD, CMOS_RESVD,	/* 0x23 - 0x26 */
	CMOS_RESVD, CMOS_RESVD, CMOS_RESVD, CMOS_RESVD,	/* 0x27 - 0x2a */
	CMOS_RESVD, CMOS_RESVD, CMOS_RESVD,	/* 0x2b - 0x2d */
	CHK_HI_INIT, CHK_LO_INIT,
	EXT_LO_INIT, EXT_HI_INIT,
	CENT_INIT,
	INFO_128_INIT,
	CMOS_RESVD, CMOS_RESVD, CMOS_RESVD, CMOS_RESVD,	/* 0x34 - 0x37 */
	CMOS_RESVD, CMOS_RESVD, CMOS_RESVD, CMOS_RESVD,	/* 0x38 - 0x3b */
	CMOS_RESVD, CMOS_RESVD, CMOS_RESVD, CMOS_RESVD,	/* 0x3c - 0x3f */
};

half_word *cmos_register = &cmos[CMOS_SHUT_DOWN];


int RtcLastAlarm;
int RtcAlarmTime;
int RtcHostUpdateTime;
BOOL RtcEoiPending;
int  RtcUpdateCycle=-1;
unsigned char TogglePfCount;
unsigned char PendingCReg = 0;
struct host_timeval RtcTickTime = {0,0};


/*
 * 
 * ===========================================================================
 * Internal functions
 * ===========================================================================
 *
 */




/*
 *  Calculates next AlarmTime in seconds based on RtcTickTime.
 *  Assumes that RtcHostUpdateTime == RtcTickTime->tv_sec
 */
void ResetAlarm(void)
{
   int HourDue;
   int MinDue;
   int SecDue;
   int SecondsTillDue;


   if (RtcLastAlarm) {
       return;
       }

   if (!(cmos[CMOS_REG_B] & AIE) || (cmos[CMOS_REG_B] & SET)) {
       RtcAlarmTime = 0;
       return;
       }


   /*
    * Determine hour, min, and sec when Next Alarm is due.
    *
    */

   HourDue = cmos[CMOS_HR_ALARM] >= DONT_CARE
                 ? ht->Hour
                 : (*_12to24)((*bcd2bin)(cmos[CMOS_HR_ALARM]));

   MinDue = cmos[CMOS_MIN_ALARM] >= DONT_CARE
                 ? ht->Minute
                 : (*bcd2bin)(cmos[CMOS_MIN_ALARM]);

   SecDue = cmos[CMOS_SEC_ALARM] >= DONT_CARE
                 ? ht->Second + 1
                 : (*bcd2bin)(cmos[CMOS_SEC_ALARM]);


   /*
    * Determine Seconds until Next alarm due. NEVER schedule alarms
    * for the current update cycle, as this will cause multiple alarms
    * to occur because alarm interrupts are queued in RtcTick(). ie
    * assume CurrTime is 1 sec in the future.
    *
    * AlarmSecs = SecDue + MinDue * 60 + HourDue * 3600;
    * CurrSecs  = ht->Second + 1 + ht->Minute * 60 + ht->Hour * 3600;
    * SecondsTillDue = AlarmSecs - CurrSecs - 1;
    *
    */

   SecondsTillDue = (HourDue - ht->Hour) * 3600 +
                    (MinDue -  ht->Minute)  * 60 +
                    SecDue - ht->Second - 1;

   if (SecondsTillDue < 0) {
       SecondsTillDue += 24 *3600;
       }

   SecondsTillDue++;

   /*
    *  The Next AlarmTime is RtcTickTime + SecondsTillDue;
    */
   RtcAlarmTime = RtcTickTime.tv_sec + SecondsTillDue;

}





/*
 *  Function to change Host Time where the the Day might change.
 *  (ie past midnight!).
 */
BOOL
HostTimeAdjust(
    int Seconds
    )
{
    TIME_FIELDS    tf;
    LARGE_INTEGER  liTime;

    tf.Milliseconds = 0;
    tf.Second     = ht->Second;
    tf.Minute     = ht->Minute;
    tf.Hour       = ht->Hour;
    tf.Day        = ht->Day;
    tf.Month      = ht->Month;
    tf.Year       = ht->Year;

    if (!RtlTimeFieldsToTime(&tf, &liTime)) {
        return FALSE;
        }

    liTime.QuadPart += Int32x32To64(Seconds, 10000000);

    RtlTimeToTimeFields(&liTime, &tf);

    ht->Second    = tf.Second;
    ht->Minute    = tf.Minute;
    ht->Hour      = tf.Hour;
    ht->Day       = tf.Day;
    ht->Month     = tf.Month;
    ht->Year      = tf.Year;
    ht->WeekDay   = tf.Weekday;

    return TRUE;
}




void
UpdateCmosTime(
   void
   )
{
   ULONG CurrTic;
   int SecsElapsed;


   if ((cmos[CMOS_REG_B] & SET)) {
       return;
       }

   TogglePfCount++;


   SecsElapsed = RtcTickTime.tv_sec - RtcHostUpdateTime;

   if (SecsElapsed > 0) {
       RtcHostUpdateTime = RtcTickTime.tv_sec;
       cmos[CMOS_REG_A] |= UIP;
       RtcUpdateCycle = 3;

       ht->Second += SecsElapsed;
       if (ht->Second >= 60) {
           ht->Minute += (ht->Second / 60);
           ht->Second = ht->Second % 60;
           if (ht->Minute >= 60) {
               ht->Hour += ht->Minute / 60;
               ht->Minute  = ht->Minute % 60;

                   /*
                    * To increment Time past midnight is hard
                    * because we don't have a calender. Let Nt
                    * deal with it.
                    */
               if (ht->Hour >= 24) {
                   int Seconds;

                   Seconds = (ht->Hour - 23) * 60 * 60;
                   ht->Hour = 23;
                   if (!HostTimeAdjust(Seconds)) {
                       ht->Hour = 0;
                       }
                   }
               }
           }
       }


}




void
QueueRtcInterrupt(
    unsigned char CRegFlag,
    BOOL  InEoi
    )
{
    unsigned long Delay;

    PendingCReg |= CRegFlag;

    if (RtcEoiPending || !PendingCReg) {
        return;
        }

    RtcEoiPending = TRUE;

    if (PendingCReg & C_PF) {
        Delay = rtc_period_mSeconds;
        }
    else if (InEoi) {
        Delay = 10000;
        }
    else {
        Delay = 0;
        }

    cmos[CMOS_REG_C] |= PendingCReg | C_IRQF;

    if (Delay) {
        host_DelayHwInterrupt(8,   // ICA_SLAVE, CPU_RTC_INT
                              1,
                              Delay
                              );
        }
    else {
        ica_hw_interrupt(ICA_SLAVE, CPU_RTC_INT, 1);
        }

    PendingCReg = 0;
}



void
RtcIntEoiHook(int IrqLine, int CallCount)
{
     RtcEoiPending = FALSE;

     if (RtcLastAlarm) {
         RtcLastAlarm = 0;
         UpdateCmosTime();
         ResetAlarm();
         }

     QueueRtcInterrupt((half_word)((cmos[CMOS_REG_B] & PIE) &&  rtc_period_mSeconds ? C_PF : 0),
                       TRUE
                       );
}



void do_checksum IFN0()
{
	int             i;
	word            checksum = 0;

	for (i = CMOS_DISKETTE; i < CMOS_CKSUM_HI; i++)
	{
		checksum += cmos[i];
	}
	cmos[CMOS_CKSUM_LO] = checksum & 0xff;
	cmos[CMOS_CKSUM_HI] = checksum >> 8;
}

half_word yes_bin2bcd IFN1(int, x)
{
	/* converts binary x to bcd */
	half_word       tens, units;

	tens = x / 10;
	units = x - tens * 10;
	return ((tens << 4) + units);
}

half_word no_bin2bcd IFN1(int, x)
{
	return ((half_word) x);
}

int yes_bcd2bin IFN1(int, x)
{
	/* converts x in bcd format to binary */
	return ((int) ((x & 0x0f) + (x >> 4) * 10));
}

int no_bcd2bin IFN1(int, x)
{
	return ((int) (half_word) x);
}

int no_12to24 IFN1(int, x)
{
	return (x);
}

half_word no_24to12 IFN1(half_word, x)
{
	return (x);
}

half_word yes_24to12 IFN1(half_word, x)
{
	/* converts binary or bcd x from 24 to 12 hour clock */
	half_word       y = (*bin2bcd) (12);

	if (x > y)
		x = (x - y) | 0x80;
	else if (x == 0)
		x = y | 0x80;
	return (x);
}

int yes_12to24 IFN1(int, x)
{
	/* converts binary or bcd x from 12 to 24 hour clock */
	half_word       y = (*bin2bcd) (12);

	if (x == (0x80 + y))
		return (0);
	else if (x & 0x80)
		return ((x & 0x7f) + y);
	else
		return (x);
}

int verify_equip_byte IFN1(half_word *, equip)
{
	static half_word display_mask[] = 
	{
		MDA_PRINTER,	CGA_80_COLUMN,	CGA_80_COLUMN,
		OWN_BIOS,	MDA_PRINTER
	};
	int equip_err;
	int num_flops;
	SHORT adapter;

	/* Check the Equipment Byte */
	*equip = 0;
        adapter = (SHORT) config_inquire(C_GFX_ADAPTER, NULL);
	if(adapter != -1)
		*equip |= display_mask[adapter];

	if( host_runtime_inquire(C_NPX_ENABLED) )
		*equip |= CO_PROCESSOR_PRESENT;

#ifdef SLAVEPC
	if (host_runtime_inquire(C_FLOPPY_SERVER) == GFI_SLAVE_SERVER)
	{
		num_flops =
			(*(CHAR *) config_inquire(C_SLAVEPC_DEVICE, NULL))
			? 1:0;
	}
	else
#endif /* SLAVEPC */
	{
		num_flops  =
			(*(CHAR *) config_inquire(C_FLOPPY_A_DEVICE, NULL))
			? 1:0;
#ifdef FLOPPY_B
		num_flops +=
			(*(CHAR *) config_inquire(C_FLOPPY_B_DEVICE, NULL))
			? 1:0;
#endif
	}

	if (num_flops == 2)
		*equip |= TWO_DRIVES;
	if (num_flops)
		*equip |= DISKETTE_PRESENT;

	equip_err = (*equip ^ cmos[CMOS_EQUIP]);
	return equip_err;
}

/*
 * =========================================================================
 *  External functions
 * =========================================================================
 */

GLOBAL void cmos_inb IFN2(io_addr, port, half_word *, value)
{
        /*
	** Tim September 92, hack for DEC 450ST
	*/
	if( port==0x78 )
	{
		*value = 0;
		return;
	}

	port = port & CMOS_BIT_MASK;	/* clear unused bits */

	if (port == CMOS_DATA)
        {
            host_ica_lock();

            UpdateCmosTime();

            switch (cmos_index) {
                case CMOS_REG_A:
                      *value = *cmos_register;

                      /*
                       *  If app polling RegA and Update Cycle pending,
                       *  complete it.
                       */
                      if (RtcUpdateCycle > 0 && !--RtcUpdateCycle) {
                          cmos[CMOS_REG_A] &= ~UIP;
                          if (cmos[CMOS_REG_B] & UIE) {
                              QueueRtcInterrupt(C_UF, FALSE);
                              }
                          else {
                              cmos[CMOS_REG_C] |= C_UF;
                              }
                          }

                      break;

                case CMOS_REG_C:
                      *value = *cmos_register;

                      /*
                       * Reading Register C on real rtc clears all bits.
                       * However, Need to toggle PF bit when PIE is
                       * not enabled for polling apps.
                       */
                      cmos[CMOS_REG_C] = C_CLEAR;
                      if (!(cmos[CMOS_REG_B] & PIE) && rtc_period_mSeconds) {
                          if (!(*value & C_PF) || (TogglePfCount & 0x8)) {
                              cmos[CMOS_REG_C]  |= C_PF;
                              }
                          }

                      break;

                case CMOS_SECONDS:
                      *value = (*bin2bcd) (ht->Second);
                      break;

                case CMOS_MINUTES:
                      *value = (*bin2bcd) (ht->Minute);
                      break;

                case CMOS_HOURS:
                      *value = (*_24to12) ((*bin2bcd) (ht->Hour));
                      break;

                case CMOS_DAY_WEEK:
                      /* Sunday = 1 on RTC, 0 in HOSTTIME */
                      *value = (*bin2bcd) (ht->WeekDay + 1);
                      break;

                case CMOS_DAY_MONTH:
                      *value = (*bin2bcd) (ht->Day);
                      break;

                case CMOS_MONTH:
                      /* [1-12] on RTC, [1-12] in HOSTTIME */
                      *value = (*bin2bcd) (ht->Month);
                      break;

                case CMOS_YEAR:
                      *value = (*bin2bcd) (ht->Year % 100);
                      break;

                case CMOS_CENTURY:
                      *value = (*bin2bcd) (ht->Year / 100);
                      break;

                default:
                      *value = *cmos_register;
                      break;
                }

            host_ica_unlock();

	}
	note_trace2(CMOS_VERBOSE, "cmos_inb() - port %x, returning val %x",
		    port, *value);
}


GLOBAL void cmos_outb IFN2(io_addr, port, half_word, value)
{
	static IU32 pirUsec[] = {
		     0,
		  3906,
		  7812,
		   122,
		   244,
		   488,
		   976,
		  1953,
		  3906,
		  7812,
		 15625,
		 31250,
		 62500,
		125000,
		250000,
		500000
	};


	/*
	** Tim September 92, hack for DEC 450ST
	*/
	if( port == 0x78 )
	    return;


	port = port & CMOS_BIT_MASK;	/* clear unused bits */

        note_trace2(CMOS_VERBOSE, "cmos_outb() - port %x, val %x", port, value);


        host_ica_lock();
        UpdateCmosTime();


        if (port == CMOS_PORT)
        {
                cmos_register = &cmos[cmos_index = (value & CMOS_ADDR_MASK)];

	} else if (port == CMOS_DATA)
        {
                switch (cmos_index)
		{
		case CMOS_REG_C:
		case CMOS_REG_D:
			/* These two registers are read only */
                        break;

                case CMOS_REG_B:

                        if (value & DM)
			{
				if (data_mode_yes)
				{
					bin2bcd = no_bin2bcd;
					bcd2bin = no_bcd2bin;
					data_mode_yes = FALSE;
				}
			} else
			{
				if (!data_mode_yes)
				{
					bin2bcd = yes_bin2bcd;
					bcd2bin = yes_bcd2bin;
					data_mode_yes = TRUE;
				}
			}
			if (value & _24_HR)
			{
				if (!twenty4_hour_clock)
				{
					_24to12 = no_24to12;
					_12to24 = no_12to24;
					twenty4_hour_clock = TRUE;
				}
			} else
			{
				if (twenty4_hour_clock)
				{
					_24to12 = yes_24to12;
					_12to24 = yes_12to24;
					twenty4_hour_clock = FALSE;
				}
                        }

                        if (value & SET) {
                            value  &= ~UIE;
                            }


                        if (*cmos_register != value) {

                            unsigned char ChangedBits;

                            ChangedBits = *cmos_register ^ value;
                            *cmos_register = value;

                            if (ChangedBits & PIE) {
                                if ((value & PIE) && rtc_period_mSeconds) {
                                    QueueRtcInterrupt(C_PF, FALSE);
                                    }
                                }

                            if (ChangedBits & (AIE | SET)) {
                                if (ChangedBits & SET) {
                                    RtcUpdateCycle = -1;
                                    cmos[CMOS_REG_A] &= ~UIP;
                                    RtcHostUpdateTime = RtcTickTime.tv_sec;
                                    }
                                ResetAlarm();
                                }
                            }

                        break;


		case CMOS_REG_A:
			/* This CMOS byte is read/write except for bit 7 */
			*cmos_register = (*cmos_register & TOP_BIT) | (value & REST);
                        rtc_period_mSeconds = pirUsec[*cmos_register & (RS3 | RS2 | RS1 | RS0)];

                        if ((*cmos_register & 0x70) != 0x20)
			{

                             note_trace1(CMOS_VERBOSE,
                                         "Cmos unsuported divider rate 0x%02x ignored",
                                         *cmos_register & 0x70);
                        }

                        break;


                case CMOS_SECONDS:
                        ht->Second = (*bcd2bin)(value);
                        break;

                case CMOS_MINUTES:
                        ht->Minute = (*bcd2bin)(value);
                        break;

                case CMOS_HOURS:
                        ht->Hour = (*_12to24)((*bcd2bin)(value));
                        break;

                case CMOS_DAY_WEEK:
                        /* Sunday = 1 on RTC, 0 in HOSTTIME */
                        ht->WeekDay  = (*bcd2bin)(value) - 1;
                        break;

                case CMOS_DAY_MONTH:
                        ht->Day = (*bcd2bin)(value);
                        break;

                case CMOS_MONTH:
                        /* [1-12] on RTC, [1-12] in HOSTTIME */
                        ht->Month = (*bcd2bin)(value);
                        break;

                case CMOS_YEAR:
                        ht->Year -=  ht->Year % 100;
                        ht->Year += (*bcd2bin)(value);
                        break;

                case CMOS_CENTURY:
                        ht->Year  %= 100;
                        ht->Year  += (*bcd2bin)(value) * 100;
                        break;

                default:
                        *cmos_register = value;
                        break;
                }



                /*
                 *  if one of the time fields changed Reset the alarm
                 */
                if (cmos_index <= CMOS_HR_ALARM) {
                    ResetAlarm();
                    }

        } else
	{
		note_trace2(CMOS_VERBOSE,
			    "cmos_outb() - Value %x to unsupported port %x", value, port);
        }

        host_ica_unlock();

}




GLOBAL void  cmos_equip_update IFN0()
{
	half_word       equip;

        host_ica_lock();

	if (verify_equip_byte(&equip))
	{
		note_trace0(CMOS_VERBOSE, "updating the equip byte silently");
		cmos[CMOS_EQUIP] = equip;
		/* correct the checksum */
		do_checksum();
        }

        host_ica_unlock();
}

/*
 * * General function to change the specified cmos byte to the specified
 * value
 *
 * MUST NOT BE USED FOR TIME. 14-Nov-1995 Jonle
 */
GLOBAL int cmos_write_byte IFN2(int, cmos_byte, half_word, new_value)
{
        if (cmos_byte >= 0 && cmos_byte <= 64)
        {

                note_trace2(CMOS_VERBOSE, "cmos_write_byte() byte=%x value=%x",
                            cmos_byte, new_value);

                host_ica_lock();
		cmos[cmos_byte] = new_value;
                do_checksum();
                host_ica_unlock();

                return (0);
	} else
	{
		always_trace2("ERROR: cmos write request: byte=%x value=%x",
			      cmos_byte, new_value);
		return (1);
        }

}
/*
 * * General fuunction to read specified cmos byte.
 *
 * MUST NOT BE USED FOR TIME. 14-Nov-1995 Jonle
 *
 */
GLOBAL int cmos_read_byte IFN2(int, cmos_byte, half_word *, value)
{

        if (cmos_byte >= 0 && cmos_byte <= 64)
        {
                host_ica_lock();
                *value = cmos[cmos_byte];
                host_ica_unlock();

		note_trace2(CMOS_VERBOSE, "cmos_read_byte() byte=%x value=%x",
			    cmos_byte, value);
		return (0);
	} else
	{
		always_trace1("ERROR: cmos read request: byte=%x", cmos_byte);
		return (1);
        }

}


void cmos_error IFN6(int, err, half_word, diag, half_word, equip,
	int, equip_err, half_word, floppy, half_word, disk)
{
	char            err_string[256];

	if (err & BAD_SHUT_DOWN)
	{
		strcpy(err_string, "shut ");
		note_trace0(CMOS_VERBOSE, "eek! someone's furtling with the shutdown byte");
	} else
		strcpy(err_string, "");

	if (err & BAD_REG_D)
	{
		strcat(err_string, "power ");
		note_trace0(CMOS_VERBOSE, " The battery is dead - this shouldn't happen!");
	}
	if (err & BAD_DIAG)
	{
		strcat(err_string, "diag ");
		if (diag & BAD_BAT)
			note_trace0(CMOS_VERBOSE, "bad battery");
		if (diag & BAD_CONFIG)
			note_trace0(CMOS_VERBOSE, "bad config");
		if (diag & BAD_CKSUM)
			note_trace0(CMOS_VERBOSE, "bad chksum");
		if (diag & W_MEM_SIZE)
			note_trace0(CMOS_VERBOSE, "memory size != configuration");
		if (diag & HF_FAIL)
			note_trace0(CMOS_VERBOSE, "fixed disk failure on init");
		if (diag & CMOS_CLK_FAIL)
			note_trace0(CMOS_VERBOSE, "cmos clock not updating or invalid");
	}
	if (err & BAD_EQUIP)
	{
		strcat(err_string, "equip ");

		if (equip_err)
		{
			if (equip_err & DRIVE_INFO)
				note_trace0(CMOS_VERBOSE, "incorrect diskette - resetting");
			if (equip_err & DISPLAY_INFO)
				note_trace0(CMOS_VERBOSE, "incorrect display - resetting");
			if (equip_err & NPX_INFO)
				note_trace0(CMOS_VERBOSE, "incorrect npx - resetting CMOS");
			if (equip_err & RESVD_INFO)
				note_trace0(CMOS_VERBOSE, "incorrect reserved bytes - resetting");
		}
	}
	if (err & BAD_FLOPPY)
	{
		strcat(err_string, "flop ");
		note_trace0(CMOS_VERBOSE, "incorrect diskette type - resetting");
	}
	if (err & BAD_DISK)
	{
		strcat(err_string, "disk ");
		note_trace0(CMOS_VERBOSE, "incorrect disk type - resetting");
	}
	if (err & BAD_BMS)
	{
		strcat(err_string, "bms ");
		note_trace0(CMOS_VERBOSE, "bad base memory - resetting");
	}
	if (err & BAD_XMS)
	{
		strcat(err_string, "extended memory ");
		note_trace0(CMOS_VERBOSE, "bad extended memory CMOS entry - resetting");
	}
	if (err & BAD_CHECKSUM)
	{
		strcat(err_string, "cksum ");
		note_trace0(CMOS_VERBOSE, "bad Checksum - resetting");
        }

        if (err & BAD_SHUT_DOWN)
		cmos[CMOS_SHUT_DOWN] = SHUT_INIT;
	if (err & BAD_REG_D)
		cmos[CMOS_REG_D] = REG_D_INIT;
	if (err & BAD_DIAG)
		cmos[CMOS_DIAG] = DIAG_INIT;
	if (err & BAD_EQUIP)
		cmos[CMOS_EQUIP] = equip;
	if (err & BAD_FLOPPY)
		cmos[CMOS_DISKETTE] = floppy;
	if (err & BAD_DISK)
		cmos[CMOS_DISK] = disk;
	if (err & BAD_BMS)
	{
		cmos[CMOS_B_M_S_LO] = BM_LO_INIT;
		cmos[CMOS_B_M_S_HI] = BM_HI_INIT;
	}
	if (err & BAD_XMS)
	{
                cmos[CMOS_E_M_S_LO] = (half_word)((sys_addr) (sas_memory_size() - PC_MEM_SIZE) >> 10);
                cmos[CMOS_E_M_S_HI] = (half_word)((sys_addr) (sas_memory_size() - PC_MEM_SIZE) >> 18);
	}
	/* Reset the Checksum if there is any error */
	if (err)
	{
		/* Do the Checksum */
		do_checksum();
	}
}


/*  rtc_nit
 *  Assumes Caller holds Ica lock
 */

GLOBAL void rtc_init(void)
{
     SYSTEMTIME st;

     RtcAlarmTime = 0;
     RtcLastAlarm = 0;
     RtcEoiPending  = FALSE;

     GetLocalTime(&st);
     ht->Second    = st.wSecond;
     ht->Minute    = st.wMinute;
     ht->Hour      = st.wHour;
     ht->Day       = st.wDay;
     ht->Month     = st.wMonth;
     ht->Year      = st.wYear;
     ht->WeekDay   = st.wDayOfWeek;

     host_GetSysTime(&RtcTickTime);
     RtcHostUpdateTime = RtcTickTime.tv_sec;

     ResetAlarm();
}



/*  RtcTick
 *  Assumes caller is holding Ica lock
 *
 *  WARNING: this routine is invoked by the hi-priority heartbeat
 *  thread at a rate of 18.2 time per sec with minimal variance.
 *  It is a polling routine, and because of the hi-freq and hi-priority
 *  it must be mean and lean, so don't do anything which could be
 *  done elsewhere.
 */

GLOBAL void RtcTick(struct host_timeval *time)
{
    /*
     *  Save away the RtcTick time stamp
     */
    RtcTickTime = *time;

    /*
     *  Check if time for Alarm interrupt
     */
    if (RtcAlarmTime && RtcAlarmTime <= RtcTickTime.tv_sec) {
        RtcLastAlarm = RtcTickTime.tv_sec;
        RtcAlarmTime = 0;
        QueueRtcInterrupt(C_AF, FALSE);
        }


    /*
     *  If we are in an update cycle complete it.
     *
     */

    if (RtcUpdateCycle >= 0) {
        RtcUpdateCycle = -1;
        cmos[CMOS_REG_A] &= ~UIP;
        if (cmos[CMOS_REG_B] & UIE) {
            QueueRtcInterrupt(C_UF, FALSE);
            }
        }

     /*
      *  If UIE active, then we have to keep HostTime in
      *  sync so we know when to do the Update End Interrupt.
      */
    else if (cmos[CMOS_REG_B] & UIE) {
        UpdateCmosTime();
        }

}



GLOBAL void cmos_init IFN0()
{
      io_addr         i;

      /* Set Up the cmos time bytes to be in BCD by default */
      bin2bcd = yes_bin2bcd;
      bcd2bin = yes_bcd2bin;
      data_mode_yes = TRUE;

      /* Set Up the cmos hour bytes to be 24 hour by default */
      _24to12 = no_24to12;
      _12to24 = no_12to24;
      twenty4_hour_clock = TRUE;


      /* attach the ports */
      io_define_inb(CMOS_ADAPTOR, cmos_inb);
      io_define_outb(CMOS_ADAPTOR, cmos_outb);

      for (i = CMOS_PORT_START; i <= CMOS_PORT_END; i++)
           io_connect_port(i, CMOS_ADAPTOR, IO_READ_WRITE);


      RegisterEOIHook(8,   // ICA_SLAVE, CPU_RTC_INT
                      RtcIntEoiHook
                      );
      rtc_init();
}


GLOBAL void cmos_pickup IFN0()
{
      /*
       *  Static init plus post is used instead of external files
       */
}


GLOBAL void cmos_post IFN0()
{
	/*
	 * The IBM POST checks the current settings in the CMOS with the
	 * equipment determined by writing to the hardware. Any discrepencies
	 * cause a bad config bit to be set and the user is then requested to
	 * run the Setup utility. Here we check the CMOS against the current
	 * settings in the config structure. If there is a discrepency we
	 * correct the CMOS silently.
	 */
	int             cmos_err, equip_err;
	half_word       diag, equip, floppy, disk;
	word            checksum = 0;
	int             i;


	cmos_err = 0;

	/* Check the Shutdown Byte */
	if (cmos[CMOS_SHUT_DOWN])
		cmos_err |= BAD_SHUT_DOWN;

	/* Check The Power */
	if (!(cmos[CMOS_REG_D] & VRT))
		cmos_err |= BAD_REG_D;

	/* Check The Diagnostic Status Byte */
	if (diag = cmos[CMOS_DIAG])
		cmos_err |= BAD_DIAG;

	/* Check the Equipment Byte */
	if (equip_err = verify_equip_byte(&equip))
		cmos_err |= BAD_EQUIP;

	/* Check the Floppy Byte */
	floppy = gfi_drive_type(1) | (gfi_drive_type(0) << 4);
	if (floppy != cmos[CMOS_DISKETTE])
		cmos_err |= BAD_FLOPPY;

	/* Check the Fixed Disk Type */
	 disk = 0x30;         /* Drive C type always 3 - then <<4 */
	 /* check whether D drive exists */
	 if ( *((CHAR *) config_inquire(C_HARD_DISK2_NAME, NULL)))
		 disk = 0x34;         /* 3 << 4 | 4 */
	if (disk != cmos[CMOS_DISK])
		cmos_err |= BAD_DISK;

	/* Check the Base Memory */
	if ((cmos[CMOS_B_M_S_LO] != BM_LO_INIT) || (cmos[CMOS_B_M_S_HI] != BM_HI_INIT))
		cmos_err |= BAD_BMS;

	/* Check the extended memory */
	if ((cmos[CMOS_E_M_S_LO] !=
	     ((sys_addr) (sas_memory_size() - PC_MEM_SIZE) >> 10) & 0xff) ||
	    (cmos[CMOS_E_M_S_HI] !=
	     ((sys_addr) (sas_memory_size() - PC_MEM_SIZE) >> 18) & 0xff))
		cmos_err |= BAD_XMS;

	/* Ignore the Contents of the Drive C and Drive D extended bytes */

	/* Do the Checksum */
	for (i = CMOS_DISKETTE; i < CMOS_CKSUM_HI; i++)
	{
		checksum += cmos[i];
	}
	/* If the CMOS is OK test the checksum */
	/* If not, we will have to change it anyway */
	if (!cmos_err)
	{
		if ((checksum & 0xff) != cmos[CMOS_CKSUM_LO])
		{
			cmos_err |= BAD_CHECKSUM;
		}
		if ((checksum >> 8) != cmos[CMOS_CKSUM_HI])
		{
			cmos_err |= BAD_CHECKSUM;
		}
	}
	if (cmos_err)
            cmos_error(cmos_err, diag, equip, equip_err, floppy, disk);

	cmos[CMOS_REG_A] = REG_A_INIT;

        /* Check the Extended Memory */
        cmos[CMOS_U_M_S_LO] = (half_word)((sys_addr) (sas_memory_size() - PC_MEM_SIZE) >> 10);
        cmos[CMOS_U_M_S_HI] = (half_word)((sys_addr) (sas_memory_size() - PC_MEM_SIZE) >> 18);

	/* Set up the default cmos location */
	cmos_register = &cmos[cmos_index = CMOS_SHUT_DOWN];

}



/*
 *  WE DON'T EVER read or write a central cmos
 */
GLOBAL void cmos_update IFN0()
{
    ; /* do nothing */
}



/*(
========================= cmos_clear_shutdown_byte ============================
PURPOSE:
	To clear the "shutdown" byte in the CMOS which indicates that the
	next reset is not a "soft" one. (e.g. it is a CTRL-ALT-DEL or panel
	reset). This routine is needed (rather than just doung cmos_outb()
	since the processor might currently be in enhanced mode with io to CMOS
	virtualised.
INPUT:
OUTPUT:
===============================================================================
)*/

GLOBAL void cmos_clear_shutdown_byte IFN0()
{
        host_ica_lock();

        cmos[CMOS_SHUT_DOWN] = 0;

        host_ica_unlock();
}
