home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 September
/
Simtel20_Sept92.cdr
/
msdos
/
sysutl
/
nbscom12.arc
/
NBSCOM.C
next >
Wrap
Text File
|
1989-01-08
|
17KB
|
495 lines
/*-----------------------------------------------------------------------------
NBS ACTS Access Routine -- Dials NBS & sets DOS date/time accordingly.
Copyright (c) Franklin Antonio, 1988, All Rights Reserved.
This program (source and object) may be freely distributed and used for non-
commercial purposes only. If you redistribute this package, you must
distribute all the files (source, object, doc, ini), in their original,
unmodified, form. You may, additionally, distribute modified versions, with
the modified versions, but they must be clearly identified as modified, with
the original copyright statements intact, and the name and address of the
modifier must be clearly shown.
Compile with Microsoft C 5.1 ... cl /Ox /DMAIN nbscom.c
Edit History...
12/19/88 -fa- change abs to dabs in printf of time_change
added .ini file
put in direct i/o (to ignore modem control signals)
started hardware RTC stuff
12/20/88 -fa- replace sscanf() with atoi() in parse_a_time (AT:3.3ms-->0.3ms)
replace putc( ,stdio) with putc_screen() (AT:1.5ms-->0.5ms)
check ftime() {1.5ms} and kbhit() {0.5ms} only every 10th char
now polled i/o won't drop chars on even the slowest machine
01/07/89 -fa- Timeout was too short for some people using pulse dialing or
using long access codes. Changed default from 30 to 90 sec,
and made it a param settable from ini file.
----------------------------------------------------------------------------*/
#include <stdio.h>
#include <bios.h> /*needed by _bios_serialcom() */
#include <dos.h> /*needed by _dos_setxxxx() */
#include <conio.h> /*needed by kbhit() */
#include <sys\timeb.h> /*needed by ftime() */
#include <time.h> /*needed by tzset() */
#include <ctype.h> /*needed by isspace() */
#include <graph.h> /*needed by _clearscreen() */
#include <math.h> /*needed by fabs() */
/*function prototypes*/
double floatime(struct timeb);
double floatimenow(void);
void delay(double);
int parse_a_time(char *, int *, int *, int *, int *, int *, int *);
void Datek2(long,long *,long *,long *);
long KDAY2(long,long,long);
#define dabs(x) fabs(x) /*double abs*/
#define xbcd(bcd) ( 10*((bcd)>>4) + ((bcd)&0x0f) ) /*packed bcd to integer*/
#define bcdx(x) ( ((x)/10)<<4) + (x)%10 ) /*integer to packed bcd*/
/*parameters initialized here, but set from NBSCOM.INI file if available*/
int nbs_port = 0; /*port # for serial comm*/
int nbs_speed = 1200; /*baud rate for serial comm*/
int maxtime = 90; /*max online time (seconds*/
char dial_sequence[50] = "ATDT1-303-494-4774"; /*dial command buffer*/
char hangup_sequence[50] = "ATH"; /*hangup command buffer*/
/*items specific to standard 8250-based com ports*/
unsigned port[4] ={0x3f8,0x2f8,0,0}; /*standard IBM port addresses*/
#define UART_DATA 0 /*data register */
#define UART_MCR 4 /*modem control register*/
#define UART_LSR 5 /*line status register */
#define UART_MSR 6 /*modem status register*/
/*-----------------------------------------------------------------------------
main (stand alone program)
----------------------------------------------------------------------------*/
#ifdef MAIN
main() { /*compile main optionally*/
nbs_init_and_acts();
}
#endif
nbs_init_and_acts() {
nbs_init();
printf("NBSCOM 1.2 -- Copyright(c) Franklin Antonio, 1988\n");
delay(1.0);
call_nbs_acts();
}
/*-----------------------------------------------------------------------------
call_nbs_acts -- Places a call to National Bureau of Standards Advanced
Computer Time Service, reads date/time, & sets dos time accordingly.
----------------------------------------------------------------------------*/
call_nbs_acts() {
struct timeb start,now,time_before,time_after;
struct dosdate_t dosdate;
struct dostime_t dostime;
double time_change;
long lda,lmo,lyr,time;
int tick,j,j1,j2,yr1,yr2,mo1,mo2,da1,da2,hr1,hr2,mi1,mi2,se1,se2;
int rtc,yr,mo,da,hr,mi,se;
char *p,c,buf[80];
j = _COM_CHR8 | _COM_NOPARITY;
if(nbs_speed == 300) /*user want low speed?*/
j |= _COM_300; /*ok*/
else
j |= _COM_1200; /*only other speed NBS allows*/
_bios_serialcom(_COM_INIT,nbs_port,j); /*initialize modem port*/
_clearscreen(_GCLEARSCREEN); /*clear screen, so no scroll during*/
/*session. Elims scn scroll time*/
printf("Accessing National Bureau of Standards \n"
" Advanced Computer Time Service...\n\n");
printf("--- Modem dialog follows --- Type any key to abort. ---\n");
dial_the_modem(); /*initiate phonecall*/
/*now sit in a loop for up to maxtime copying characters to screen, and a
buffer. when lf seen, attempt to parse. two correct parses in a row which
differ by exactly 1 second causes successful termination. Timeout or a hit
from the keyboard causes unsuccessful termination. The most time-consuming
item in the loop (by far) is the ftime() call. ftime() takes 6ms on a
4.77 MHz PC, which is 2/3 of a character time @ 1200 baud. ftime() is a
good example of a library routine that could have been 20 times faster
if it had been written carefully. */
fflush(stdin); /*flush kb, so kbhit works*/
ftime(&start); /*begin timeout*/
p=buf; /*init ptr to line buffer*/
for(tick=0; 1; tick = (tick>=10) ? 0 : ++tick) { /*serial port poll loop*/
c = nbgetc_modem(); /*get a char from modem*/
if(c != 0) putc_screen(c); /*echo to screen*/
if(isprint(c) || isspace(c)) { /*elim trash*/
*p++ = c; /*good char to buffer*/
if(c == '\n' || p == buf+80-1) { /*eol?*/
*p=0; p=buf; /*tie off & reset ptr*/
j = parse_a_time(buf,&yr1,&mo1,&da1,&hr1,&mi1,&se1);
if(j==1) { /*a valid time line?*/
if(yr1==yr2 && mo1==mo2 && da1==da2 && hr1==hr2 &&
mi1==mi2 && se1==se2+1) { /*and it's 2nd one?*/
goto goodtime; /*zounds*/
}
else {
yr2=yr1; mo2=mo1; da2=da1; /*then it's 1st one*/
hr2=hr1; mi2=mi1; se2=se1; /*remember values*/
}
} /*end if j==1 */
} /*end if c == */
} /*end if isprint( */
if(tick == 0) { /*occasionally check*/
ftime(&now); /*for timeout*/
if(now.time > start.time + maxtime) break;
}
if(tick == 5) { /*occasionally check*/
if(kbhit()) break; /*for manual abort*/
}
} /*end for(tick */
if(kbhit()) printf("--- aborted by user ---\n");
else printf("--- %d seconds elapsed. aborted. ---\n",maxtime);
time_change = 0.; /*there was no change*/
goto hangup; /*attempt to hang up phone*/
/* ------- Here when we have read a good time. Tell DOS. ---------*/
/*we have to adjust the UTC time we've just received by our local timezone
so we can set DOS date/time local. Adjustment may be negative, and the lib.
routine mktime() can't handle negative arguments, so we convert using local
routines to a single time variable, adjust, then convert back. messy.*/
goodtime: /*got a valid time*/
tzset(); /*make timezone valid*/
time = 24L*KDAY2((long)da1,(long)mo1,(long)1900+yr1) + hr1;
time -= timezone/(60L*60L); /*adjust for timezone*/
Datek2(time/24L,&lda,&lmo,&lyr); /*get new d/m/y*/
hr1 = time%24L; /*fractional day*/
/*Now we have local time. Do all the time-setting operations before printing
any of the results, or doing any of the calls that use floating-point
operations, so that we get everything set before much time passes. */
ftime(&time_before); /*time before setting*/
dostime.hour = hr1;
dostime.minute = mi1;
dostime.second = se1;
dostime.hsecond = 0;
j2 = _dos_settime(&dostime); /*set dos time*/
dosdate.year = lyr;
dosdate.month = lmo;
dosdate.day = lda;
dosdate.dayofweek = 0;
j1 = _dos_setdate(&dosdate); /*set dos date*/
ftime(&time_after); /*time after setting*/
rtc = read_hw_rtc(&yr,&mo,&da,&hr,&mi,&se); /*hardware realtime clock*/
printf("--- Setting DOS Date & Time to local: %02d/%02d/%d %02d:%02d:%02d ---\n",
dosdate.month,dosdate.day,dosdate.year,
dostime.hour,dostime.minute,dostime.second);
if(j1 != 0) printf("--- DOS set Date failed ---\n");
if(j2 != 0) printf("--- DOS set Time failed ---\n");
if(j1 == 0 && j2 == 0) {
time_change = floatime(time_after) - floatime(time_before); /*change*/
printf("--- Your DOS time was %s NBS by %.2f seconds ---\n",
time_change>0 ? "behind" : "ahead of",
dabs(time_change) );
}
if(rtc) printf("--- Info only: Your hardware realtime clock reads: "
"%02d/%02d/%d %02d:%02d:%02d ---\n",
mo,da,yr,hr,mi,se);
goto hangup; /*attempt to hang up phone*/
hangup:
hangup_the_modem(); /*terminate phone call*/
printf("--- Done --- Phone call duration was %.1f seconds. ---\n",
floatimenow() - floatime(start) - time_change );
return 0;
}
/*-----------------------------------------------------------------------------
dial_the_modem -- does just that. initiates phone call.
----------------------------------------------------------------------------*/
dial_the_modem() {
control_modem(0x3); /*turn on DTR & RTS*/
printf ("+++");
puts_modem("+++"); /*enter hayes command mode*/
delay(1.25); /* +++ guardtime*/
printf( dial_sequence); /*dial!*/
puts_modem(dial_sequence);
printf("\n"); /*dial command terminator*/
puts_modem("\r\n");
delay(0.1); /*avoid echo last char of dial*/
}
/*-----------------------------------------------------------------------------
hangup_the_modem -- does just that. terminates phone call.
----------------------------------------------------------------------------*/
hangup_the_modem() {
printf ("+++");
puts_modem("+++"); /*enter hayes command mode*/
delay(1.25); /* +++ guardtime*/
printf( hangup_sequence); /*hangup command*/
puts_modem(hangup_sequence);
printf("\n");
puts_modem("\r\n");
control_modem(0x00); /*drop DTR (hangs up some modems) */
}
/*-----------------------------------------------------------------------------
parse_a_time -- attempt to parse a "time" line from NBS
MJD YR MO DA H M S ST S UT1 msADV OTM
nbs format--> 47511 88-12-16 06:03:44 00 0 -.1 045.0 UTC(NBS) *
@ 1200 baud 47511 88-12-16 06:03:45 00 0 -.1 045.0 UTC(NBS) *
H M S msADV OTM
nbs format--> 06:03:44 045.0 * <--but how do i get the date?
@ 300 baud 06:03:45 045.0 *
----------------------------------------------------------------------------*/
parse_a_time(char *p, int *yr, int *mo, int *da,
int *hr, int *mi, int *se) {
int mjd;
/*This scanf takes 3.3ms on my AT, but the following code that replaces it
takes 0.3ms, so is a clear winner.
j = sscanf(line,"%d %d-%d-%d %d:%d:%d",&mjd,yr,mo,da,hr,mi,se);
*/
while(isspace(*p)) p++;
while(isdigit(*p)) p++; /*past mjd*/
while(isspace(*p)) p++;
*yr = atoi(p); p+=3; /*date*/
*mo = atoi(p); p+=3;
*da = atoi(p); p+=3;
while(isspace(*p)) p++;
*hr = atoi(p); p+=3; /*time*/
*mi = atoi(p); p+=3;
*se = atoi(p); p+=3;
if(*yr<88 || *yr>99 || *mo<=0 || *mo>12 || *da<=0 || *da>31 ||
*hr<0 || *hr>23 || *mi<0 || *mi>59 || *se<0 || *se>59 ) return 0;
/*and legal values everywhere?*/
return 1; /*good, then we got it*/
}
/*-----------------------------------------------------------------------------
delay -- spinloop delay for wait seconds.
----------------------------------------------------------------------------*/
void delay(double wait) {
struct timeb start;
ftime(&start);
while(floatimenow() < floatime(start) + wait);
}
/*-----------------------------------------------------------------------------
floatime -- converts a timeb structure to floating point seconds. Used
outside critical time sections.
----------------------------------------------------------------------------*/
double floatime(struct timeb t) {
return t.time + .001*t.millitm;
}
double floatimenow() {
struct timeb t;
ftime(&t);
return floatime(t);
}
/*-----------------------------------------------------------------------------
nonblocking read character from serial port using direct i/o
----------------------------------------------------------------------------*/
nbgetc_modem() {
if((inp(port[nbs_port]+UART_LSR) & 0x01) == 0) /*character ready?*/
return 0; /*no*/
return inp(port[nbs_port]+UART_DATA); /*yes*/
}
/*-----------------------------------------------------------------------------
write character to serial port using direct i/o
----------------------------------------------------------------------------*/
putc_modem(char c) {
while((inp(port[nbs_port]+UART_LSR) & 0x20) == 0); /*wait for holding reg*/
outp(port[nbs_port]+UART_DATA, c);
}
/*-----------------------------------------------------------------------------
write string to serial port using direct i/o
----------------------------------------------------------------------------*/
puts_modem(char *s) {
while(*s != 0)
putc_modem(*s++);
}
/*-----------------------------------------------------------------------------
set modem-control signals on serial port using direct i/o
----------------------------------------------------------------------------*/
control_modem(char c) {
outp(port[nbs_port]+UART_MCR,c);
}
/*-----------------------------------------------------------------------------
put character to screen using bios. putc(c,stdio) took 1.4 mS, this takes
0.5 mS on my AT.
----------------------------------------------------------------------------*/
putc_screen(char c) {
static union REGS reg;
reg.h.ah = 0xE; /*write tty style*/
reg.h.al = c; /*this char*/
reg.h.bh = 0; /*scn page 0*/
int86(0x10,®,®); /*do it*/
}
/*----------------------------------------------------------------------------
ACM Algorithm 199
convert calendar date to day number
K=1 at March 1, 1900.
--------------------------------------------------------------------------*/
long KDAY2(long Iday,long Month,long Iyear) {
long M,IY,Kday;
if(Iday < 1 || Iday > 31 || Month < 1 || Month > 12
|| Iyear < 1900 || Iyear > 1999) printf("?kday: ilg param\n");
M=Month-3 ; IY=Iyear-1900;
if(Month <= 2) {
M=Month+9;
IY=IY-1;
}
Kday=(1461*IY)/4+(153*M+2)/5+Iday;
return Kday;
}
/*----------------------------------------------------------------------------
ACM algorithm 199
day number to calendar date
Valid from 3/1/1900 thru 2/28/2000.
--------------------------------------------------------------------------*/
void Datek2(long K,long *Iday,long *Month,long *Iyear) {
long day,month,year;
if(K < 0 || K > 36500) printf("?datek: ilg param\n");
year=(4*K-1)/1461;
day=4*K-1-1461*year;
day=(day+4)/4;
month=(5*day-3)/153;
day=5*day-3-153*month;
day=(day+5)/5;
month=month+3;
year=year+1900;
if(month >= 13) {
month=month-12;
year=year+1;
}
*Iday=day; *Month=month; *Iyear=year;
}
/*----------------------------------------------------------------------------
Parameter initialization (reads .INI file)
--------------------------------------------------------------------------*/
nbs_init() {
FILE *init;
char buf[80],*p;
init = fopen("nbscom.ini","r"); /*open .ini file*/
if(init == NULL) return 0; /*if no file, use defaults*/
while(NULL != fgets(buf,80,init)) { /*until end-of-file*/
sscanf(buf," dial = %50s", dial_sequence);
sscanf(buf," hangup = %50s",hangup_sequence);
sscanf(buf," port = %d", &nbs_port);
sscanf(buf," speed = %d", &nbs_speed);
sscanf(buf," maxtime = %d", &maxtime);
}
fclose(init); /*close .ini file*/
}
/*-----------------------------------------------------------------------------
Routines to diddle the hardware realtime-clock (via BIOS)
It's difficult to set the RTC accurately, because its least digit is seconds.
Furthermore, the RTC can refuse to be read (if it's propagating a carry at
the time). I plan to write some code that overcomes these difficulties, so
that i can then set the RTC accurately.
This only works on an IBMAT or later BIOS. It would also work if aftermarket
RTCs came with TSRs that implemented the AT BIOS RTC functions, but none do.
**NOTFINISHED**
----------------------------------------------------------------------------*/
read_hw_rtc(int *yr,int *mo,int *da,int *hr,int *mi,int *se) {
union REGS reg;
reg.h.ah = 4; /*read date from RTC*/
reg.h.cl = reg.h.ch = 0xff; /*marker*/
int86(0x1A,®,®); /*BIOS call*/
if(reg.h.cl==0xff || reg.h.ch==0xff) return 0; /*if no RTC BIOS, fail return*/
*yr = 100*xbcd(reg.h.ch) + xbcd(reg.h.cl);
*mo = xbcd(reg.h.dh);
*da = xbcd(reg.h.dl);
reg.h.ah = 2; /*read time from RTC*/
int86(0x1A,®,®); /*BIOS call*/
*hr = xbcd(reg.h.ch);
*mi = xbcd(reg.h.cl);
*se = xbcd(reg.h.dh);
}