        TITLE   'str - string functions for rxdos'
        PAGE 59, 132
        .LALL

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  String Functions for RxDOS                                   ;
        ;...............................................................;

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Real Time Dos                                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  This material  was created as a published version  of a DOS  ;
        ;  equivalent product.   This program  logically  functions in  ;
        ;  the same way as  MSDOS functions and it  is  internal  data  ;
        ;  structure compliant with MSDOS 6.0                           ;
        ;                                                               ;
        ;  This product is distributed  AS IS and contains no warranty  ;
        ;  whatsoever,   including  warranty  of   merchantability  or  ;
        ;  fitness for a particular purpose.                            ;
        ;                                                               ;
        ;                                                               ;
        ;  (c) Copyright 1990, 1997. Api Software and Mike Podanoffsky  ;
        ;      All Rights Reserved Worldwide.                           ;
        ;                                                               ;
        ;  This product is protected under copyright laws and  may not  ;
        ;  be reproduced  in whole  or in part, in any form  or media,  ;
        ;  included but not limited to source listing, facsimile, data  ;
        ;  transmission, cd-rom, or  floppy disk without the expressed  ;
        ;  written consent of the author.                               ;
        ;                                                               ;
        ;  License  for  distribution  for commercial  use  or  resale  ;
        ;  required from:                                               ;
        ;                                                               ;
        ;  Api Software                                                 ;
        ;  12 South Walker Street                                       ;
        ;  Lowell,  MA   01851                                          ;
        ;                                                               ;
        ;  internet: mikep@world.std.com                                ;
        ;                                                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;  Compile with MASM 5.1                                        ;
        ;...............................................................;

        include rxdosmac.asm
        include rxdosdef.asm

RxDOS   SEGMENT PUBLIC 'CODE'
        assume cs:RxDOS, ds:RxDOS, es:RxDOS, ss:RxDOS

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  String Managers                                              ;
        ;...............................................................;

        public upperCase
        public upperCaseName
        public lowerCase
        public skipToNextName
        public skipToLast
        public skipSpaces
        public ifPathSeparator
        public scanInvalidFilenameChars
        public CompareString
        public CopyString
        public CopyBlock
        public StringLength                     ; StrLen
        public condStringLength
        public convFilenametoFCBString
        public convFCBNametoASCIZ
        public getSysDateinDirFormat
        public __ascii_stosb
        public getMonthDayYear
        public getDaysSince1980
        public getSystemDateValue

        extrn getActualDrive                    : near
        extrn sizeInvFnChars                    : abs
        extrn _invalidFnCharacters              : byte
        extrn getSysDate                        : near
        extrn getExpandedDateTime               : near
        extrn DaysInMonthTable                  : byte
        extrn AccumDaysPerMonthTable            : word

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  String Functions                                             ;
        ;...............................................................;

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Upper Case                                                   ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  al    character                                              ;
        ;...............................................................;

upperCase:
        cmp al, 'a'                                     ;** add language support
        jc upperCase_08
        cmp al, 'z'+1
        jnc upperCase_08

        and al, NOT 20h

upperCase_08:
        or al, al
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Lower Case                                                   ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  al    character                                              ;
        ;...............................................................;

lowerCase:
        cmp al, 'A'                                     ;** add language support
        jc lowerCase_08
        cmp al, 'Z'+1
        jnc lowerCase_08

        or al, 20h

lowerCase_08:
        or al, al
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Upper case FCB format filename                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;  es:si pointer to buffer                                      ;
        ;...............................................................;

UpperCaseName:
        push si
        mov cx, sizeFILENAME

UpperCaseName_08:
        mov al, byte ptr es:[ si ]
        call upperCase
        mov byte ptr es:[ si ], al
        inc si
        loop UpperCaseName_08

        pop si
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Skip To Start of Name                                        ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  This is a simple routine that skips any \ and spaces to a    ;
        ;  name.  It will not parse drives or anything else.            ;
        ;                                                               ;
        ;  es:si pointer to buffer                                      ;
        ;...............................................................;

skipToNextName:

        cmp byte ptr [ si ], ' '
        jz skipToNextName_04                            ; if space -->
        cmp byte ptr [ si ], '/'
        jz skipToNextName_04                            ; if slash -->
        cmp byte ptr [ si ], '\'
        jnz skipToNextName_08                           ; if anything but name -->

skipToNextName_04:
        inc si
        jmp skipToNextName

skipToNextName_08:
        mov al, byte ptr [ si ]
        or al, al
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Skip To Start of Last Name in path string                    ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  This routine skips any components of a name until it reaches ;
        ;  the last name.                                               ;
        ;                                                               ;
        ;  es:si pointer to buffer                                      ;
        ;...............................................................;

skipToLast:
        push bx
        mov bx, si

skipToLast_04:
        mov al, byte ptr es:[ si ]
        or al, al
        jz skipToLast_12

        inc si
        cmp al, '/'
        jz skipToLast_08                                ; if slash -->
        cmp al, '\'
        jnz skipToLast_04                               ; if anything but slash -->

skipToLast_08:
        mov bx, si                                      ; remember last /
        jmp skipToLast_04

skipToLast_12:
        mov si, bx
        mov al, byte ptr es:[ si ]
        or al, al
        pop bx
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Skip Spaces                                                  ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  es:si pointer to buffer                                      ;
        ;...............................................................;

skipSpaces:

        cmp byte ptr es:[ si ], ' '
        jnz skipSpaces_04                               ; non-space -->

        inc si
        jmp skipSpaces

skipSpaces_04:
        mov al, byte ptr es:[ si ]
        or al, al
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Check For Path Separator                                     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  al    character                                              ;
        ;...............................................................;

ifPathSeparator:

        cmp al, '/'
        jnz ifPathSeparator_08
        mov al, '\'                                     ; convert / to \

ifPathSeparator_08:
        cmp al, '\'
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Scan Invalid Filename Chars                                  ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  es:si pointer to buffer                                      ;
        ;...............................................................;

scanInvalidFilenameChars:

        saveSegments di, si
        setDS es
        currSegment es

scanInvalidFnChars_08:
        lodsb                                           ; get character
        or al, al                                       ; end of file name ?
        jz scanInvalidFnChars_12                        ; yes -->

        cmp al, ' '                                     ; make sure its not a control character
        jc scanInvalidFnChars_10                        ; if invalid character ->

        mov di, offset _invalidFnCharacters
        mov cx, sizeInvFnChars
        repnz scasb                                     ; any in invalid character list ?
        jnz scanInvalidFnChars_08                       ; if valid character ->

scanInvalidFnChars_10:
        stc

scanInvalidFnChars_12:
        restoreSegments si, di
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Compare String                                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Emulates rep cmpsb instruction except that it matches '?'    ;
        ;  characters.  No character conversions.                       ;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ds:si  source string (may contain ? character )             ;
        ;   es:di  match string (may not contain wild character )       ;
        ;   cx     count                                                ;
        ;...............................................................;

CompareString:

        rep cmpsb                                       ; match ?
        jz compareString_16                             ; yes, continue if not zero -->

        mov al, byte ptr [si - 1]
        cmp al, questionMark                            ; previous a wild character ?
        jnz compareString_16                            ; if not ? wild character -->
        or cx, cx
        jnz compareString                               ; else continue matching -->
        ret

compareString_16:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Copy String                                                  ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   stack  source string address                                ;
        ;   stack  dest string address                                  ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   es:di  points to null terminator of dest string.            ;
        ;...............................................................;

CopyString:

        Entry 4
        darg _srcstring
        darg _dststring

        saveRegisters ds, si, ax

        getdarg ds, si, _srcstring
        getdarg es, di, _dststring

CopyString_08:
        lodsb
        stosb
        or al, al
        jnz CopyString_08

        dec di
        restoreRegisters ax, si, ds
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Copy Block                                                   ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   stack  source string address                                ;
        ;   stack  dest string address                                  ;
        ;   cx     length                                               ;
        ;...............................................................;

CopyBlock:

        Entry 4
        darg _src
        darg _dest

        saveSegments di, si

        getdarg ds, si, _src
        getdarg es, di, _dest
        rep movsb

        restoreSegments si, di
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Compute String Length                                        ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   stack  string address                                       ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   cx     string length                                        ;
        ;...............................................................;

StringLength:

        Entry 2
        darg _string

        push es
        push di
        push ax
        xor al, al
        mov cx, -1
        getdarg es, di, _string
        repnz scasb

        not cx
        dec cx
        or cx, cx

        pop ax
        pop di
        pop es
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Conditional String Length                                    ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   stack  string address                                       ;
        ;   cx     max string length to scan                            ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   cx     actual string length                                 ;
        ;   al     character just before null                           ;
        ;   zr/nz  if string terminates with null                       ;
        ;...............................................................;

condStringLength:

        Entry 2
        darg _string

        push es
        push di
        push bx

        xor al, al
        mov bx, cx                                      ; orig length to bx
        getdarg es, di, _string                         ; where to search
        repnz scasb                                     ; scan for null

        pushf                                           ; zr means we have located a null
        sub cx, bx
        neg cx                                          ; -length
        dec cx                                          ; minus the null
        mov al, byte ptr es:[ di - 2 ]                  ; character just before null

        popf                                            ; return status
        pop bx
        pop di
        pop es
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Fix Pattern Match                                            ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  If * in pattern matching filenames is the last character in  ;
        ;  either the filename or extension, we can convert the pattern ;
        ;  to a simple question filled pattern.  If the pattern is not  ;
        ;  converted, the NZ flag is returned.  ZR is returned if the   ;
        ;  pattern has been converted or if there was no conversion     ;
        ;  necessary.                                                   ;
        ;                                                               ;
        ;  This function will also convert a filename from (text.ext)   ;
        ;  to space padded (8 + 3 ) format.                             ;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ds:si  source string (null terminated)                      ;
        ;   es:di  output string                                        ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   ds:si  points to terminator found in source string          ;
        ;   es:di  points to output string                              ;
        ;   zr     filename is all blank                                ;
        ;...............................................................;

convFilenametoFCBString:

        push di                                         ; save original pointers
        mov al, ' '
        mov cx, sizeFILENAME                            ; max filename field length
        rep stosb                                       ; blank fill

        pop di
        push di                                         ; save original pointers
        call skipToNextName                             ; skip to starting name

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  see if special cases . and ..
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        cmp word ptr [ si ], '.'                        ; special case of just . filename ?
        jz convFilenametoFCBString_08                   ; yes -->
        cmp word ptr [ si ], '..'                       ; special case of just .. filename ?
        jnz convFilenametoFCBString_12                  ; no -->
        cmp byte ptr [ si + 2 ], 0                      ; must be ..[0]
        jnz convFilenametoFCBString_12                  ; no -->

convFilenametoFCBString_08:
        lodsb                                           ; get next
        or al, al                                       ; end of string
        jz convFilenametoFCBString_14                   ; yes -->

        stosb                                           ; save period
        jmp convFilenametoFCBString_08                  ; continue copy -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  presumably normal filename
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

convFilenametoFCBString_12:
        mov cx, sizeFILENAME                            ; max filename field length
        call __convfilename                             ; get name
        jnz convFilenametoFCBString_14                  ; if not followed by a period -->

        add di, sizefnName
        mov cx, sizefnExtension                         ; max extension field length
        call __convfilename                             ; get extension

convFilenametoFCBString_14:
        pop di
        cmp byte ptr es:[ di ], ' '                     ; if entire name is blank
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Grab/ fill filename characters                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ds:si  source string (null terminated)                      ;
        ;   es:di  output string                                        ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   zr means dot following name term encountered                ;
        ;...............................................................;

__convfilename:

        push di

__convfilename_04:
        lodsb                                           ; get character
        or al, al  
        jz __convfilename_26                            ; null is end of name -->
        cmp al, ' '
        jz __convfilename_04                            ; null is end of name -->
        cmp al, '/'
        jz __convfilename_28                            ; '\' is end of name -->
        cmp al, '\'
        jz __convfilename_28                            ; '\' is end of name -->
        cmp al, '.'
        jz __convfilename_28                            ; period is end of name -->
        cmp al, ','
        jz __convfilename_28                            ; comma is end of name -->
        cmp al, ';'
        jz __convfilename_28                            ; semicolon is end of name -->
        cmp al, ':'
        jz __convfilename_28                            ; colon is end of name -->
        cmp al, '*'
        jz __convfilename_12                            ; '*' requires expansion -->

        call upperCase
        stosb                                           ; else just copy
        loop __convfilename_04
        jmp short __convfilename_20

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  asterisk fill
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

__convfilename_12:
        mov al, questionMark
        rep stosb

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  keep scanning until end of field
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

__convfilename_20:
        lodsb                                           ; get character
        or al, al  
        jz __convfilename_26                            ; null is end of name -->
        cmp al, ' '
        jz __convfilename_20                            ; null is end of name -->
        cmp al, '\'
        jz __convfilename_28                            ; '\' is end of name -->
        cmp al, '/'
        jz __convfilename_28                            ; '/' is end of name -->
        cmp al, '.'
        jz __convfilename_28                            ; period is end of name -->
        cmp al, ','
        jz __convfilename_28                            ; comma is end of name -->
        cmp al, ';'
        jz __convfilename_28                            ; semicolon is end of name -->
        jmp __convfilename_20

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

__convfilename_26:
        dec si
        xor ax, ax                                      ; invalid term ends with null

__convfilename_28:
        cmp al, '.'                                     ; last character parsed a period ?
        pop di
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Convert FCB Name to ASCIZ                                    ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   arg    FCB                                                  ;
        ;   arg    expanded name buffer                                 ;
        ;                                                               ;
        ;...............................................................;

convFCBNametoASCIZ:

        Entry 4
        darg _source
        darg _destination

        SaveSegments di, si, dx

        getdarg ds, si, _source
        push word ptr [ si ]                            ; drive code from fcb
        call getActualDrive                             ; convert to actual
        mov al, dl                                      ; in case of error, restore value
        
        mov ah, ':'
        add al, 'a'

        getdarg es, di, _destination
        stosw

        mov si, word ptr [ _source. _pointer ][ bp ]
        lea si, offset fcbName [ si ]
        mov cx, sizefnName

convFCBName_08:
        lodsb                                           ; get character
        cmp al, ' '+1
        jc convFCBName_12
        stosb                                           ; copy filename
        loop convFCBName_08

convFCBName_12:
        mov cx, sizefnExtension
        mov si, word ptr [ _source. _pointer ][ bp ]
        lea si, offset fcbExtension [ si ]
        cmp byte ptr [ si ], ' '+1
        jc convFCBName_20

        mov al, '.'                                     ; place a period after
        stosb

convFCBName_14:
        lodsb
        cmp al, ' '+1
        jc convFCBName_20
        stosb                                           ; copy extension
        loop convFCBName_14

convFCBName_20:
        mov byte ptr es:[ di ], 0                       ; NULL terminator
        restoreSegments dx, si, di
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Convert al byte to es:[di]                                   ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   al     byte                                                 ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   [di]   two byte ascii contents of [al]                      ;
        ;...............................................................;

__ascii_stosb:
        push ax
        mov ah, al
        shr al, 1
        shr al, 1
        shr al, 1
        shr al, 1
        and ax, 0f0fh                                   ; mask off extra bits
        add ax, 'AA'                                    ; convert to ascii
        stosw                                           ; save
        pop ax                                          ; restore ax
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Get Month, Day and Year from System Date                     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ax     system day                                           ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   al     day of week (Sunday = 0, ... )                       ;
        ;   cx     year                                                 ;
        ;   dh     month                                                ;
        ;   dl     day                                                  ;
        ;...............................................................;

getMonthDayYear:

        push bx

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  compute year
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        xor dx, dx
        mov cx, 365
        div cx                                          ; expected years

        mov cx, ax                                      ; year to cx
        test cx, 11b                                    ; leap year ?
        jz getMonthDayYear_08                           ; yes -->
        dec dx                                          ; adjust days left in year
        call _daysAdjust                                ; adjust for days in year

getMonthDayYear_08:
        shr cx, 1
        shr cx, 1                                       ; divide by 4
        sub dx, cx                                      ; day this year
        jnc getMonthDayYear_10                          ; this year -->
        call _daysAdjust                                ; adjust for days in year

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  compute month
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
getMonthDayYear_10:
        xor ch, ch
        xor bx, bx
        xchg ax, dx                                     ; day this year

getMonthDayYear_12:
        mov cl, byte ptr ss:[ DaysInMonthTable ][ bx ]
        test dx, 11b                                    ; leap year ?
        jnz getMonthDayYear_14                          ; no -->
        cmp bl, 1                                       ; is this february ?
        jnz getMonthDayYear_14                          ; no -->
        inc cl

getMonthDayYear_14:
        cmp ax, cx                                      ; this month ?
        jc getMonthDayYear_20                           ; yes -->

        sub ax, cx                                      ; account for this month
        inc bx
        jmp getMonthDayYear_12

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  dx year, bx month, ax day
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
getMonthDayYear_20:
        mov cx, dx
        add cx, 1980                                    ; year

        mov dh, bl                                      ; month
        mov dl, al                                      ; day
        add dx, 0101h                                   ; within proper range

        call getDayOfWeek
        pop bx
        ret

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  adjust for undeflow in year/ day
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_daysAdjust:

        or dx, dx                                       ; days left negative ?
        jge _daysAdjust_08                              ; no -->

        dec ax                                          ; convert to days
        add dx, 365                                     ; if wrap around error, 
        test ax, 11b                                    ; leap year ?
        jnz _daysAdjust_08                              ; no -->
        inc dx                                          ; if leap year

_daysAdjust_08:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Get Day of Week                                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   cx     year   ( 1980 ... )                                  ;
        ;   dh     month  ( 1 - 12)                                     ;
        ;   dl     day    ( 1 - 31)                                     ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   ax     day of week (Sunday = 0, ... )                       ;
        ;...............................................................;

getDayOfWeek:

        Entry
        def dayofweek

        push bx
        push cx
        push dx
        mov bx, dx
        sub bh, 2                                       ; past february ?
        jg getDayOfWeek_08                              ; yes -->

        add bh, 12                                      ; rotate full year
        dec cx                                          ; adjust year

getDayOfWeek_08:
        mov ax, 13                                      ;
        mul bh                                          ; months * 13
        dec ax                                          ; 13 * months - 1 
        mov dh, 5
        div dh                                          ; (13 * months - 1)/5
        xor ah, ah                                      ; ignore remainder

        add al, bl                                      ; add days
        storarg dayofweek, ax                           ; save interim value

        xor dx, dx
        mov ax, cx                                      ; copy years
        mov cx, 100                                     ; century
        div cx                                          ; centuries in ax/ years in dx

        add word ptr [ dayofweek ][ bp ], dx            ; add year

        shr dx, 1        
        shr dx, 1        
        add word ptr [ dayofweek ][ bp ], dx            ; add year/4

        mov dx, ax
        shr dx, 1        
        shr dx, 1        
        add word ptr [ dayofweek ][ bp ], dx            ; add century/4

        add ax, ax                                      ; 2 * century
        sub word ptr [ dayofweek ][ bp ], ax            ; sub 2*century

        xor dx, dx
        getarg ax, dayofweek
        mov cx, 7
        div cx                                          ; divide by 7

        mov dx, ax
        add ax, ax                                      ; x2
        add dx, ax                                      ; x3
        add ax, ax                                      ; x4
        add dx, ax                                      ; x7
        
        getarg ax, dayofweek
        sub ax, dx                                      ; real day of week
        jge getDayOfWeek_12                             ; if ok -->
        add ax, 7                                       ; else warp around day of week

getDayOfWeek_12:
        pop dx
        pop cx
        pop bx
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Get Days Since 1980                                          ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   cx     year   ( 1980 ... )                                  ;
        ;   dh     month  ( 1 - 12)                                     ;
        ;   dl     day    ( 1 - 31)                                     ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   ax     days since 1980 format                               ;
        ;                                                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;  Based On:                                                    ;
        ;                                                               ;
        ;   jdn = (long)(d - 32076)                                     ;
        ;       + 1461L * (y + 4800L + (m - 14) / 12) / 4               ;
        ;       + 367 * (m - 2 - (m - 14) / 12 * 12) / 12               ;
        ;       - 3 * ((y + 4900L + (m - 14) / 12) / 100) / 4           ;
        ;       + 1;            /* correction by rdg */                 ;
        ;                                                               ;
        ;...............................................................;

getDaysSince1980:

        Entry
        def _year, cx
        def _month, 0000
        def _day, 0000
        def _m14_12, 0000
        ddef _daysSince

        push bx
        push cx
        push dx

        mov byte ptr [ _month ][ bp ], dh
        mov byte ptr [ _day   ][ bp ], dl

        dec dh
        jnz getDaysSince1980_06
        mov word ptr [ _m14_12 ][ bp ], -1

getDaysSince1980_06:
        xor dx, dx
        mov ax, word ptr [ _m14_12 ][ bp ]
        mov cx, 12
        mul cx                                  ; *12
        inc ax
        inc ax                                  ; 2
        sub ax, word ptr [ _month ][ bp ]
        neg ax                                  ; - m_14
        mov cx, 367
        mul cx
        
        xor dx, dx
        mov cx, 12
        div cx
        mov word ptr [ _daysSince. _Low ][ bp ], ax
        mov word ptr [ _daysSince. _High ][ bp ], 0000

        mov ax, word ptr [ _m14_12 ][ bp ]
        add ax, 4900
        add ax, word ptr [ _year ][ bp ]

        xor dx, dx
        mov cx, 100
        div cx

        mov cx, 3
        mul cx

        shr ax, 1
        shr ax, 1                               ; / 4
        dec ax
        sub word ptr [ _daysSince. _Low  ][ bp ], ax
        sbb word ptr [ _daysSince. _High ][ bp ], 0000

        mov ax, word ptr [ _m14_12 ][ bp ]
        add ax, 4800
        add ax, word ptr [ _year ][ bp ]

        mov cx, 1461
        mul cx

    ; do a long shift here
        shr dx, 1
        rcr ax, 1
        shr dx, 1                               ; / 4
        rcr ax, 1
        add word ptr [ _daysSince. _Low  ][ bp ], ax
        adc word ptr [ _daysSince. _High ][ bp ], dx  

        xor dx, dx
        mov ax, word ptr [ _day ][ bp ]
        sub ax, 32076
        sbb dx, 0000
        add ax, word ptr [ _daysSince. _Low  ][ bp ]
        adc dx, word ptr [ _daysSince. _High ][ bp ]

        sub ax, 4BD0h
        sbb dx, 25h

getDaysSince1980_12:
        pop dx
        pop cx
        pop bx
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Get System Date, Day of Week from Year, Month, Day           ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   cx     year                                                 ;
        ;   dh     month                                                ;
        ;   dl     day                                                  ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   dx     system day                                           ;
        ;   al     day of week (Sunday = 0, ... )                       ;
        ;...............................................................;

getSystemDateValue:

        Entry
        def  _year, cx                                  ; not adjusted
        def  _month                                     ; adjusted
        def  _day                                       ; adjusted
        def  _Totaldays

        push bx

        xor ax, ax
        mov al, dh
        storarg _month, ax

        mov al, dl
        storarg _day, ax

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov ax, 365
        sub cx, 1980
        mul cx                                          ; * 365
        storarg _Totaldays, ax

        or cx, cx                                       ; 1980 ?
        jz getSystemDateValue_06                        ; yes -->

        dec cx                                          ; adjust for current year
        shr cx, 1
        shr cx, 1                                       ; years / 4
        inc cx                                          ; adjust for 1980 (leap year)
        add word ptr [ _Totaldays ][ bp ], cx

     ;  or cx, cx                                       ; 1980 ?
     ;  jz getSystemDateValue_06                        ; yes -->
     ;  inc word ptr [ _Totaldays ][ bp ]               ; adjust days for 1980

getSystemDateValue_06:
     ;  xor dx, dx
     ;  mov ax, cx
     ;  mov bx, 100
     ;  div bx
     ;  sub word ptr [ _Totaldays ][ bp ], ax
        
     ;  shr ax, 1
     ;  shr ax, 1                                       ; years / 400
     ;  add word ptr [ _Totaldays ][ bp ], ax

        mov bx, word ptr [ _month ][ bp ]
        dec bx
        add bx, bx
        mov ax, word ptr cs:[ AccumDaysPerMonthTable ][ bx ]

        cmp word ptr [ _month ][ bp ], 2
        jle getSystemDateValue_08

        test word ptr [ _year ][ bp ], 11b
        jnz getSystemDateValue_08
        inc ax                                          ; adjust for leap year        

getSystemDateValue_08:
        add ax, word ptr [ _day ][ bp ]
        add ax, word ptr [ _Totaldays ][ bp ]
        push ax                                         ; save days

        xor dx, dx
        inc ax
        mov cx, 7
        div cx
        pop ax
        xchg ax, dx

        pop bx
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Get System Date and Time In Dir Format                       ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   ax     time of day                                          ;
        ;   dx     date                                                 ;
        ;...............................................................;

getSysDateinDirFormat:

        call getExpandedDateTime                        ; get system date

    ; date

        push cx
        push dx
        call getMonthDayYear                            ; conv sys day to [cx] = year, [dx] = mo/ day.
        call convSysDatetoDirDate                       ; convert [cx/dx] to [ax] date in dir format

    ; time

        pop dx
        pop cx                                          ; recall time
        push ax                                         ; save dir date
        call convSysTimetoDirTime                       ; convert time to dir format

        pop dx                                          ; date to dx
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Convert cx/dx date to Dir Date.                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   cx     year ( 1980 + )                                      ;
        ;   dh     month                                                ;
        ;   dl     day                                                  ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   ax     date in dir format                                   ;
        ;...............................................................;

convSysDatetoDirDate:

        sub cx, 1980
        mov ax, cx
        shl ax, 1
        shl ax, 1
        shl ax, 1
        shl ax, 1                                       ; shift over to get month

        or al, dh                                       ; month
        shl ax, 1
        shl ax, 1
        shl ax, 1
        shl ax, 1
        shl ax, 1

        or al, dl                                       ; day
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Convert cx/dx date to Dir Date.                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ch     hours                                                ;
        ;   cl     minutes                                              ;
        ;   dh     seconds                                              ;
        ;   dl     hundredths of seconds                                ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   ax     time in dir format                                   ;
        ;...............................................................;

convSysTimetoDirTime:

        mov al, ch                                      ; hours
        shl ax, 1
        shl ax, 1
        shl ax, 1
        shl ax, 1
        shl ax, 1
        shl ax, 1

        or al, cl                                       ; minutes
        shl ax, 1
        shl ax, 1
        shl ax, 1
        shl ax, 1
        shl ax, 1

        shr dh, 1
        or al, cl                                       ; seconds
        ret

RxDOS   ENDS
        END

