MCALL  .MODULE
MODULE MKTIME,RELEASE=Y01,VERSION=01,COMMENT=<MKTIME/MBGLIB>,IDENT=NO,LIB=YES

;                           Copyright (c) 2000
;                              Megan Gentry
;                       Framingham, Massachusetts
;                          All Rights Reserved
;                  Commercial Distribution Prohibited
;
; This software may be  freely copied  and used in its entirety for any
; purpose  so long as the above copyright notice and these comments are
; preserved  in  the  source  form  of  this  software, and  the binary
; copyright is preserved in any image built from it.
;
; The author has used best efforts in the research, design, development
; and  testing of  this software.  The author  makes no warranty of any
; kind,  expressed or  implied,  with  regard to  this software and its
; suitability for a given application.  The author shall not  be liable
; in any  event for  incidental or  consequential damages in connection
; with, or arising out of, the use or performance of this software. Use
; of this software constitutes acceptance of these terms.
;
; The author  is committed to making a best effort at fixing any errors
; found  in the  software and  would welcome  any reports  of problems,
; comments  or suggestions  regarding the software.   Please send email
; to <[email protected]>.
      .SBTTL  Edit History

;+
;
; Edit History:
;
; X01 (00) 11-Feb-2000 Megan Gentry
;       Initial coding.
;
; X01 (01) 14-Feb-2000 Megan Gentry
;       o Month entry is returned from locltm in range 0-11, per U*x
;         libary routine localtime(), change range checking and some
;         code to account for this
;       o Corrected TM Array entry names to be consistent with
;         U*x library routine localtime()
;
; Y01 (01) 14-Feb-2000 Megan Gentry
;       o Ready for initial release.
;
;-
      .SBTTL  Definitions

       .MCALL  .ASSUM

; Declare the system definition library

       .LIBRARY "SRC:SYSTEM.MLB"

; Specifiy the macros we'll be using

       .MCALL  .DSECT  .DS     .EQU

       .MCALL  .DATDF
       .DATDF

       .MCALL  .DTMDF
       .DTMDF

       .MCALL  .TIMDF
       .TIMDF
      .SBTTL  Local definitions

; Define local structure of TM Array

       .DSECT
       .DS     TM.SEC                  ; : Seconds after the minute (0 - 59)
       .DS     TM.MIN                  ; : Minutes after the hour (0 - 59)
       .DS     TM.HOUR                 ; : Hours since midnight (0 - 23)
       .DS     TM.MDAY                 ; : Day of month (1 - 31)
       .DS     TM.MON                  ; : Month of year (1 - 12)
       .DS     TM.YEAR                 ; : Years since 1900 (72 - 199)
       .DS     TM.WDAY                 ; : Days since Sunday (0 - 6)
       .DS     TM.YDAY                 ; : Days since January 1st (0 - 365)
       .DS     TM.ISDST                ; : Daylight Savings Time flag
       .DS     TM.GMT  2               ; : Seconds east of Greenwich
       .DS     TM.TZ                   ; : Time zone code
       .DS     TM.TICKS                ; : Ticks into a second (0 - 59)
      .SBTTL  Miscellaneous Definitions

; Define the globals

       .GLOBL  $SYSLB
       .GLOBL  $ARGER
       .GLOBL  $NXADR

; Define any conditionals

       .IIF NDF FT.EIS FT.EIS = 0      ;By default, don't use EIS
      .SBTTL  Revision and Copyright string for images

       .PSECT  .COPY.  RO,D

       .NLCSI  TYPE=I
       .ASCII  /Copyright (c) 2000 Megan Gentry/<15><12>
      .SBTTL  MKTIME  - Convert TM Array to RT-11 date/time block

;+
;
; MKTIME
;       This fortran-callable subroutine is modelled after the U*x
;       library routine mktime(), and is designed to take selected
;       contents of a TM Array and and fill an RT-11 date/time
;       block with the converted date and time information.
;
; Call:
;
;       CALL MKTIME ( TM , DATMBK )
;
;    where:
;       TM      is an INTEGER*2 array containing 13. entries which
;               contains the date and time to be converted (only
;               the TM.YEAR, TM.MON, TM.MDAY, TM.HOUR, TM.MIN, TM.SEC
;               and TM.TICKS entries are used for the conversion).
;
;               TM[0]   Seconds after the minute [0-59]
;               TM[1]   Minutes after the hour [0-59]
;               TM[2]   Hours since midnight [0-23]
;               TM[3]   Day of the month [1-31]
;               TM[4]   Month of the year [0-11]
;               TM[5]   Years since 1900 [0-199]
;
;       The following entries are not used in the conversion.
;
;               TM[6]   Days since Sunday [0-6]
;               TM[7]   Days since January 1 [0-365]
;               TM[8]   Daylight Savings Time flag
;               TM[9]   Seconds east of Greenwich (lo-order word) <future>
;               TM[10]  Seconds east of Greenwich (hi-order word) <future>
;                         (negative indicates west of Greenwich)
;               TM[11]  Time Zone code                            <future>
;
;       The following entry is used in the conversion
;
;               TM[12]  Ticks into second [0-59]
;
;       DATMBK  is an INTEGER*2 array containing 3. entries which will
;               be filled with the RT-11 formatted date and time as
;               follows:
;
;                       +----------+
;                       |   date   | [0] DTM.DT (16 bits, EEMMMMDDDDDYYYYY)
;                       +----------+
;                       | time, hi | [2] DTM.TM + TIM.HI
;                       +          +
;                       | time, lo | [4] DTM.TM + TIM.LO
;                       +----------+
;
; Return:
;       If there is no error, the DATMBK contains an RT-11 formatted
;       date and time.
;
; Errors:
;       o Insufficient arguments
;       o Invalid information in the TM Array
;
; Notes:
;       o This module was designed from the standpoint of producing
;         a date/time value specified in whatever form is appropriate
;         for the system.  This is why we don't use the U*x style 32-bit
;         integer containing seconds since Jan 1, 1900.
;
;       o The base year of the RT-11 Epoch is 1972, so dates prior
;         to 1972 cannot be converted.
;
;-

       .GLOBL  MKTIME

       .PSECT  SYS$I,I

       .ENABL  LSB

ERROR:  MOV     #$ARGER,R0
       SEC
       RETURN

MKTIME::
       MOV     (R5)+,R4                ;R4 = Argument count

       CALL    $NXADR                  ;Get first argument (-> TM Array)
       BCS     ERROR                   ;None specified!
       MOV     R0,R1                   ;R1 -> TM Array

       CALL    $NXADR                  ;Try for pointer to date/time block
       BCS     ERROR                   ;None specified!
       MOV     R0,R4                   ;R4 -> RT-11 Date/Time block

       CALL    TMVAL                   ;Validate the TM Array
       BCS     ERROR                   ;Information was invalid

; We have to do double precision here since the largest value can
; be 5183999. (60 ticks * 60 seconds * 60 minutes * 24 hours - 1 tick)
;       5183999 = 79 (*65536) + 6655.

       CLR     R2                      ;Clear high-order time
       MOV     TM.HOUR(R1),R3          ;Start with hours
       MOV     #60.,R0                 ;R0 = Minutes per hour
       CALL    DIMULT                  ;Do the multiplication (R0 * R2,R3)
       ADD     TM.MIN(R1),R3           ;Add in minutes
       ADC     R2                      ; in double-precision
       BCS     ERROR                   ;Overflow...
       MOV     #60.,R0                 ;R0 = Seconds per minute
       CALL    DIMULT                  ;Do the multiplication (R0 * R2,R3)
       ADD     TM.SEC(R1),R3           ;Add in seconds
       ADC     R2                      ; in double-precision
       BCS     ERROR                   ;Overflow...
       MOV     #60.,R0                 ;R0 = Ticks per second
       CALL    DIMULT                  ;Do the multiplication (R0 * R2,R3)
       ADD     TM.TICKS(R1),R3         ;Add in ticks
       ADC     R2                      ; in double-precision
       BCS     ERROR                   ;Overflow

; Here we validate the resulting time.  The time is patently invalid
; if the hi-order portion is greater than 79.  If it exactly equals 79,
; then the low order can be no larger than 6655.

       CMP     R2,#79.                 ;Is time invalid?
       BHI     ERROR                   ;Clearly yes...
       BLO     30$                     ;Clearly no...
       CMP     R3,#6655.               ;Maybe... Check lo-order limit...
       BHI     ERROR                   ;Clearly yes...
30$:

; Time is validated, lets put it in the supplied structure

       MOV     R2,<DTM.TM+TIM.HI>(R4)  ;Store high-order time
       MOV     R3,<DTM.TM+TIM.LO>(R4)  ;Store lo-order time

; Now we assemble an RT-11 date

       MOV     TM.MON(R1),R3           ;R3 = Month of year
       INC     R3                      ;Convert from range 0-11 to 1-12

       .IF EQ FT.EIS
       SWAB    R3                      ;Shift month into position
       ASL     R3                      ; ...
       ASL     R3                      ; ...
       .IFF ;EQ FT.EIS
       ASH     #<DA$MON>,R3            ;Shift month into position
       .ENDC ;EQ FT.EIS

       MOV     TM.MDAY(R1),R2          ;R2 = Day of month

       .IF EQ FT.EIS
       SWAB    R2                      ;Shift day into position
       ASR     R2                      ; ...
       ASR     R2                      ; ...
       ASR     R2                      ; ...
       .IFF ;EQ FT.EIS
       ASH     #<DA$DAY>,R2            ;Shift it into position
       .ENDC ;EQ FT.EIS

       BIS     R2,R3                   ;Merge with month

       MOV     TM.YEAR(R1),R2          ;R2 = Years since 1900
       ADD     #1900.,R2               ;R2 = Actual year
       SUB     #<DA.YR0>,R2            ;Subtract base of RT-11 Epoch
       BLT     ERROR                   ;Error if less than the base
       MOV     R2,R0                   ;Make a copy of it
       BIC     #^C<DA.YR>,R2           ;Isolate the year offset
       BIS     R2,R3                   ;Merge with day and month
       BIC     #<DA.YR>,R0             ;Isolate the epoch bits

       .IF EQ FT.EIS
       ASL     R0                      ;Shift epoch bits into position
       SWAB    R0                      ; ...
       .IFF ;EQ FT.EIS
       ASH     #<DA$AGE-DA$DAY>,R0     ;Shift epoch bits into position
       .ENDC ;EQ FT.EIS

       BIS     R0,R3                   ;Merge with day, month and year offset

       .Assume DTM.DT EQ 0
       MOV     R3,@R4                  ;Store converted date

       CLC                             ;Return success
       RETURN

       .DSABL  LSB
      .SBTTL  TMVAL   - Validates TM Array

;+
;
; TMVAL
;       Examines the contents of the TM Array and verifies that
;       each entry is within valid ranges.
;
; Call:
;       R1 -> TM Array
;
; Return:
;       All registers unaffected
;       C-bit clear, TM Array (portion we need) is valid
;       C-bit set, TM Array (portion we need) contains invalid information
;
; Notes:
;       o We could be pedantic about it and verify that the day field
;         is correct for a given month and year (accounting for leap
;         years), but then the routine would not be as general-purpose.
;
;-

       .PSECT  SYS$I,I

TMVAL:  MOV     R0,-(SP)                ;Save a few registers while
       MOV     R2,-(SP)                ; we use them
       MOV     #TMVTAB,R0              ;R0 -> TM Array validation table
10$:    CMP     @R0,#-1                 ;End of table?
       BEQ     30$                     ;Yep, TM array is okay
       MOV     R1,R2                   ;R2 -> Base of TM Array
       ADD     @R0,R2                  ;R2 -> Entry to check
       CMP     @R2,2(R0)               ;Is it lower than the minimum?
       BLT     20$                     ;Yes...
       CMP     @R2,4(R0)               ;No, is it higher than the maximum?
       BGT     20$                     ;Yes...
       ADD     #6,R0                   ;Nope, this TM Array offset was okay,
       BR      10$                     ; on to the next one...

20$:    SEC                             ;TM Array did not validate
       BR      40$

30$:    CLC                             ;TM Array validated okay
40$:    MOV     (SP)+,R2                ;*C* Restore
       MOV     (SP)+,R0                ;*C*  previously saved registers
       RETURN
      .SBTTL  DIMULT  - Double Precision Integer Multiply

;+
;
; DIMULT
;       Performs a double-precision integer multiply
;
; Call:
;       R2,R3 contain double-precision multiplicand
;       R0    contains single-precision multiplier
;
; Return:
;       PSW<C> = 0
;               R2,R3 contain double-precision product (32 bits)
;       PSW<C> = 1
;               overflow occurred
;
; Note:
;       o We don't check for multiplier of zero, since we know it
;         will always be some value (60.)
;       o We don't check for multiplicand of zero, since we don't
;         care
;
;-

       .PSECT  SYS$I,I

       .ENABL  LSB

DIMULT:
       CLR     -(SP)                   ;Reset lo-order product
       CLR     -(SP)                   ; and hi-order product
       BR      20$

10$:
       .IF EQ FT.EIS
       ASL     R3                      ;Double-precision shift multiplicand
       ROL     R2                      ; ...
       .IFF ;EQ FT.EIS
       ASHC    #1,R2                   ;Double-precision shift multiplicand
       .ENDC ;EQ FT.EIS

       BCS     40$                     ;Overflow (w/stacked items)

20$:    ASR     R0                      ;Shift multiplier right
       BCC     30$                     ;No addition required...

       ADD     R3,2(SP)                ;Add lo-order intermediate to product
       ADC     @SP                     ; w/carry
       ADD     R2,@SP                  ;Add hi-order intermediate to product
       BCS     40$                     ;Overflow (w/stacked items)

30$:    TST     R0                      ;More to do?
       BNE     10$                     ;Yes if non-zero...
       MOV     (SP)+,R2                ;Return hi-order product
       MOV     (SP)+,R3                ; and lo-order product
       CLC
       BR      50$                     ;Nope, we're all done...

40$:    CMP     (SP)+,(SP)+             ;Dump invalid product from stack
       SEC                             ;Failure due to overflow
50$:    RETURN

       .DSABL  LSB
      .SBTTL  Pure Data

       .PSECT  SYS$D,D

; TM Array validation table
;       First word is TM Array offset to check
;       Second word is lower limit
;       Third word is upper limit

TMVTAB: .WORD   TM.TICKS, 0, 59.
       .WORD   TM.SEC, 0, 59.
       .WORD   TM.MIN, 0, 59.
       .WORD   TM.HOUR, 0, 24.
       .WORD   TM.MDAY, 1, 31.
       .WORD   TM.MON, 0, 11.
       .WORD   TM.YEAR, 0, 199.
       .WORD   -1      ; ** Table Fence **

       .END