Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

828 lines
19 KiB

#include "windows.h" /* included for Sleep() */
#include "insignia.h"
#include "host_def.h"
/*
* SoftPC Revision 2.0
*
* File : time_day.c
*
* Title : Time of day
*
* Sccs ID : @(#)time_day.c 1.27 4/20/94
*
* Description : Get/Set time of day
*
* Author : Henry Nash
*
* Notes : The PC-XT version has an interrupt 18.203 times a second
* to keep the counter up to date. We interrupt at a similar
* rate, but because of occasional heavy graphics or disk
* operations we lose ticks. In an attempt to still keep
* good time, we correct the stored time whenever the host
* detects a timer event, using the host time facilities.
*
* Upon reset time_of_day_init() grabs the host system time &
* puts it into the BIOS data area variables. Subsequent
* time of day accesses are maintained using the host system
* time. This enables well behaved programs to keep good time
* even if ticks are missed.
*
* Mods: (r3.4) : Make use of the host time structures host_timeval,
* host_timezone, and host_tm, which are equivalent
* to the Unix BSD4.2 structures.
*
* Removed calls to cpu_sw_interrupt and replaced with
* host_simulate
*/
#ifdef SCCSID
static char SccsID[]="@(#)time_day.c 1.27 4/20/94 Copyright Insignia Solutions Ltd.";
#endif
#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_BIOS.seg"
#endif
/*
* O/S include files.
*/
#include <stdlib.h>
#include <stdio.h>
#include TimeH
#include TypesH
/*
* SoftPC include files
*/
#include "xt.h"
#include "sas.h"
#include "ios.h"
#include CpuH
#include "bios.h"
#include "fla.h"
#include "host.h"
#include "timeval.h"
#include "timer.h"
#include "error.h"
#include "cmos.h"
#include "cmosbios.h"
#include "ica.h"
/*
* ===========================================================================
* Local static data and defines
* ===========================================================================
*/
#ifdef XTSFD
# define DAY_COUNT BIOS_VAR_START + 0xCE
#endif
#ifdef NTVDM
BOOL UpDateInProgress(void);
#define UPDATE_IN_PROGRESS UpDateInProgress()
IMPORT VOID host_init_bda_timer(void);
#else
#define UPDATE_IN_PROGRESS ( cmos_read(CMOS_REG_A ) & 0x80 )
static sys_addr user_timer_int_vector;
static IVT_ENTRY standard_user_timer_int_vector;
static IVT_ENTRY compatibility_user_timer_int_vector;
#endif
#ifdef ANSI
LOCAL void get_host_timestamp(word *, word *, half_word *);
LOCAL void write_host_timestamp(int, int);
LOCAL void TimeToTicks(int, int, int, word *, word *);
LOCAL void get_host_time(int *, int *, int *);
#else
LOCAL void get_host_timestamp();
LOCAL void write_host_timestamp();
LOCAL void TimeToTicks();
LOCAL void get_host_time();
#endif /* ANSI */
#define TICKS_PER_HOUR 65543L
#define TICKS_PER_MIN 1092L
#define TICKS_PER_SEC 18L
/*
* ============================================================================
* External functions
* ============================================================================
*/
void time_of_day()
{
/*
* BIOS function to return the number of PC interrupts since boot.
*/
half_word mask;
word low, high;
half_word overflow, alarm;
/*
* Block the Alarm signal whilst we are looking at the clock timer
*/
#ifdef BSD4_2
host_block_timer();
#endif
switch (getAH()) {
case 0x00: /* Get time */
#ifdef NTVDM
sas_loadw(TIMER_LOW, &low);
setDX(low);
sas_loadw(TIMER_HIGH, &high);
setCX(high);
sas_load(TIMER_OVFL, &overflow);
setAL(overflow);
sas_store(TIMER_OVFL, 0); /* Always write zero after read */
#else /* ! NTVDM */
#ifndef PROD
if (host_getenv("TIME_OF_DAY_FRIG") == NULL){
#endif
/*
* First get the time from the host
*/
get_host_timestamp(&low, &high, &overflow);
/*
* Use it to return the time AND overwrite the BIOS data
*/
setDX(low);
sas_storew(TIMER_LOW, low);
setCX(high);
sas_storew(TIMER_HIGH, high);
setAL(overflow);
sas_store(TIMER_OVFL, 0); /* Always write zero after read */
#ifndef PROD
}else{
SAVED int first=1;
if (first){
first = 0;
printf ("FRIG ALERT!!!! - time of day frozen!\n");
}
setDX(1);
sas_storew(TIMER_LOW, 1);
setCX(1);
sas_storew(TIMER_HIGH, 1);
setAL(0);
sas_store(TIMER_OVFL, 0); /* Always write zero after read */
}
#endif
#endif /* NTVDM */
break;
case 0x01: /* Set time */
/*
* Load the BIOS variables
*/
sas_storew(TIMER_LOW, getDX());
sas_storew(TIMER_HIGH, getCX());
sas_store(TIMER_OVFL, 0);
#ifndef NTVDM
/*
* Also the host timestamp
*/
write_host_timestamp(getDX(), getCX());
#endif
break;
case 2: /* read the real time clock */
#ifndef NTVDM
#ifndef PROD
if (host_getenv("TIME_OF_DAY_FRIG") == NULL){
#endif
#endif
if( UPDATE_IN_PROGRESS )
setCF(1);
else
{
setDH( cmos_read( CMOS_SECONDS ) );
setDL( cmos_read( CMOS_REG_B ) & 1 ); /* DSE bit */
setCL( cmos_read( CMOS_MINUTES ) );
setCH( cmos_read( CMOS_HOURS ) );
setCF(0);
}
#ifndef NTVDM
#ifndef PROD
}else{
SAVED int first=1;
if (first){
first = 0;
printf ("FRIG ALERT!!!! - real time clock frozen!\n");
}
setDH( 1 );
setDL( 0 ); /* DSE bit */
setCL( 1 );
setCH( 1 );
setCF(0);
}
#endif
#endif
break;
case 3: /* Set the real time clock */
if( UPDATE_IN_PROGRESS )
{
/* initialise real time clock */
cmos_write( CMOS_REG_A, 0x26 );
cmos_write( CMOS_REG_B, 0x82 );
cmos_read( CMOS_REG_C );
cmos_read( CMOS_REG_D );
}
cmos_write( CMOS_SECONDS, getDH() );
cmos_write( CMOS_MINUTES, getCL() );
cmos_write( CMOS_HOURS, getCH() );
alarm = ( cmos_read( CMOS_REG_B ) & 0x62 ) | 2;
alarm |= (getDL() & 1); /* only use the DSE bit */
cmos_write( CMOS_REG_B, alarm );
setCF(0);
break;
case 4: /* read the date from the real time clock */
#ifndef NTVDM
#ifndef PROD
if (host_getenv("TIME_OF_DAY_FRIG") == NULL){
#endif
#endif
if( UPDATE_IN_PROGRESS )
setCF(1);
else
{
setDL( cmos_read( CMOS_DAY_MONTH ) );
setDH( cmos_read( CMOS_MONTH ) );
setCL( cmos_read( CMOS_YEAR ) );
setCH( cmos_read( CMOS_CENTURY ) );
setCF(0);
}
#ifndef NTVDM
#ifndef PROD
}else{
SAVED int first=1;
if (first){
first = 0;
printf ("FRIG ALERT!!!! - date frozen!\n");
}
setDL( 1 );
setDH( 4 );
setCL( 91 );
setCH( 19 );
setCF(0);
}
#endif
#endif
break;
case 5: /* Set the date into the real time clock */
if( UPDATE_IN_PROGRESS )
{
/* initialise real time clock */
cmos_write( CMOS_REG_A, 0x26 );
cmos_write( CMOS_REG_B, 0x82 );
cmos_read( CMOS_REG_C );
cmos_read( CMOS_REG_D );
}
cmos_write( CMOS_DAY_WEEK, 0 );
cmos_write( CMOS_DAY_MONTH, getDL() );
cmos_write( CMOS_MONTH, getDH() );
cmos_write( CMOS_YEAR, getCL() );
cmos_write( CMOS_CENTURY, getCH() );
alarm = cmos_read( CMOS_REG_B ) & 0x7f; /* clear 'set bit' */
cmos_write( CMOS_REG_B, alarm);
setCF(0);
break;
case 6: /* set the alarm */
if( cmos_read(CMOS_REG_B) & 0x20 ) /* alarm already enabled? */
{
setCF(1);
#ifdef BSD4_2
host_release_timer();
#endif
return;
}
if( UPDATE_IN_PROGRESS )
{
/* initialise real time clock */
cmos_write( CMOS_REG_A, 0x26 );
cmos_write( CMOS_REG_B, 0x82 );
cmos_read( CMOS_REG_C );
cmos_read( CMOS_REG_D );
}
cmos_write( CMOS_SEC_ALARM, getDH() );
cmos_write( CMOS_MIN_ALARM, getCL() );
cmos_write( CMOS_HR_ALARM, getCH() );
inb( ICA1_PORT_1, &mask );
mask &= 0xfe; /* enable alarm timer int. */
outb( ICA1_PORT_1, mask );
alarm = cmos_read( CMOS_REG_B ) & 0x7f; /* ensure set bit turned off */
alarm |= 0x20; /* turn on alarm enable */
cmos_write( CMOS_REG_B, alarm );
break;
case 7:
alarm = cmos_read( CMOS_REG_B );
alarm &= 0x57; /* turn off alarm enable */
cmos_write( CMOS_REG_B, alarm );
break;
#ifdef XTSFD
case 0x0A:
{
word count;
sas_loadw(DAY_COUNT, &count);
setCX( count );
break;
}
case 0x0B:
sas_storew(DAY_COUNT, getCX() );
break;
default:
setCF( 1 );
#else
default:
; /* Do nothing */
#endif
}
setAH( 0 );
#ifdef BSD4_2
host_release_timer();
#endif
}
void time_int()
{
/*
* NT port does everything in 16 bit int08 handler
*/
#ifndef NTVDM
/*
* The BIOS timer interrupt routine.
*/
word low, high;
half_word motor_count, motor_flags;
/*
* Increment the low portion
*/
sas_loadw(TIMER_LOW, &low);
sas_storew(TIMER_LOW, ++low);
/*
1.9.92 MG
We need to actually load the timer high value before doing the 24 hour
test below here.
*/
sas_loadw(TIMER_HIGH, &high);
if (low == 0)
{
/*
* Timer has wrapped so update the high count
*/
sas_storew(TIMER_HIGH, ++high);
}
/*
* Wrap at 24 hrs
*/
if (high == 0x0018 && low == 0x00b0)
{
sas_storew(TIMER_LOW, 0x0000);
sas_storew(TIMER_HIGH, 0x0000);
sas_store(TIMER_OVFL, 0x01);
}
/*
* Decrement motor count
*/
sas_load(MOTOR_COUNT, &motor_count);
if(motor_count < 4)
motor_count = 0;
else
motor_count -= 4;
sas_store(MOTOR_COUNT, motor_count);
if (motor_count == 0)
{
/*
* Turn off motor running bits
*/
sas_load(MOTOR_STATUS,&motor_flags);
motor_flags &= 0xF0;
sas_store(MOTOR_STATUS,motor_flags);
/*
* Provided FLA is not busy, then actually turn the motor off.
*/
if (!fla_busy)
outb(DISKETTE_DOR_REG, 0x0C);
}
if ( getVM() ||
((standard_user_timer_int_vector.all != sas_dw_at(user_timer_int_vector)) &&
(compatibility_user_timer_int_vector.all != sas_dw_at(user_timer_int_vector))) )
/*
* There is a user time routine defined - so lets call it
*/
{
exec_sw_interrupt(USER_TIMER_INT_SEGMENT,
USER_TIMER_INT_OFFSET);
}
#endif /* NTVDM */
}
/*
* ============================================================================
* Internal Functions
* ============================================================================
*/
/*
* NT's sense of time in the bios data area is always
* kept in sync with the real systems tic count
* Most of the compensation to readjust tics according
* to the time of day stuff is not needed
*/
#ifndef NTVDM
/*
* The routines get_host_timestamp() and write_host_timestamp() are used to
* override the BIOS record of time, since timer events are known to be lost.
* Internally the routines work in seconds and microseconds, using the "timeval"
* struct provided by 4.2BSD. Since System V does not provide this, we supply a
* version of the 4.2BSD gettimeofday() function locally, making use of the
* System V function ticks().
*/
/*
* Our own timestamp for calculating PC time
*/
static struct host_timeval time_stamp;
LOCAL void get_host_timestamp(low, high, overflow)
word *low, *high;
half_word *overflow;
{
/*
* Provide the time in PC interrupts since startup, in the
* 32-bit value high:low. The parameter overflow is set to 1
* if a 24-hour boundary has been passed since the last call.
*/
struct host_timeval now, interval;
struct host_timezone junk; /* Not used */
unsigned long ticks; /* Total ticks elapsed */
long days;
SAVED long last_time = 0;
long hours, mins, secs;
/*
* Obtain the current time (since host boot-up)
*/
host_gettimeofday(&now, &junk);
/*
* Calculate how long has passed since the time stamp
*/
interval.tv_sec = now.tv_sec - time_stamp.tv_sec;
interval.tv_usec = now.tv_usec - time_stamp.tv_usec;
/*
* Handle the "borrow" correction
*/
if (interval.tv_sec > 0 && interval.tv_usec < 0)
{
interval.tv_usec += 1000000L;
interval.tv_sec -= 1;
};
/*
* TMM 8/1/92:
* -----------
*
* If someone changes the date forwards by >= 24 hours then we should set
* the overflow flag and ensure that we don't return an interval greater
* than 24 hours. If the date has changed by >= 48 hours then we will have
* lost a day. So we put up a panel to tell the user.
*
* If some one has set the date backwards and the interval has gone
* negative then all we can do is put up an error panel informing
* the user and ensure that we don't set the interval to a negative
* value.
*
* Notes:
*
* 1. Setting the overflow flag causes DOS to add a day onto the current
* date.
*
* 2. Setting the interval to a value greater than 24 hours causes DOS
* to print a "Divide Overflow" error.
*
* 3. Setting the interval to a -ve value causes DOS to go into an
* infinite loop printing "Divide Overflow".
*/
days = interval.tv_sec / (24 * 60 * 60);
if (days >= 1)
{
/*
* Someone has set the clock forwards, or we have been frozen for a
* couple of days. Ensure that the interval is not more than 24 hours,
* adjust the time_stamp to take care of the lost days.
*/
interval.tv_sec %= 24 * 60 * 60;
time_stamp.tv_sec += days * (24 * 60 * 60);
if (days > 1)
{
host_error (EG_DATE_FWD, ERR_CONT | ERR_RESET, "");
}
*overflow = 1;
}
else if (interval.tv_sec < 0)
{
/*
* Somebody has set the clock backwards, all we can do is maintain
* the same time that we had before the clock went back.
*/
time_stamp.tv_sec -= (last_time - now.tv_sec );
interval.tv_sec = now.tv_sec - time_stamp.tv_sec;
*overflow = 0;
host_error (EG_DATE_BACK, ERR_CONT | ERR_RESET, "");
}
else
*overflow = 0;
/*
* Convert seconds to hours/minutes/seconds
*/
hours = interval.tv_sec / (60L*60L); /* Hours */
interval.tv_sec %= (60L*60L);
mins = interval.tv_sec / 60L; /* Minutes */
secs = interval.tv_sec % 60L; /* Seconds */
/*
* Now convert the interval into PC ticks
* One tick lasts 54925 microseconds.
*/
ticks = hours * TICKS_PER_HOUR + mins * TICKS_PER_MIN +
secs * TICKS_PER_SEC + interval.tv_usec/54925 ;
/*
* Split the value into two 16-bit quantities and return
*/
*low = ticks & 0xffff;
*high = ticks >> 16;
}
LOCAL void write_host_timestamp(low, high)
int low, high;
{
/*
* Update our timestamp so that subsequent calls of get_host_timestamp
* return the correct value. A call of get_host_timestamp() made immediately
* after this call must return the values set here, so set the timestamp
* to be the current time less the value set here.
*/
struct host_timeval now, interval;
struct host_timezone junk; /* Not used */
long lowms;
/*
* Get the current time.
*/
host_gettimeofday(&now, &junk);
interval.tv_sec = high * 3599 + high/2; /* high ticks to seconds */
/*
* The multiply below can overflow, which has the interesting effect
* of making Softpc 1 hr 12 mins 40 secs (4300 secs, or 2^32 us) slow
* if booted in the last third of every hour. So compensate by
* letting the overflow occur and correcting interval by 4300 secs.
*/
lowms = (IS32) (low & 0xffff) * 54925 + (low & 0xffff)/2;
if (low > 39098)
interval.tv_sec += 4300;
interval.tv_sec += lowms / 1000000;
interval.tv_usec = lowms % 1000000;
/*
* The timestamp is the current time less this interval
*/
time_stamp.tv_sec = now.tv_sec - interval.tv_sec;
time_stamp.tv_usec = now.tv_usec - interval.tv_usec;
/*
* Handle the "borrow" correction, including negative timestamps
*/
if (time_stamp.tv_sec > 0 && time_stamp.tv_usec < 0)
{
time_stamp.tv_usec += 1000000L;
time_stamp.tv_sec -= 1;
}
else
if (time_stamp.tv_sec < 0 && time_stamp.tv_usec > 0)
{
time_stamp.tv_usec -= 1000000L;
time_stamp.tv_sec += 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 get_host_time( h, m, s )
int *h, *m, *s; /* hours, minutes and secs */
{
struct host_tm *tp;
time_t SecsSince1970;
SecsSince1970 = host_time(NULL);
tp = host_localtime(&SecsSince1970);
*h = tp->tm_hour;
*m = tp->tm_min;
*s = tp->tm_sec;
}
/*
** Take a normal time in hours, minutes and seconds then
** transmutate it into PC ticks since the beginning of the day.
*/
LOCAL void TimeToTicks( hour, minutes, sec, low, hi )
int hour, minutes, sec; /* inputs */
word *low, *hi; /* outputs */
{
unsigned long ticks; /* Total ticks elapsed */
/*
* Calculate ticks to date
*/
ticks = hour * TICKS_PER_HOUR + minutes * TICKS_PER_MIN +
sec * TICKS_PER_SEC;
/*
* Split the value into two 16-bit quantities and return
*/
*low = ticks & 0xffff;
*hi = ticks >> 16;
}
#endif /* ifndef NTVDM */
void time_of_day_init()
{
#ifndef NTVDM
int hour, minutes, sec; /* Current host time */
word low, hi; /* Host time in PC ticks */
/*
* Initialise the clock timer.
*/
get_host_time( &hour, &minutes, &sec ); /* get the time from the host */
TimeToTicks( hour, minutes, sec, &low, &hi ); /* convert to PC time */
sas_storew(TIMER_LOW, low );
sas_storew(TIMER_HIGH, hi );
sas_store(TIMER_OVFL,0x01);
/*
* Initialise the host time stamp
*/
write_host_timestamp( low, hi );
/*
* Build the standard IVT entry for the user timer interrupt(s)
*/
compatibility_user_timer_int_vector.all = ((double_word)ADDR_COMPATIBILITY_SEGMENT << 16) + ADDR_COMPATIBILITY_OFFSET;
standard_user_timer_int_vector.all = ((double_word)DUMMY_INT_SEGMENT << 16) + DUMMY_INT_OFFSET;
user_timer_int_vector = BIOS_USER_TIMER_INT * 4;
#endif /* NTVDM */
}
#ifdef NTVDM
/*
* NTVDM: the rtc is setup so that the UIP bit is set on a cmos
* port read if the cmos ports haven't been touched for at least
* 1 second. The IBM pc bios routine for accessing the clock
* polls RegA for UIP bit in a tight loop 600h times before
* failing the call. This means that MOST of the time the int1ah
* rtc fns almost never fail! To mimic this behaviour we poll
* the port until success, since we know that our rtc will clear
* UIP bit very quickly.
*/
BOOL UpDateInProgress(void)
{
while (cmos_read(CMOS_REG_A) & 0x80) {
Sleep(0); // give other threads a chance to work
}
return FALSE;
}
#endif