PROGRAM DateDemo_11_18_86;

(* ------------------------------------------------------------------------ *
 * ------------------------------------------------------------------------ *


      This program is a Turbo Pascal version of the CP/M 'Date' utility.
   It is not intended as a replacement, but only as a source of code
   for the different procedures and functions it uses.  When compiled
   it requires 12k of disk space.

   The program will do the following:

      1. get the date from the system clock and display it in
         day of week, month, day of month, and year.

      2. get the time from the system clock and display it in
         HH:MM:SS format.

      3. get the date, time, or both in any order from the command
         line, parse out the appropriate values, and set the date
         and/or time.  for example: date 11/5/86,14:30:20

      4. prompt for the date and time if the user types 'date set'
         at the command line.

      5. display the date and time continuously until a key is pressed
         if the user types 'date c'.

   My appreciation goes to Ken Kroninger for supplying the code that
   showed how the Bdos calls are made.  The code that Ken supplied was
   written by Milton Hicks and J. Bauernschub Jr.  It was lightly revised
   by Jim LaSalle.

   Requirements : Turbo Pascal v2.0 or higher.
                  CP/M 3.0 (CP/M plus) - banked version.


   Please address any comments or questions to Ben Diss.  On Qlink address
   mail to 'Duque', on GEnie address mail to 'BDiss'.


 * ------------------------------------------------------------------------ *
 * ------------------------------------------------------------------------ *)

{ The Following information will prove useful in understanding this
  program:

  Bdos 105 gets the date and time from the system clock and puts the
  information in a four byte data structure beginning at the address
  passed in the DE register pair.  This program uses two integers to
  input that data: DateInt, and TimeInt. These two integers are
  declared next to each other and so that DateInt will reside higher
  in memory so that the data will be passed to the approriate
  variables.  Bdos uses this four byte structure both in setting and
  in getting the date and time.  Bdos 104 is used to set the time.

       Byte 0 - 1 : Date field as an integer representing
                    the number of days since January 1, 1978.

       Byte 2     : Hours field in BCD.
       Byte 3     : Minutes field in BCD.

  In getting the time the Bdos passes the seconds in register A in BCD.
  Turbo Pascal returns the A register when the Bdos statement is used
  as a function.

  BCD stands for Binary Coded Decimal.  An array was declared that is
  used to transfer the BCD value to an integer value.  A BCD number in
  hexidecimal form when written appears as the integer equivalent.
  For example 12h has an integer value of 18 yet its BCD value is 12.
  A hexidecimal number that does not display integers has no BCD
  equivalent.                                                           }



Const
    Days : Array [1..12] of Integer = (31,28,31,30,31,30,31,31,30,31,30,31);
    BCD  : Array [0..89] of Integer = (0,1,2,3,4,5,6,7,8,9,99,99,99,99,99,99,
                             10,11,12,13,14,15,16,17,18,19,99,99,99,99,99,99,
                             20,21,22,23,24,25,26,27,28,29,99,99,99,99,99,99,
                             30,31,32,33,34,35,36,37,38,39,99,99,99,99,99,99,
                             40,41,42,43,44,45,46,47,48,49,99,99,99,99,99,99,
                             50,51,52,53,54,55,56,57,58,59);

Type
    Date_Type = String [30];
    Time_Type = String [8];

Var
    mm, dd, yy        : Integer;
    hh, mnts, ss      : Integer;
    am                : Boolean;

PROCEDURE Get_CPM_3_Date;

Var TimeInt, DateInt  : Integer;

Begin
    ss := BCD [Bdos (105, Addr (DateInt))];
    hh := BCD [Lo (TimeInt)];
    mnts := BCD [Hi (TimeInt)];
    yy := 78;
    While DateInt > 365 Do
    Begin
        If yy/4 = Int (yy/4) then DateInt := DateInt - 1;
        yy := yy + 1;
        DateInt := DateInt - 365;
    End;
    If yy/4 = Int (yy/4) then Days [2] := 29;
    mm := 1;
    While DateInt > Days [mm] Do
    Begin
        DateInt := DateInt - Days [mm];
        mm := mm + 1;
    End;
    dd := Trunc (DateInt);
End;

PROCEDURE Build_String (Var Date : Date_Type; Var Time : Time_Type);

Const
    Day_Array       : Array [0..6] of String [9] =
                    ('Sunday','Monday','Tuesday','Wednesday',
                     'Thursday','Friday','Saturday');

    Month_Array     : Array [1..12] of String [9] =
                    ('January','February','March','April','May','June','July',
                     'August','September','October','November','December');

Var Temp1, Temp2, Temp3 : String [4];

FUNCTION Day_Of_Week (Month, Day, Year : Integer) : Integer;

Var Century : Integer;

Begin
    If Month < 2 then
    Begin
        Month := Month + 10;
        Year  := Year - 1;
    End
    Else Month := Month - 2;
    Century := Year Div 100;
    Year := Year Mod 100;
    Day_Of_Week := (Day - 1 + ((13 * Month - 1) Div 5) + (5 * Year Div 4)+
                    Century Div 4 - 2 * Century + 1) Mod 7;
End;

Begin
    Str (dd,Temp1);
    Str (yy + 1900,Temp2);
    Date := Concat (Day_Array [Day_of_Week (mm, dd, yy + 1900)],', ',
                       Month_Array [mm],' ',Temp1,', ',Temp2);
    If hh >= 12 then
    Begin
        am := False;
        hh := hh - 12;
    End
    Else am := True;
    Str (hh,Temp1);
    Str (mnts,Temp2);
    Str (ss,Temp3);
    Time := Copy ('0' + Temp1, Length (Temp1), 2) + ':' +
            Copy ('0' + Temp2, Length (Temp2), 2) + ':' +
            Copy ('0' + Temp3, Length (Temp3), 2);
End;

PROCEDURE Set_Date_Time;

Var
    Month, Year, Number_Of_Days : Integer;
    TimeInt, DateInt            : Integer;
    LoTimeInt, HiTimeInt, I     : Integer;
    Chr                         : Char;

Begin
    DateInt := dd;
    If yy/4 = Int (yy/4) then Days [2] := 29;
    For Month := 1 to mm-1 Do DateInt := DateInt + Days [Month];
    For Year := yy downto 79 Do
    Begin
        Number_Of_Days := 365;
        If Year/4 = Int (Year/4) then Number_Of_Days := 366;
        DateInt := DateInt + Number_Of_Days;
    End;
    For I := 0 to 89 Do
    Begin
        If BCD [I] = hh then LoTimeInt := I;
        If BCD [I] = mnts then HiTimeInt := I;
    End;
    TimeInt := (HiTimeInt * 256) + LoTimeInt;
    Write ('Press any key to set the time');
    Read (Kbd,Chr);
    Bdos (104, Addr (DateInt));
End;

PROCEDURE Input_Date_And_Time (Var Date, Time : Time_Type);

Begin
    Write ('Enter the date in MM/DD/YY format: ');
    ReadLn (Date);
    Write ('Enter the time in HH:MM:SS format: ');
    ReadLn (Time);
End;

PROCEDURE Parse (Str              : Time_Type;
                 Delimeter        : Char;
             Var Val1, Val2, Val3 : Integer;
             Var Error            : Boolean);

Var Error1, Error2, Error3 : Integer;

Begin
    Val (Copy (Str, 1, Pos (Delimeter,Str) - 1), Val1, Error1);
    Delete (Str, 1, Pos (Delimeter,Str));
    Val (Copy (Str, 1, Pos (Delimeter,Str) - 1), Val2, Error2);
    Delete (Str, 1, Pos (Delimeter,Str));
    Val (Str, Val3, Error3);
    If (Error1 > 0) or (Error2 > 0) or (Error3 > 0) then Error := True
    Else Error := False;
End;

PROCEDURE Display_Date (Continuous : Boolean);

Var
    Old_String : String [38];
    Date       : Date_Type;
    Time       : Time_Type;
    Chr        : Char;

Begin
    Old_String := '';
    Repeat
        Get_CPM_3_Date;
        Build_String (Date, Time);
        If Old_String <> (Date + Time) then
        Begin
            Write (^m,Date,'; ',Time);
            If am then Write (' am') else Write (' pm');
            Old_String := Date + Time;
        End;
        If KeyPressed then
        Begin
            Continuous := False;
            Read (Kbd,Chr);
        End;
    Until Not Continuous;
    Halt;
End;

PROCEDURE Parse_Parameter;

Var
    ParStr      : String [30];
    Date, Time  : Time_Type;
    Error       : Boolean;
    I           : Integer;

Begin
    Date := '';
    Time := '';
    ParStr := '';
    For I := 1 to ParamCount Do ParStr := ParStr + ParamSTR (I);
    If Pos ('C',ParStr) > 0 then
    Begin
        Display_Date (True);
        Exit;
    End;
    If (Pos (',',ParStr) > 0) then
        If (Pos ('/',ParStr) < Pos (':',ParStr)) then
        Begin
            Date := Copy (ParStr, 1, Pos (',',ParStr) - 1);
            Time := Copy (ParStr, Pos (',',ParStr) + 1, Length (ParStr));
        End
        Else
        Begin
            Time := Copy (ParStr, 1, Pos (',',ParStr) - 1);
            Date := Copy (ParStr, Pos (',',ParStr) + 1, Length (ParStr));
        End
    Else If (Pos ('/',ParStr) > 0) and (Date = '') then
        Date := Copy (ParStr, 1, Length (ParStr))
    Else If (Pos (':',ParStr) > 0) and (Time = '') then
        Time := Copy (ParStr, 1, Length (ParStr))
    Else If Pos ('S',ParStr) > 0 then Input_Date_And_Time (Date,Time);
    Get_CPM_3_Date;
    If Date <> '' then Parse (Date, '/', mm, dd, yy, Error);
    If Time <> '' then Parse (Time, ':', hh, mnts, ss, Error);
    If (mm<0) or (mm>12) or (dd<0) or (dd>31) or (yy<0) or (yy>99)
        or (hh<0) or (hh>24) or (mnts<0) or (mnts>59) or (ss<0) or (ss>59)
        then Error := True;
    If Error then WriteLn ('ERROR: Illegal time/date specification.');
    If (Error = False) and ((Date <> '') or (Time <> '')) then Set_Date_Time;
End;

BEGIN
    If ParamCount > 0 then Parse_Parameter
    Else Display_Date (False);
END.