/****************************************************************************/
/* PROGRAM:  ROM                                                            */
/* VERSION:  1.0                                                            */
/*    DATE:  December 18, 1989                                              */
/*  AUTHOR:  Sydney M. Willett                                              */
/* DESCRIPTION:  Will either save to file, or restore from file, CMOS ROM   */
/*               data used on AT class IBM or compatible computers.         */
/* INPUTS:  ...Command Line...                                              */
/*          s - save CMOS ROM data to a file named "CMOSROM.DAT"            */
/*          r - restore CMOS ROM data from a file named "CMOSROM.DAT"       */
/*   hh:mm:ss - hours:minutes:seconds in 24 hour format to reconfigure CMOS */
/* mm/dd/yyyy - month/day/year to reconfigure CMOS                          */
/*          b - cold boot the system after program executes                 */
/* OUTPUTS:  Will create a file named "CMOSROM.DAT" when saving CMOS ROM    */
/*           data.  Will restore CMOS ROM data from the file.               */
/****************************************************************************/


/*============================================================================
INCLUDES
----------------------------------------------------------------------------*/
#include <conio.h>
#include <dir.h>
#include <dos.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>


/*============================================================================
EQUATES
----------------------------------------------------------------------------*/
#define COLD 0


/*============================================================================
DATA DECLARATIONS
----------------------------------------------------------------------------*/
typedef unsigned char BYTE ;
BYTE action = 0 ;
BYTE dateflag = 0 ;
BYTE timeflag = 0 ;
BYTE cent, hour, mday, min, mon, sec, year ;
int temp ;


/*============================================================================
FUNCTION DECLARATIONS
----------------------------------------------------------------------------*/
extern BOOT ( int cold_warm ) ;
unsigned char int2bcd ( int integer ) ;
int parse_nbr ( char *string ) ;
void time_date ( void ) ;


/*============================================================================
FUNCTION:  main
PURPOSE:  Will either save to file, or restore from file, CMOS ROM data used
          on AT class IBM or compatible computers.
RETURNS:  None
ARGUMENTS:  ...Command Line...
            s - save CMOS ROM data to a file named "CMOSROM.DAT"
            r - restore CMOS ROM data from a file named "CMOSROM.DAT"
     hh:mm:ss - hours:minutes:seconds in 24 hour format to reconfigure CMOS
   mm/dd/yyyy - month/day/year to reconfigure CMOS
            b - cold boot the system after program executes
GLOBAL VARIABLES:  action, cent, dateflag, hour, min, mday, mon, sec, temp,
                   timeflag, year
FUNCTIONS USED:  BOOT, cputs, fclose, fcloseall, fgetc, fopen, fread, fwrite,
                 inportb, int2bcd, int86, localtime, outportb, parse_nbr,
                 remove, strchr, strlwr, remove, time, time_date
----------------------------------------------------------------------------*/
void main ( int argc, char **argv )
{
FILE *cmosfile ;
struct ffblk Ffblk ;
BYTE cmosdata[50] ;
BYTE bootflag = 0 ;
int arglen, i, j, portbyte ;

  for (i=1 ; i<argc && action!=0xFF ; i++) /* command line parser */
  {
    arglen = strlen(argv[i]) ;
    strlwr(argv[i]) ;
    j = 0 + (argv[i][0]=='/' || argv[i][0]=='-') ;
    if ( argv[i][j]=='r' || argv[i][j]=='s' ) /* save or restore */
      action = argv[i][j] ;
    else
    {
      if ( argv[i][j] == 'b' ) /* boot */
        bootflag++ ;
      else
      {
        if ( strchr(&argv[i][j],':') != NULL ) /* time input */
        {
          j += parse_nbr(&argv[i][j]) ;
          hour = temp ;
          if ( hour > 23 )
            action = 0xFF ;
          else
          {
            j += parse_nbr(&argv[i][j]) ;
            min = temp ;
            if ( min > 59 )
              action = 0xFF ;
            else
            {
              j += parse_nbr(&argv[i][j]) ;
              sec = temp ;
              if ( j>arglen+1 || sec>59 )
                action = 0xFF ;
              else
                timeflag++ ;
            }
          }
        }
        else /* date input (or maybe an error) */
        {
          j += parse_nbr(&argv[i][j]) ;
          mon = temp ;
          if ( mon > 12 )
            action = 0xFF ;
          else
          {
            j += parse_nbr(&argv[i][j]) ;
            mday = temp ;
            if ( mday > 31 )
              action = 0xFF ;
            else
            {
              j += parse_nbr(&argv[i][j]) ;
              if ( j>arglen+1 || temp<1970 || temp>2099 )
                action = 0xFF ;
              else
              {
                cent = temp/100 ;
                year = temp%100 ;
                dateflag++ ;
              }
            }
          }
        }
      }
    }
  }
  switch ( action )
  {
    case 'r' : cmosfile = fopen("CMOSROM.DAT","rb") ;
               if ( cmosfile == NULL )
                 cputs("\n\r*** Error Opening File CMOSROM.DAT ***\n\r") ;
               else
               {
                 findfirst("CMOSROM.DAT",&Ffblk,0) ;
                 if ( Ffblk.ff_fsize != 50 )
                   cputs("\n\r*** File CMOSROM.DAT Corrupted ***\n\r") ;
                 else
                 {
                   fread(&cmosdata[0],sizeof(BYTE),50,cmosfile) ; /* read cmos data file */
                   for (portbyte=14 ; portbyte<=63 ; portbyte++) /* restore cmos data */
                   {
                     outportb(0x70,portbyte) ;
                     outportb(0x71,cmosdata[portbyte-14]) ;
                   }
                   time_date() ;
                   if ( bootflag ) /* cold boot system if requested */
                     BOOT(COLD) ;
                 }
               }
               break ;
    case 's' : for (portbyte=14 ; portbyte<=63 ; portbyte++) /* read cmos data */
               {
                 outportb(0x70,portbyte) ;
                 cmosdata[portbyte-14] = inportb(0x71) ;
               }
               cmosfile = fopen("CMOSROM.DAT","wb") ; /* open cmos data file */
               if ( cmosfile == NULL ) /* error opening file */
                 cputs("\n\r*** Error Opening File CMOSROM.DAT ***\n\r") ;
               else
               {
                 fwrite(&cmosdata[0],sizeof(BYTE),50,cmosfile) ;
                 fclose(cmosfile) ; /* close file */
                 cmosfile = fopen("CMOSROM.DAT","rb") ; /* open cmos file to read */
                 if ( cmosfile == NULL )                /* error opening file */
                 {
                   cputs("\n\r*** Error Reading File CMOSROM.DAT ***\n\r") ;
                   remove("CMOSROM.DAT") ;
                 }
                 else
                 {
                   for (i=0 ; i<50 ; i++)
                   {
                     if ( cmosdata[i] != fgetc(cmosfile) )
                     {
                       cputs("\n\r*** Error Comparing File CMOSROM.DAT and CMOS ROM Data ***\n\r") ;
                       remove("CMOSROM.DAT") ;
                       break ;
                     }
                   }
                   time_date() ;
                   if ( bootflag )
                     BOOT(COLD) ;
                 }
               }
               break ;
    default  : if ( dateflag || timeflag || bootflag )
               {
                 time_date() ;
                 if ( bootflag )
                   BOOT(COLD) ;
               }
               else
               {
                 cputs("\n\r*** Invalid Command Line Parameter(s) ***\n\r") ;
                 cputs("\n\rUsage: ROM [action] [time] [date] [boot]\n\r") ;
                 cputs("  action - s           save CMOS ROM data to file\n\r") ;
                 cputs("           r           restore CMOS ROM data from file\n\r") ;
                 cputs("    time - hh:mm:ss    24 hour time\n\r") ;
                 cputs("    date - mm/dd/yyyy  month/day/year\n\r") ;
                 cputs("    boot - b           cold boot the system after executing program\n\r") ;
               }
               break ;
  }
  fcloseall() ;
}


/*============================================================================
FUNCTION NAME:  int2bcd
PURPOSE:  Converts an integer to a single byte binary coded decimal (BCD).
          Will truncate any digits more than two.
RETURNS:  unsigned char - BCD conversion of the argument
ARGUMENTS:  int integer - integer to be conveted to BCD
GLOBAL VARIABLES:  None
FUNCTIONS USED:  None
----------------------------------------------------------------------------*/
BYTE int2bcd ( int integer )
{
BYTE bcd ;

  bcd = integer/10 ;
  bcd <<= 4 ;
  bcd += integer%10 ;
  return(bcd) ;
}


/*============================================================================
FUNCTION NAME:  parse_nbr
PURPOSE:  To convert consecutive ascii digits into an integer
RETURNS:  int = Number of characters converted plus one
ARGUMENTS:  char *string = pointer to the first character of string to convert
GLOBAL VARIABLES:  temp
FUNCTIONS USED:  None
----------------------------------------------------------------------------*/
int parse_nbr ( char *string )
{
int counter ;

  temp = 0 ;
  for ( counter=0 ; 1 ; counter++ )
  {
    if ( string[counter]<'0' || string[counter]>'9' )
      break ;
    else
      temp *= 10 ;
      temp += string[counter]-0x30 ;
  }
  return(counter+1) ;
}


/*============================================================================
FUNCTION NAME:  time_date
PURPOSE:  To update CMOS ROM and system time and date if needed.
RETURNS:  None
ARGUMENTS:  None
GLOBAL VARIABLES:  action, cent, dateflag, hour, min, mday, mon, sec, timeflag,
                   year
FUNCTIONS USED:  int86, intdos
----------------------------------------------------------------------------*/
void time_date ( void )
{
struct tm *local ;
time_t bintime ;
union REGS Regs ;

  time(&bintime) ; /* get system date and time */
  local = localtime(&bintime) ;
  if ( timeflag ) /* set system time if user supplied */
  {
    Regs.h.ch = hour ;
    Regs.h.cl = min ;
    Regs.h.dh = sec ;
    Regs.h.dl = 0 ;
    Regs.h.ah = 0x2D ;
    intdos(&Regs,&Regs) ;
  }
  else /* use system time to update cmos if not user supplied */
  {
    hour = local->tm_hour ;
    min = local->tm_min ;
    sec = local->tm_sec ;
  }
  if ( timeflag || action=='r' ) /* update time */
  {
    Regs.h.ch = int2bcd(hour) ;
    Regs.h.cl = int2bcd(min) ;
    Regs.h.dh = int2bcd(sec) ;
    Regs.h.dl = 0 ;
    Regs.h.ah = 3 ;
    int86(0x1A,&Regs,&Regs) ;
  }
  if ( dateflag ) /* set system date if user supplied */
  {
    Regs.x.cx = cent*100 + year ;
    Regs.h.dh = mon ;
    Regs.h.dl = mday ;
    Regs.h.ah = 0x2B ;
    intdos(&Regs,&Regs) ;
  }
  else /* use system date to update cmos if not user supplied */
  {
    cent = 19 ;
    year = local->tm_year ;
    mon = local->tm_mon + 1 ;
    mday = local->tm_mday ;
  }
  if ( dateflag || action=='r' ) /* update date */
  {
    Regs.h.ch = int2bcd(cent) ;
    Regs.h.cl = int2bcd(year) ;
    Regs.h.dh = int2bcd(mon) ;
    Regs.h.dl = int2bcd(mday) ;
    Regs.h.ah = 5 ;
    int86(0x1A,&Regs,&Regs) ;
  }
}
