#include "insignia.h"
#include "host_def.h"
/*
 * [ Name:		cmos.c Derived From:	Unknown Author:
 * n Created On:	Unknown Sccs ID:	@(#)cmos.c	1.22 11/10/92 Purpose:
 * Unknown
 * 
 * (c)Copyright Insignia Solutions Ltd., 1990. All rights reserved.
 * 
 * ] */

#ifdef SEGMENTATION
/*
 * The following #include specifies the code segment into which this
 * module will by placed by the MPW C compiler on the Mac II running
 * MultiFinder.
 */
#include "SOFTPC_CMOS.seg"
#endif


/*
 * O/S include files.
 */
#include <stdlib.h>
#include <stdio.h>
#include StringH
#include FCntlH

/*
 * 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"

/*
 * Allow a suitable default for the CMOS file name.
 */

#ifndef CMOS_FILE_NAME
#define CMOS_FILE_NAME ".spccmosram"
#endif

IMPORT char *host_getenv IPT1(char *, envstr) ;	
/*
 * 
 * ============================================================================
 * Global data
 * ===========================================================================
 * =
 */
static boolean  data_mode_yes;
static          half_word(*bin2bcd) ();
static          half_word(*_24to12) IPT1(half_word, x);
static int      (*bcd2bin) IPT1(int, x);
static int      (*_12to24) ();
static boolean  twenty4_hour_clock;

static boolean  cmos_has_changed = FALSE;
static boolean  cmos_read_in = FALSE;
static long     filesize;
static int      cmos_index;
static boolean  reset_alarm = FALSE;
static long     user_time = 0;	/* difference between the host and the CMOS
				 * time */
static struct host_tm *ht;	/* The host time */
static half_word programmed_numints = 0;
static 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,
	CMOS_RESVD,
	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 */
};

static half_word *cmos_register = &cmos[CMOS_SHUT_DOWN];

#ifdef NTVDM
unsigned long   dwTickCount,dwAccumulativeMilSec;
extern unsigned long GetTickCount (VOID);
#endif

#ifdef TEST_HARNESS
unsigned long   io_verbose = 0;
#endif

int             rtc_int_enabled;


/*
 * 
 * ============================================================================
 * Static data and defines
 * ===========================================================================
 * =
 */

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

LOCAL void enable_nmi IFN0()
{
}

LOCAL void disable_nmi IFN0()
{
}

LOCAL 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;
}

LOCAL 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);
}

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

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

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

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

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

LOCAL 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);
}

LOCAL 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);
}

LOCAL void rtc_alarm IFN1(long, param)
{
        UNUSED (param);

	note_trace0(CMOS_VERBOSE, "rtc_alarm() gone off\n");

	cmos[CMOS_REG_C] |= C_AF;

	if (cmos[CMOS_REG_B] & AIE)
	{
		note_trace0(CMOS_VERBOSE, "rtc_alarm() setting IRQF due to AIE\n");
		cmos[CMOS_REG_C] |= C_IRQF;
	}

	reset_alarm = TRUE;
}

LOCAL void set_alarm IFN0()
{
	long            numsecs;
	long            alarm_secs, now_secs;
	long            num_pc_ticks;
	static word     handle;

	if (cmos[CMOS_HR_ALARM] & DONT_CARE)
	{
		if (cmos[CMOS_MIN_ALARM] & DONT_CARE)
		{
			if (cmos[CMOS_SEC_ALARM] & DONT_CARE)
				numsecs = 1;
			else
				numsecs = 60;
		} else
			numsecs = 3600;
	} else
	{
		alarm_secs = (*bcd2bin) (cmos[CMOS_SEC_ALARM]) +
			((*bcd2bin) (cmos[CMOS_MIN_ALARM]) * 60) +
			((*_12to24) ((*bcd2bin) (cmos[CMOS_HR_ALARM])) * 3600);
		now_secs = ht->tm_sec + 60 * ht->tm_min + 3600 * ht->tm_hour;
		numsecs = alarm_secs - now_secs;
		if (numsecs < 0)
			numsecs += 24 * 3600;
	}
	num_pc_ticks = 20 * numsecs;
	note_trace1(CMOS_VERBOSE, "set_alarm() requesting alarm in %d ticks\n", num_pc_ticks);
	if (handle > 0)
		delete_tic_event(handle);
	handle = add_tic_event(rtc_alarm, num_pc_ticks, 0);
}

LOCAL 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 = (ULONG) 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)
{

#ifdef NTVDM
	/*
	** Tim September 92, hack for DEC 450ST
	*/
	if( port==0x78 )
	{
		*value = 0;
		return;
	}
#endif
	port = port & CMOS_BIT_MASK;	/* clear unused bits */

	if (port == CMOS_DATA)
	{
		*value = *cmos_register;
		/* 
		 * Reading Register C clears it.
		 * However, if we're being called from the rtc interrupt handler
		 * Register C's contents should be consistent with the rtc having
		 * just raised an interrupt.
		 */
		if (cmos_index == CMOS_REG_C)
		{
                        if ((*value == C_CLEAR) && VirtualIca[ICA_SLAVE].ica_count[CPU_RTC_INT])
			{
				/* Some of the batch of interrupts for the rtc are still unserviced */
				if (cmos[CMOS_REG_B] & PIE)
					*cmos_register = C_IRQF | C_PF;
				else
					*cmos_register = C_PF;
			}
			else
				*cmos_register = C_CLEAR;
		}
		else if (cmos_index < CMOS_REG_A)
                {
#ifndef NTVDM
#ifndef PROD
			if (host_getenv("TIME_OF_DAY_FRIG") == NULL)
			{
#endif
#endif

				switch (cmos_index)
				{
				case CMOS_SECONDS:
					*cmos_register = (*bin2bcd) (ht->tm_sec);
					break;
				case CMOS_MINUTES:
					*cmos_register = (*bin2bcd) (ht->tm_min);
					break;
				case CMOS_HOURS:
					*cmos_register = (*_24to12) ((*bin2bcd) (ht->tm_hour));
					break;
				case CMOS_DAY_WEEK:
					/* Sunday = 1 on RTC, 0 in structure */
					*cmos_register = (*bin2bcd) (ht->tm_wday + 1);
					break;
				case CMOS_DAY_MONTH:
					*cmos_register = (*bin2bcd) (ht->tm_mday);
					break;
				case CMOS_MONTH:
					/* [1-12] on RTC, [0-11] in structure */
					*cmos_register = (*bin2bcd) (ht->tm_mon + 1);
					break;
				case CMOS_YEAR:
					*cmos_register = (*bin2bcd) (ht->tm_year);
					break;
				default:
					break;
                                }
#ifndef NTVDM
#ifndef PROD
			} else
			{
				static int      first = 1;

				if (first)
				{
					first = 0;
					printf("FRIG ALERT!!!! - cmos clock frozen!\n");
				}
				*cmos_register = 1;
			}
#endif
#endif

			*value = *cmos_register;
		}
	}
	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 half_word pir[] = {
		PIR_NONE, PIR_256HZ, PIR_128HZ, PIR_8MHZ, PIR_4MHZ, PIR_2MHZ,
		PIR_1MHZ, PIR_512HZ, PIR_256HZ, PIR_128HZ, PIR_64HZ, PIR_32HZ,
		PIR_16HZ, PIR_8HZ, PIR_4HZ, PIR_2HZ,
	};

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

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

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

	if (port == CMOS_PORT)
	{
		if (value & NMI_DISABLE)
			disable_nmi();
		else
			enable_nmi();

		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 (*cmos_register != value)
			{
				cmos_has_changed = TRUE;
				*cmos_register = value;
			}
			break;
		case CMOS_REG_A:
			/* This CMOS byte is read/write except for bit 7 */
			*cmos_register = (*cmos_register & TOP_BIT) | (value & REST);
			if (*cmos_register & DV1)
			{
				/* 32.768 kHz time base */
				pir[1] = PIR_256HZ;
				pir[2] = PIR_128HZ;
			} else
			{
				/* 4.19 MHz or 1.04 MHz time base */
				pir[1] = PIR_32MHZ;
				pir[2] = PIR_16MHZ;
			}
			programmed_numints = pir[*cmos_register & (RS3 | RS2 | RS1 | RS0)];
			cmos_has_changed = TRUE;
			break;
		case CMOS_SECONDS:
			/* This CMOS byte is read/write except for bit 7 */
			*cmos_register = (*cmos_register & TOP_BIT) | (value & REST);
			user_time += (*bcd2bin) (value) - ht->tm_sec;
			reset_alarm = TRUE;
			break;
		case CMOS_MINUTES:
			user_time += ((*bcd2bin) (value) - ht->tm_min) * 60;
			*cmos_register = value;
			reset_alarm = TRUE;
			break;
		case CMOS_HOURS:
			user_time += ((*_12to24) ((*bcd2bin) (value)) - ht->tm_hour) * 60 * 60;
			*cmos_register = value;
			reset_alarm = TRUE;
			break;
		case CMOS_DAY_WEEK:
			/* this being changed doesn't change the time */
			*cmos_register = value;
			break;
		case CMOS_DAY_MONTH:
			user_time += ((*bcd2bin) (value) - ht->tm_mday) * 60 * 60 * 24;
			*cmos_register = value;
			break;
		case CMOS_MONTH:
			user_time += ((*bcd2bin) (value) - 1 - ht->tm_mon) * 60 * 60 * 24 * 30;
			*cmos_register = value;
			break;
		case CMOS_YEAR:
			user_time += ((*bcd2bin) (value) - ht->tm_year) * 60 * 60 * 24 * 30 * 12;
			*cmos_register = value;
			break;
		case CMOS_SEC_ALARM:
		case CMOS_MIN_ALARM:
		case CMOS_HR_ALARM:
			reset_alarm = TRUE;
			/* falling through */
		default:
			*cmos_register = value;
			cmos_has_changed = TRUE;
			break;
		}
	} else
	{
		note_trace2(CMOS_VERBOSE,
			    "cmos_outb() - Value %x to unsupported port %x", value, port);
	}
}

static int      cmos_count = 0;

GLOBAL void rtc_tick IFN0()
{
#ifdef PM
	int             num_ints = 1;

	switch (cmos_count)
	{
	case 0:
		if (cmos[CMOS_REG_B] & UIE)
		{
			cmos[CMOS_REG_C] |= C_IRQF;
			note_trace0(CMOS_VERBOSE, "rtc_tick() setting IRQF due to UIE\n");
		}
		/* DROP THRU!! */

	case 1:
		/* Once a second */
		/* Flip UIP bit */
                cmos[CMOS_REG_C] ^= C_UF;
#ifndef NTVDM
                cmos[CMOS_REG_A] ^= UIP;
#endif
		break;

	case 19:
		/* update the time at some suitable point in cycle */
		if (cmos[CMOS_REG_B] & SET)
		{
			/* User is updating user_time */
		} else
		{
                    /* simple update - add 1 second to time */
#ifdef NTVDM
        // sudeepb 08-Jul-1993 Old code assumed rtc-tick will be called
        // 20 times a second. This is not true under NTVDM. So we have
        // to keep track of time seperately and add to the cmos time.
unsigned long dwTemp;
                    dwTemp =  GetTickCount();
                    dwAccumulativeMilSec += (dwTemp - dwTickCount);
                    dwTickCount = dwTemp;
                    ht->tm_sec = (ULONG) ht->tm_sec +
                                        (dwAccumulativeMilSec / 1000);
                    dwAccumulativeMilSec = dwAccumulativeMilSec % 1000;
                    if (ht->tm_sec >= 60)
                    {
                            ht->tm_min += (ht->tm_sec / 60);
                            ht->tm_sec = (ht->tm_sec % 60);
                            if (ht->tm_min >= 60)
                            {
                                    ht->tm_hour++;
                                    ht->tm_min -= 60;
                                    if (ht->tm_hour == 25)
                                    {
                                            ht->tm_hour = 0;
                                            ht->tm_mday++;
                                            /* Kop out at this point */
                                    }
                            }
                    }
#else
                    ht->tm_sec++;
                    if (ht->tm_sec == 60)
                    {
                            ht->tm_sec = 0;
                            ht->tm_min++;
                            if (ht->tm_min == 60)
                            {
                                    ht->tm_min = 0;
                                    ht->tm_hour++;
                                    if (ht->tm_hour == 25)
                                    {
                                            ht->tm_hour = 0;
                                            ht->tm_mday++;
                                            /* Kop out at this point */
                                    }
                            }
                    }
#endif
		}
		break;

	default:
		break;
	}

        /* 32 times a second  VERY OS/2 SPECIFIC!! */
#ifdef NTVDM
        cmos_count = (++cmos_count) % 20;
#else
        cmos_count = (++cmos_count) % 32;
#endif

	cmos[CMOS_REG_C] ^= C_PF;
	if (cmos[CMOS_REG_B] & PIE)
	{
		num_ints = programmed_numints;
		note_trace0(CMOS_VERBOSE, "rtc_tick() setting IRQF due to PIE\n");
		cmos[CMOS_REG_C] |= C_IRQF;
	}
	if (reset_alarm)
	{
		reset_alarm = FALSE;
		set_alarm();
	}
	if (cmos[CMOS_REG_C] & C_IRQF)
	{
		if (rtc_int_enabled)
		{
			note_trace1(CMOS_VERBOSE, "INTERRUPT (%d).\n", num_ints);
			ica_hw_interrupt(ICA_SLAVE, CPU_RTC_INT, num_ints);
		}
	}
#else
	/*
	 * This routine is called from the timer tick hopefully about twenty
	 * times a second, and endeavours to emulate what goes on in the real
	 * time clock part of the MC146818A. An accurate emulation requires
	 * this routine to be called about 1000 times a second. Since 20 and
	 * 1000 are almost equal, we should have no problems!
	 */
	static int      cmos_count = 0;
	int             num_ints = 1;
	long            bintim;

	switch (cmos_count)
	{
	case 0:
		if (cmos[CMOS_REG_B] & UIE)
		{
			cmos[CMOS_REG_C] |= C_IRQF;
			note_trace0(CMOS_VERBOSE, "rtc_tick() setting IRQF due to UIE\n");
		}
	case 1:
		/* Once a second */
		/* Flip UIP bit */
		cmos[CMOS_REG_C] ^= C_UF;
		cmos[CMOS_REG_A] ^= UIP;
	case 4:
	case 14:
#ifdef NO_CLEVER_ALARM_SCHEME
		/* update the time in cmos so we may check the alarm */
		cmos[CMOS_SECONDS] = (*bcd) (ht->tm_sec);
		cmos[CMOS_MINUTES] = (*bcd) (ht->tm_min);
		cmos[CMOS_HOURS] = (*bcd) ((*twenty4) (ht->tm_hour));
		cmos[CMOS_DAY_WEEK] = (*bcd) (ht->tm_wday + 1);
		cmos[CMOS_MONTH] = (*bcd) (ht->tm_mon + 1);
		cmos[CMOS_YEAR] = (*year_bcd) (ht->tm_year);
		cmos[CMOS_CENTURY] = (*bcd) (19);
		if ((cmos[CMOS_SECONDS] == cmos[CMOS_SEC_ALARM]) &&
		    (cmos[CMOS_MINUTES] == cmos[CMOS_MIN_ALARM]) &&
		    (cmos[CMOS_HOURS] == cmos[CMOS_HR_ALARM]))
		{
			cmos[CMOS_REG_C] |= C_AF;
			if (cmos[CMOS_REG_B] & AIE)
			{
				cmos[CMOS_REG_C] |= C_IRQF;
			}
		}
#endif
	case 9:
	case 19:		/* update the time stored in ht every quarter
				 * sec */
		if (cmos[CMOS_REG_B] & SET)
		{
			/* User is updating user_time */
		} else
		{
			/* User_time is set */
			/* Fill the localtime structure with the users time */
			bintim = host_time((long *) 0);
			bintim += user_time;
			ht = (struct host_tm *) host_localtime(&bintim);
			/*
			 * printf("%02d-%02d-%02d %02d:%02d:%02d\n",
			 * ht->tm_mday,ht->tm_mon,ht->tm_year,
			 * ht->tm_hour,ht->tm_min,ht->tm_sec);
			 */
		}
	default:
		/* 20 times a second */
		cmos_count = (++cmos_count) % 20;
		cmos[CMOS_REG_C] ^= C_PF;
		if (cmos[CMOS_REG_B] & PIE)
		{
			num_ints = programmed_numints;
			note_trace0(CMOS_VERBOSE, "rtc_tick() setting IRQF due to PIE\n");
			cmos[CMOS_REG_C] |= C_IRQF;
		}
		if (reset_alarm)
		{
			reset_alarm = FALSE;
			set_alarm();
		}
		if (cmos[CMOS_REG_C] & C_IRQF)
		{
			if (rtc_int_enabled)
			{
				note_trace0(CMOS_VERBOSE, "INTERRUPT!!!!\n");
				ica_hw_interrupt(ICA_SLAVE, CPU_RTC_INT, num_ints);
			}
		}
	}
#endif				/* PM */
}

GLOBAL void  cmos_equip_update IFN0()
{
	half_word       equip;

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

/*
 * * General function to change the specified cmos byte to the specified
 * value
 */
GLOBAL int cmos_write_byte IFN2(int, cmos_byte, half_word, new_value)
{
	note_trace2(CMOS_VERBOSE, "cmos_write_byte() byte=%x value=%x\n",
		    cmos_byte, new_value);
	if (cmos_byte >= 0 && cmos_byte <= 64)
	{
		cmos[cmos_byte] = new_value;
		do_checksum();
		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.
 */
GLOBAL int cmos_read_byte IFN2(int, cmos_byte, half_word *, value)
{
	if (cmos_byte >= 0 && cmos_byte <= 64)
	{
		*value = cmos[cmos_byte];
		note_trace2(CMOS_VERBOSE, "cmos_read_byte() byte=%x value=%x\n",
			    cmos_byte, value);
		return (0);
	} else
	{
		always_trace1("ERROR: cmos read request: byte=%x", cmos_byte);
		return (1);
	}
}

#ifdef SEGMENTATION
/*
 * The following #include specifies the code segment into which this
 * module will by placed by the MPW C compiler on the Mac II running
 * MultiFinder.
 */
#include "SOFTPC_INIT.seg"
#endif

LOCAL void read_cmos IFN0() 
{
	filesize = host_read_resource(CMOS_REZ_ID, CMOS_FILE_NAME,
				      cmos, sizeof(cmos), SILENT);

	/* Set a flag to say we've tried to read the CMOS file */
	cmos_read_in = TRUE;
}

LOCAL void write_cmos IFN0()
{
	/* terminate might be called before reset */
	if (cmos_read_in && cmos_has_changed)
	{
		host_write_resource(CMOS_REZ_ID, CMOS_FILE_NAME,
				    cmos, sizeof(cmos));
	}
}

LOCAL 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\n");
	} 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!\n");
	}
	if (err & BAD_DIAG)
	{
		strcat(err_string, "diag ");
		if (diag & BAD_BAT)
			note_trace0(CMOS_VERBOSE, "bad battery\n");
		if (diag & BAD_CONFIG)
			note_trace0(CMOS_VERBOSE, "bad config\n");
		if (diag & BAD_CKSUM)
			note_trace0(CMOS_VERBOSE, "bad chksum\n");
		if (diag & W_MEM_SIZE)
			note_trace0(CMOS_VERBOSE, "memory size != configuration\n");
		if (diag & HF_FAIL)
			note_trace0(CMOS_VERBOSE, "fixed disk failure on init\n");
		if (diag & CMOS_CLK_FAIL)
			note_trace0(CMOS_VERBOSE, "cmos clock not updating or invalid\n");
	}
	if (err & BAD_EQUIP)
	{
		strcat(err_string, "equip ");

		if (equip_err)
		{
			if (equip_err & DRIVE_INFO)
				note_trace0(CMOS_VERBOSE, "incorrect diskette - resetting\n");
			if (equip_err & DISPLAY_INFO)
				note_trace0(CMOS_VERBOSE, "incorrect display - resetting\n");
			if (equip_err & NPX_INFO)
				note_trace0(CMOS_VERBOSE, "incorrect npx - resetting CMOS\n");
			if (equip_err & RESVD_INFO)
				note_trace0(CMOS_VERBOSE, "incorrect reserved bytes - resetting\n");
		}
	}
	if (err & BAD_FLOPPY)
	{
		strcat(err_string, "flop ");
		note_trace0(CMOS_VERBOSE, "incorrect diskette type - resetting\n");
	}
	if (err & BAD_DISK)
	{
		strcat(err_string, "disk ");
		note_trace0(CMOS_VERBOSE, "incorrect disk type - resetting\n");
	}
	if (err & BAD_BMS)
	{
		strcat(err_string, "bms ");
		note_trace0(CMOS_VERBOSE, "bad base memory - resetting\n");
	}
	if (err & BAD_XMS)
	{
		strcat(err_string, "extended memory ");
		note_trace0(CMOS_VERBOSE, "bad extended memory CMOS entry - resetting\n");
	}
	if (err & BAD_CHECKSUM)
	{
		strcat(err_string, "cksum ");
		note_trace0(CMOS_VERBOSE, "bad Checksum - resetting\n");
	}
#ifndef PROD
	if (!filesize)
		always_trace1("Incorrect CMOS entries %s", err_string);
#endif

	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] =
			((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;
	}
	/* Reset the Checksum if there is any error */
	if (err)
	{
		/* Do the Checksum */
		do_checksum();
	}
}

GLOBAL void cmos_init IFN0()
{
#ifndef TEST_HARNESS
	cmos_io_attach();
#endif

#ifndef NTVDM
#ifndef PROD
	if (host_getenv("TIME_OF_DAY_FRIG") == NULL)
#endif                          /* PROD */
#endif
                rtc_int_enabled = TRUE;
#ifndef NTVDM
#ifndef PROD
	else
		rtc_int_enabled = FALSE;
#endif                          /* PROD */
#endif

        rtc_init();

// this is called from reset!!
#ifdef NTVDM
        cmos_post();
#endif
}

GLOBAL void cmos_pickup IFN0()
{
	read_cmos();
}

GLOBAL void cmos_io_attach IFN0()
{
	io_addr         i;

	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);
}

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 = 0x10;         /* Drive C type always 1 - then <<4 */
	 /* check whether D drive exists */
	 if ( *((CHAR *) config_inquire(C_HARD_DISK2_NAME, NULL)))
		 disk = 0x12;         /* 1 << 4 | 2 */
	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;


#ifndef NTVDM
        set_tod();
#endif

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

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

	cmos_has_changed = FALSE;
}

GLOBAL void cmos_update IFN0()
{
#ifndef PROD
	int             i;
#endif				/* nPROD */

	write_cmos();
#ifndef PROD
	if (io_verbose & CMOS_VERBOSE)
	{
		for (i = 0; i < 64; i++)
			fprintf(trace_file, "%02x ", cmos[i]);
		fprintf(trace_file, "\n");
	}
#endif
}

GLOBAL void  rtc_init IFN0()
{
	long            bintim;

	cmos_count = 0;
	bintim = host_time((long *) 0);
        ht = host_localtime(&bintim);
#ifdef NTVDM
        dwTickCount = GetTickCount ();
        dwAccumulativeMilSec = 0;
#endif

	/* 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;

	cmos[CMOS_SECONDS] = (*bin2bcd) (ht->tm_sec);
        cmos[CMOS_MINUTES] = (*bin2bcd) (ht->tm_min);
	cmos[CMOS_HOURS] = (*_24to12) ((*bin2bcd) (ht->tm_hour));
	cmos[CMOS_DAY_WEEK] = (*bin2bcd) (ht->tm_wday + 1);
	cmos[CMOS_MONTH] = (*bin2bcd) (ht->tm_mon + 1);
	cmos[CMOS_YEAR] = (*bin2bcd) (ht->tm_year);
	cmos[CMOS_CENTURY] = (*bin2bcd) (19);

	set_alarm();
}




#ifdef TEST_HARNESS
main()
{
	int             i;
	half_word       j;

	cmos_init();

	printf("\n");
	for (i = 0; i < CMOS_SIZE; i++)
	{
		cmos_outb(CMOS_PORT, i);
		cmos_inb(CMOS_DATA, &j);
		printf("%c", j);
	}
	printf("\n");
	for (i = 0; i < CMOS_SIZE; i++)
	{
		cmos_outb(CMOS_PORT, i);
		cmos_outb(CMOS_DATA, (i + 0x30));
		printf("%c", cmos[i]);
	}
	printf("\n");

	cmos_update();
}
#endif				/* TEST_HARNESS */
