; Dates operations
;
; (C)1989 By MEDA COMP, INC.
;
;-All commercial rights reserved, etc.
;-No warranties and/or guarranties of any kind, etc.
;-Not responsible for damages resulting from the use of this program, etc.
;
; I can be reached at:
;               Meda Comp Computer Systems
;               18587 Sigma Rd. #220
;               San Antonio Tx 78258
;               (210) 490-9008
;               8am-5pm
;
;

       VEDIT=3

       SYM
       OBJNAM  .SBR

       SEARCH  SYS
       SEARCH  SYSSYM
       SEARCH  MACLIB
       SEARCH  DATES.UNV

       RADIX   10

       DEFAULT VEDIT,2
       DEFAULT $$MFLG,PV$RSM
       DEFAULT $$SFLG,PH$REE!PH$REU

       PHDR    -1,$$MFLG,$$SFLG

       EXTERN  $IDTIM,$DSTOI,$ODTIM,$FLSET
;
;
       .OFINI
       .OFDEF  ASCIIB,12       ;ascii work area
       .OFSIZ  IMPSIZ

       XCMEM   IMPSIZ          ; Check for enough memory

       CLEAR   @A4,IMPSIZ

       CNTURY  = 2415021               ; CONVERT  IDATE<-->CDATE

; SOME BASIC XCALL INTERFACING MACROS
;
; Move the type field from the Nth parameter block off the
; BASIC XCALL parameters list into DEST
;
       DEFINE  BTYPE REG,DEST
1$$     =       2+^D10*<REG-1>
       CLR     DEST
       MOVW    1$$(A3),DEST
       ANDW    #^H000F,1$$(A3)         ;mask off all but last 4 bits
       ENDM
;
; Move the address field from the Nth parameter block off the
; BASIC XCALL parameters list into DEST
;
       DEFINE  BADRS REG,DEST
1$$     =       4+^D10*<REG-1>
       MOVL    1$$(A3),DEST
       ENDM
;
;
; Move the size field from the Nth parameter block off the
; BASIC XCALL parameters list into DEST
;
       DEFINE  BSIZE REG,DEST
1$$     =       8+^D10*<REG-1>
       MOVL    1$$(A3),DEST
       ENDM
;
DATES:
       MOV     SP,A5   ; Save the stack pointer
       CALL    GETFUN  ; Get function in to D0
       CMP     D0,#1
       JEQ     CONVRT  ; Convert DATE1 to DATE2
       CMP     D0,#2
       JEQ     COMPDT  ; COMPUTE DATE2 = DATE1+ DAYS
       CMP     D0,#3
       JEQ     DIFFDT  ; COMPUTE DAYS  = DATE1- DATE2
       CMP     D0,#4
       JEQ     COMPYW  ; COMPUTE DATE = YEAR,YWEEK,DOW
       CMP     D0,#5
       JEQ     COMPMW  ; COMPUTE DATE = YEAR,MONTH,MWEEK,DOW
       CMP     D0,#6
       JEQ     COMPEM  ; COMPUTE DATE = END OF MONTH
       CMP     D0,#7
       JEQ     IODTIM  ; $ODTIM INTERFACE
       CMP     D0,#8.
       JEQ     IIDTIM  ; $IDTIM INTERFACE
       JMP     FUNERR

EXIT:   ;return to caller
       BADRS   2,A2
       MOVW    D5,@A2          ;set return flag
       MOV     A5,SP           ;restore stack pointer
       RTN                     ;return to caller

CONVRT: ; Convert from any format to any format
       CMPW    @A3,#4          ;4 args?
       JNE     ARGERR          ; no
       CALL    GETID1          ; convert DATE1 to internal format D2
       CALL    PUTIDT          ; convert INTERNAL D2 to DATE2
       BR      EXIT            ; return to caller

COMPDT: ; Function 2. DATE2 = DATE1+DAYS
       CMPW    @A3,#5          ;5 vars passed?
       JNE     ARGERR          ; no
       CALL    GETID1          ;DATE1 to internal in D2
       MOV     #5,D0
       CALL    SETXC
       CALL    XVALUE          ;days to add in D1
       ADD     D1,D2           ; D2=D2+D1
       CALL    PUTIDT
       BR      EXIT

DIFFDT: ; Function 3. DAYS = DATE2-DATE1
       CMPW    @A3,#5          ;5 args?
       JNE     ARGERR          ; no
       CALL    GETID1          ;date1 to internal D2
       MOV     D2,D3           ;save date1
       CALL    GETID2          ;date2 to internal D2
       SUB     D3,D2           ;get diff
       MOV     #5,D0           ;xc.var #
       CALL    SETXC           ;get param
       CMPW    D6,#X.Flt       ;arg float?
       JNE     TYPERR          ; no
       FLTOF   D2,@A2          ;Put floating point arg
       BR      EXIT

COMPYW: ; Compute Date from YEAR,YWEEK,DOW
       CMPW    @A3,#4          ;4 vars sent?
       JNE     ARGERR          ; no
       MOV     #3,D5
       BTYPE   3,D0
       TST     D0              ;is DATE1 unformated?
       JNE     EXIT            ; no
       BSIZE   3,D0
       CMP     D0,#5           ;is DATE1 at least 5 bytes?
       JLT     EXIT            ; no
       BADRS   3,A2
       MOV     #257.,D7        ;mask D7 to 0101 MMDD
       MOVB    2(A2),D7        ;move in year
       CALL    $DSTOI          ;D7 = Julian for 01/01/19 cur year
       MOV     D7,D2           ; move it to D2
       CALL    DOW             ;D1 has DOW of 01/01/19 cur year
       SUB     D1,D2           ; sub DOW for 01/01/19 cur year
       CLR     D7              ; D7=0
       MOVB    4(A2),D7        ; D7=YWEEK
       DECW    D7              ; D7=YWEEK-1
       MUL     D7,#7           ; D7=(YWEEK-1)*7
       ADD     D7,D2           ; D2=START'I'JUL+(YWEEK-1)*7
       ADDB    3(A2),D2
       CALL    PUTIDT
       CLR     D5
       JMP     EXIT

COMPMW: ; Compute Date from YEAR,MONTH,MWEEK,DOW
       CMPW    @A3,#4
       JNE     ARGERR
       BTYPE   3,D0
       TST     D0
       JNE     EXIT
       BSIZE   3,D0
       CMP     D0,#6
       JLT     EXIT
       BADRS   3,A2
       MOV     #256.,D7        ;day 1 to D7
       MOVB    @A2,D7          ;month to D7
       SWAP    D7              ;shift them to high order bytes
       MOVB    2(A2),D7        ;year to D7 (D7 now in alpha sep format )
       CALL    $DSTOI          ;D7 has julian day 1 of month, year
       MOV     D7,D2           ; move it to D2
       CALL    DOW             ;D1 has DOW of 01/month/year
       SUB     D1,D2           ; sub DOW
       CLR     D7              ; D7=0
       MOVB    5(A2),D7        ; D7=MWEEK
       DECW    D7              ; D7=MWEEK-1
       MUL     D7,#7           ; D7=(MWEEK-1)*7
       ADD     D7,D2           ; D2=START'I'JUL+(MWEEK-1)*7
       ADDB    3(A2),D2
       CALL    PUTIDT
       JMP     EXIT

COMPEM: ;DATE2 = DATE1 changed to last day of the month
       CMPW    @A3,#4
       JNE     ARGERR
       CALL    GETID1          ;convert DATE1 to internal format (D2)
       MOV     D2,D7
       CALL    DITOS           ;convert to separate format and get last day
       ROL     D7,#8
       MOVB    D6,D7
       ROR     D7,#8
       CALL    $DSTOI
       MOV     D7,D2
       CALL    PUTIDT          ;convert seperated to DATE2
       JMP     EXIT

IODTIM:
;       XCALL DATES, 7, FLAGS, [STR'DATE or FILE CHAN], IDATE, ITIME
;       (interface) $ODTIM:  Output Date and Time (Time last)
;       Entry: D3=Date, D4=time (0=use sys)(separated), D5=format flags,A2->out
;       Return: A2->nxt char (if used)
;
       CMPW    @A3,#5          ;5 args?
       JNE     ARGERR          ; no

       MOV     #4,D0           ;xc var# ( IDATE )
       CALL    SETXC
       CALL    XVALUE
       MOV     D1,D3           ;IDATE to D3

       MOV     #5,D0           ;xc var# ( IDATE )
       CALL    SETXC
       CALL    XVALUE
       MOV     D1,D4           ;IDATE to D4

       CALL    FLGIN           ;get flags D5

       MOV     #3,D0
       CALL    SETXC
       CMPW    X.Typ(A3)[D0],#X.Str    ;is return var a string?
       JNE     TYPERR
       CMP     X.Siz(A3)[D0],#46
       JLO     SIZERR          ; no

BR      20$     ;TEMP SKIP OF FILESET

       BTST    #15.,D3         ;OUTPUT TO FILE?
       BEQ     20$             ;NO
       BTYPE   5,D0
       BSIZE   5,D5
;       CALL    GETNUM          ;GET FILE CHANNEL NUMBER
       MOV     D3,D5
       CALL    $FLSET          ;FIND FILE'S DDB
       BEQ     20$             ;EVERYTHING'S OK, PROCEED
       MOV     #6,D5           ;ERROR #6 BAD CHANNEL NUMBER
       JMP     EXIT            ;ABORT WITH ERROR

20$:    CALL    $ODTIM
       CLR     D5              ;$ODTIM has no error conditions
       JMP     EXIT            ;return to caller
;
;various calls
;
FLGIN:  MOV     #2,D0                   ;xcall var# ( FLAGS )
       CALL    SETXC
       CALL    XVALUE
       MOV     D1,D5
       RTN                     ;back to call

BIN4OU: CMPW    D6,#X.Bin       ;binary?
       JNE     TYPERR          ; no
       CMP     D7,#4           ;4 bytes long?
       JNE     SIZERR          ; no
       MOV     D1,@A2          ;mov it to @A2
       RTN

SETXC:  CLR     D6
       DEC     D0
       MUL     D0,#X.Off
       MOVW    X.Typ(A3)[D0],D6
       ANDW    #^H000F,D6      ;mask off all but last 4 bits
       MOV     X.Siz(A3)[D0],D7
       MOV     X.Adr(A3)[D0],A2
       RTN

ADJDAY:
       ROR     D1,#8
       ADDB    #2,D1           ;MON=2 SUNDAY = 8
       CMPB    D1,#8
       BLO     1$
       MOVB    #1,D1
1$:     ROL     D1,#8           ;SUN=1 SAT = 7
       RTN
;
;
IIDTIM:
; XCALL DATES, 8, FLAGS, ADATE, output IDATE, output ITIME
; (interface) $IDTIM:  Input Date and/or Time (if both, time must be first)
; Entry: A2->Input string, D5(bit 0)=1:no date,(bit 1)=1:no time
; Return: D3=date (separated), D4=time (separated), A2->nxt chr
; N set:error
;
       CMPW    @A3,#5          ;5 arguments?
       JNE     ARGERR          ; no
       CALL    FLGIN           ;FLAGS to D5
       BADRS   3,A2            ;index input
       CALL    $IDTIM          ;call subroutine
       BNE     1$              ;branch on error
       MOV     #4,D0           ;var #
       CALL    SETXC           ;get param
       MOV     D3,D1
       CALL    ADJDAY          ;0=MON 6=SUN to 1=MON 7=SAT
       CALL    BIN4OU          ;IDATE out
       MOV     #5,D0           ;var #
       CALL    SETXC           ;get param
       MOV     D4,D1
       CALL    BIN4OU          ;ITIME out
       CLR     D5              ;clr return flags
       JMP     EXIT            ;RETURN TO CALLER
1$:     MOV     #2,D5
       JMP     EXIT

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                               ;
;  gets DATE1 and DATE2 in internal format      ;
;_______________________________________________;
;                                               ;
GETID1: ;get first date in internal format      ;
       SAVE    A2,D0,D3,D4,D7                  ;
       MOV     #3,D0                           ;
       BR      GETIDT                          ;
GETID2: ;get second date in internal format     ;
       SAVE    A2,D0,D3,D4,D7                  ;
       MOV     #4,D0                           ;
GETIDT: ;convert a date to internal format      ;
       CALL    GETDAT                          ;
       REST    A2,D0,D3,D4,D7                  ;
       RTN                                     ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                               ;
;  put inernal format date into DATE2           ;
;_______________________________________________;
;                                               ;
PUTIDT:
       SAVE    A2,D5,D4
       MOV     #4,D0           ;xc.var# ( DATE2 )
       CALL    SETXC
PUTDAT: TJMP    D6
       OFFSET  PUTBX           ;seperated format date
       OFFSET  PUTASC          ;string mm/dd/yy or mm/dd/yyyy
       OFFSET  PUTFLT          ;julian
       OFFSET  PUTBIN          ;4 byte yyyymmdd format
       ;assume unform or binary
PUTBX:
       CMPW    D7,#3           ;at least 3 bytes?
       JLO     SIZERR          ; no
;Unformated (X) or binary, 1st 3 bytes are always: MONTH,DAY,YEAR
       MOV     D7,D0           ;save size of var
       MOV     D2,D7           ;
       CALL    DITOS
       CMPW    D0,#7           ;do we want days in month?
       BLO     1$              ; no
       PUSHB   D6              ;save days in month
1$:     MOV     D7,D3
       MOVB    D7,2(A2)        ; SET YEAR
       SWAP    D7
       MOVB    D7,@A2          ; SET MONTH
       RORW    D7,#8
       MOVB    D7,1(A2)        ; SET DAY
       CMPW    D0,#4           ;need more than MONTH,DAY,YEAR?
       JLO     PUTEXT          ; no
       MOV     D2,D7
       CALL    DOW             ;DOW to D1
       MOVB    D1,3(A2)        ;move in DOW
       CMPW    D0,#5           ;do we want the week of the year
       JLO     PUTEXT          ;no
;YWEEK=INT((NOW-START+START'DOW)/7)+1
       MOV     #257.,D7        ;day 1 mon 1 to D7
       SWAP    D7              ; shift to high order
       MOVB    D3,D7           ;year to low order
       CALL    $DSTOI          ; D7=START julian days for jan 1 present year
       MOV     D2,D5           ; D5=NOW   julian days for present date
       SUB     D7,D5           ;
       CALL    DOW             ;get DOW into D1
       ADDW    D1,D5           ;add in the day of the week
       DEC     D5              ;adjust
       DIV     D5,#7
       INCB    D5
       MOVB    D5,4(A2)        ;
       CMP     D0,#6
       BLO     PUTEXT
;MWEEK=INT((NOW-START+START'DOW)/7)+1
       MOV     D3,D7
       AND     #16777215.,D7
       OR      #16777216.,D7   ; SET DAY=01
       CALL    $DSTOI          ; D7=START cur month
       CALL    DOW             ; D1 has day of the week cur month
       ADDB    1(A2),D1        ; add in the day of the month
       SUB     #2,D1
       DIV     D1,#7
       INCB    D1
       MOVB    D1,5(A2)
       CMP     D0,#7
       BLO     PUTEXT
MDAYS:
       POPB    D6
       MOVB    D6,6(A2)

PUTEXT:
       REST    A2,D5,D4
       RTN
PUTASC:
;INTERNAL date in D2 to ASCII date in DATE2
       CMP     D7,#8           ;is string at least 8 bytes
       JLO     SIZERR          ; no
       MOV     D2,D7           ;internal to reg
       CALL    DITOS           ;D7 has date seperated
       MOV     D7,D3           ;IDATE for $ODTIM
       MOV     #840.,D5        ;flags for $ODTIM
       CALL    $ODTIM          ;
       JMP     PUTEXT

PUTFLT:
;INTERNAL to FLOAT is just Long Word to Float
       FLTOF   D2,@A2
       JMP     PUTEXT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PUTBIN: ;special format bin4 yyyymmdd
       CMPW    D7,#4           ;4 bytes?
       JNE     SIZERR          ; no
       MOV     #4,D0           ;set counter
       MOV     D2,D7           ;internal to reg
       CALL    DITOS           ;D7 has date seperated
       CLR     D0              ;clear work reg
       CLR     D1              ;clear accumulator
       MOVB    D7,D0           ;year to D0
       ADDW    #1900,D0        ;assume 20th century
       MUL     D0,#10000       ;adjust 4 left
       MOV     D0,D1           ;move to accumulator
       SWAP    D7              ;month to first byte
       CLR     D0              ;clear work reg
       MOVB    D7,D0           ;month to D0
       MUL     D0,#100         ;adjust 2 left
       ADDW    D0,D1           ;month to accumlator
       RORW    D7,#8           ;day to first byte
       CLR     D0
       MOVB    D7,D0
       ADD     D0,D1           ;add day
       MOV     #4,D0           ;set counter
1$:     MOVB    D1,(A2)+        ;move a byte
       ROR     D1,#8           ;shift right a byte
       SOB     D0,1$           ;do it again
       BR      PUTEXT          ;return to caller


GETFUN:
; Get function number D0
       CLR     D0
       CALL    XVALUE
       MOV     D1,D0
       RTN

XVALUE: XCVALU  D0
       JNE     TYPERR
       RTN
;
DITOS:
; CONVERT INTERNAL DATE (D7) TO SEPERATED DATE (D7),
; SET D6 TO NUMBER OF DAYS IN MONTH
; D2=MONTH
; D3=DAY
; D4=YEAR
;
       SAVE    D2,D3,D4,A2,A3
       SUB     #CNTURY,D7
       DIV     D7,#1461.
       CLR     D3
       MOVW    D7,D3
       ADDW    D3,D3
       ADDW    D3,D3
       CLRW    D7
       SWAP    D7
       DIV     D7,#365.
       ADDW    D7,D3
       CLR     D4
       MOVW    D3,D4
       TSTW    D7              ;Leap year?
       BEQ     1$              ; Yes
       LEA     A2,NORMYR       ;Index normal year and day tables
       LEA     A3,NORMDS       ;^^^^^
       SWAP    D7
       BR      2$
1$:     LEA     A2,LEAPYR
       LEA     A3,LEAPDS
       SWAP    D7
       INCW    D7
2$:     MOVW    #-1,D3
3$:     INCW    D3
       CMPW    D7,(A2)+
       BCC     3$
       CLR     D2
       MOVW    D3,D2
       SUB     #4,A2
       SUBW    @A2,D7
       CLR     D3
       MOVW    D7,D3
       INC     D3
                               ;    DY MN DW YR
       CLR     D7              ; D7=00 00 00 00
       MOVB    D3,D7           ; D7=00 00 00 D3
       RORW    D7,#8           ; D7=00 00 D3 00
       MOVB    D2,D7           ; D7=00 00 D3 D2
       SWAP    D7              ; D7=D3 D2 00 00
       MOVB    D4,D7           ; D7=D3 D2 00 D4
       MOVB    -1(A3)[D2],D6   ; Days in month to D6
5$:     REST    D2,D3,D4,A2,A3
       RTN

NORMYR: WORD    0,31,59,90,120,151,181,212,243,273,304,334,365
LEAPYR: WORD    0,31,60,91,121,152,182,213,244,274,305,335,366
NORMDS: BYTE    31,28,31,30,31,30,31,31,30,31,30,31
LEAPDS: BYTE    31,29,31,30,31,30,31,31,30,31,30,31
;
GETDAT:
       CALL    SETXC
       TJMP    D6
       OFFSET  GTDBX           ;seperated format date
       OFFSET  GTDASC          ;string mm/dd/yy or mm/dd/yyyy
       OFFSET  GTDFLT          ;julian
       OFFSET  GTDBIN          ;4 byte yyyymmdd format
GTDBX:
; Unformatted 1st 3 bytes are always MONTH,DAY,YEAR
; Basic stores em backwards
       CLR     D7              ;clear storage
       MOVB    1(A2),D7        ;day to D7
       RORW    D7,#8           ;shift right 8 bytes
       MOVB    @A2,D7          ;month to D7
       SWAP    D7              ;mov to high order bytes of Lwrd
       MOVB    2(A2),D7        ;year to D7
       TST     D7              ;was date 0?
       BNE     GTDEXT          ; no
GDATEI: GDATEI  D2              ;today in internal format
       RTN                     ; use todays date
       ;D7 now in ALPHA seperated format but DOW is invalid.
       ;Don't need it.
GTDEXT: CALL    $DSTOI          ;call for ALPHA internal format. ( Julian )
       MOV     D7,D2           ;D2 has date in ALPHA internal format
       RTN

GTDASC:
;ASCII to ALPHA INTERNAL
       CMP     D7,#8           ;string at least 8 bytes?
       JLO     SIZERR          ; no
       MOVB    8(A2),D1        ;save byte after string
       CLRB    8(A2)           ;make sure null byte for termination
       MOV     #2,D5           ;don't scan for time
       PUSH    A2
       CALL    $IDTIM
       SETNE   D2
       POP     A2
       MOVB    D1,8(A2)
       MOVW    #1,D5
       TSTB    D2
       JNE     EXIT            ;error
       CLRW    D5              ;clear error
       MOV     D3,D7
       BR      GTDEXT
       ;end of ASCII to ALPHA INTERNAL

GTDFLT: FFTOL   @A2,D2
       TST     D2              ;date 0?
       BEQ     GDATEI          ; yes - use today
       RTN

GTDBIN: ;special format bin4 yyyymmdd
       ;we know type is binary
       CMP     X.Siz(A3)[D0],#4
       JNE     SIZERR          ; no
       CALL    XVALUE
       PUSH
       CLR     @SP
       PUSH
       CLR     @SP
       PUSH                    ;get storage
       CLR     @SP
       MOV     SP,A2           ;index storage with A2
       DCVT    0,OT$MEM        ;ASCII rep of bin4 date to ASCIIB
       SUB     #2,A2           ;index day
       GTDEC                   ;day to D1
       CLR     D0
       MOVB    D1,D0           ;day to D7
       ROLW    D0,#8           ;shift day left
       SUB     #4,A2           ;index month
       CLRB    2(A2)
       GTDEC                   ;month to D1
       MOVB    D1,D0           ;month to D7
       SWAP    D0              ;shift to high word
       SUB     #6,A2           ;index year
       CLRB    4(A2)
       GTDEC                   ;year to D1 yyyy
       SUBW    #1900,D1        ;assume this century
       MOVB    D1,D0           ;date is now in alpha seperated format
       MOV     D0,D7
       POP
       POP
       POP                     ;restore stack pointer
       JMP     GTDEXT          ;
;
DOW:    ;Convert true julian in D7 to day of the week ( 1=sun  7=sat )
       ;assuming 20th century and return it in D1
       MOV     D7,D1
       SUB     #CNTURY,D1
       ADD     #2,D1           ;adjust so DOW will divide
       DIV     D1,#7           ;DOW is in high order of D1
       CLRW    D1              ;clear all but DOW
       SWAP    D1              ;DOW is low order of D1
                               ;DOW ( 0 - 6 ) ( sat - sun )
       TSTB    D1              ;is sat
       BNE     1$              ; no
       MOVB    #7,D1           ; DOW is 1 - 7 ( sun - sat )
1$:     RTN

;
; Abort routine for improper arguments passed to SBR
;
       DEFINE  ERROR   TEXT
               TYPE    <? 'TEXT'>
               JMP     ABORT
               ENDM

ADRERR: ERROR   Odd address given as WORD or LONG
ARGERR: ERROR   improper number of arguments
TYPERR: ERROR   argument type error
SIZERR: ERROR   argument size error
FUNERR: ERROR   function out of range (1..8)
CHNERR: ERROR   error locating file channel

ABORT:  TYPECR  < in DATES.SBR>
       MOV     JOBCUR,A6
       ORW     #J.CCC,JOBSTS(A6)
       EXIT

       END