;       SCCSID = @(#)cpcase.asm 6.1 90/11/17
;      SCCSID = @(#)cpcase.asm    13.3 90/01/25
;***    CPCASE - perform Code Page correct UpCasing & string compares
;
;       NameUpCase    - UpCase source to dest
;       NameUpCaseCmp - Compare 2 names, upcasing 1 name

.286

.MODEL MEDIUM, PASCAL

ifdef MEDIUM_MODEL
CALLTYPE    equ <far>
else
CALLTYPE    equ <near>
endif


include codepage.inc

.data

extrn pbCaseCmpBuf:dword


IRP SEG,<_TEXT,_BTXT>

.CODE SEG


;**     NameUpCase - Up-case name (code page correct)
;       =============
;
;       DirEnt name must be upcased using the proper code page translation
;       table.  DBCS characters are correctly skipped.
;
;       This is done when returning old names, when ATTR_NEWNAME is FALSE.
;
;       This is done by using XLAT with the CaseTable for the associated code
;       page (indexed by 1 byte in DirEnt).
;
;       If DirEnt name has any DBCS characters, DBCS characters (2 bytes)
;       must be skipped instead of doing the XLAT.
;
;       ENTRY   pbDirEnt    - pointer to DirEnt name (mixed-case)
;               cbDirEnt    - length of DirEnt name
;               pCPInfo     - pointer to CodePageInfo for DirEnt name
;               pbDest      - pointer to destination buffer
;
;       EXIT    (none)

public NameUpCase&SEG
NameUpCase&SEG  proc CALLTYPE   uses ds si di,     \
        pbDirEnt:dword,                \
        cbDirEnt:word,                 \
        pCPInfo:dword,                 \
        pbDest:dword

;   Set up for upcase

        mov     cx,cbDirEnt         ; Subject len >= DirEnt len

        or      cx,cx               ; zero length?
        jz      nucas99             ; yes, bail out

;   Move DirEnt name to special buffer (in segment with xlat tables)

        push    cx                  ; save number of bytes to compare
        push    ds                  ; save data segment

        les     di,pbCaseCmpBuf     ; (es:di) -> special compare buffer
        lds     si,pbDirEnt         ; (ds:si) -> DirEnt name
        shr     cx,1                ; (cx)    =  # words to move,
                                    ;            carry = odd byte, if any
        rep movsw
        adc     cx,0                ; (cx) = 1, if 1 odd byte left to move
        rep movsb                   ; move last byte, if any

        pop     ds                  ; (ds) selector for data segment
        pop     cx                  ; (cx) = # bytes to compare

;   Set up pointers for loop

        les     di,pCPInfo                  ; (es:di) -> Code Page Info record
        mov     si,word ptr pbCaseCmpBuf    ; (si) = offset of spcl cmp buffer
        lds     bx,es:[di].pCaseTable       ; (ds:bx) -> XLAT table

        test    es:[di].bFlags,CPF_DBCS     ; check for DBCS
        jnz     nucas50                     ; DBCS chars

        les     di,pbDest                   ; (es:di) -> Destination buffer

;   Now do upcase & move, one byte at a time
;
;   (ds:si) -> Special compare buffer (DirEnt name)
;   (ds:bx) -> XLAT table
;   (es:di) -> Subject name
;   (cx)    =  bytes to process

nucas20:lodsb                       ; (al) = mixed-case character (DirEnt name)
        xlat                        ; (al) = upper-case character
        stosb                       ; store byte in destination
        loop    nucas20             ; continue loop

;   All done

        jmp     short nucas99

;   DBCS character(s) present - must not upcase it(them)

;   (es:di) -> Code Page Info record
;   (ds:bx) -> XLAT table
;   (ds:si) -> special compare buffer

;   Save BP & set up to point to DBCS table

nucas50:push    bp

        mov     ax,es:[di].oDBCSTable   ; (ds:ax) -> DBCS table
        les     di,pbDest               ; (es:di) -> Destination buffer
        mov     bp,ax                   ; (ds:bp) -> DBCS table

        xor     ah,ah                   ; (ah) = 0

;   Now do upcase & move, one byte at a time
;
;   (ds:si) -> Special compare buffer (DirEnt name)
;   (ds:bp) -> DBCS table
;   (ds:bx) -> XLAT table
;   (es:di) -> Subject name
;   (cx)    =  bytes to process
;   (TOS)   =  save BP

nucas55:lodsb                           ; (al) = byte from mixed-case DirEnt name
        xchg    ax,si                   ; (si) = byte value,
                                        ; (ax) = saved si offset)
        test    byte ptr ds:[bp+si],1   ; check for DBCS character
        xchg    ax,si                   ; (al) = byte from mixed-case DirEnt name,
                                        ; (si) = offset in DirEnt name
        jnz     nucas70                 ; DBCS lead byte, jump

        xlat                        ; (al) = upper-case character
        stosb                       ; store byte in destination
        loop    nucas55             ; continue loop

;   All done

        jmp     short nucas98

;   DBCS lead byte - don't upcase, just move 2 bytes

nucas70:stosb                       ; store lead byte
        movsb                       ; move 2nd byte
        dec     cx                  ; (cs) = # bytes left + 1
        loop nucas55                ; (cx) = # bytes left, continue loop

;   all done
                                    ; drop through, to return

nucas98:pop     bp

nucas99:ret

NameUpCase&SEG  endp

;**     NameUpCaseCmp - Compare up-cased name to mixed-case DirEnt name
;       =============
;
;       Case insensitive compare (code page correct).
;
;       DirEnt name must be upcased before compare.  Since most searches
;       fail within a few bytes, it is upcased one character at a time.
;       This is done by using XLAT with the CaseTable for the associated
;       code page (indexed by 1 byte in DirEnt).
;
;       If DirEnt name has any DBCS characters, they (2 bytes) must be
;       skipped instead of doing the XLAT.
;
;       Since the 286 only has 2 segment registers, we move the DirEnt
;       name into a temp buffer in the same segment as the xlat tables.
;       We will probably add a 386-only routine that uses FS & GS some-
;       time in the future.
;
;       ENTRY   pbSubject   - pointer to Subject name (already upcased)
;               cbSubject   - length of Subject name
;               pbDirEnt    - pointer to DirEnt name (mixed-case)
;               cbDirEnt    - length of DirEnt name
;               pCPInfo     - pointer to CodePageInfo for DirEnt name
;
;       EXIT    (AX) = 0    -> exact match
;                    > 0    -> Subject name > DirEnt name
;                    < 0    -> Subject name < DirEnt name

; @75229 Put function in both segs
public NameUpCaseCmp&SEG
NameUpCaseCmp&SEG proc CALLTYPE   uses ds si di, \
        pbSubject:dword, cbSubject:word,   \
        pbDirEnt:dword, cbDirEnt:word,     \
        pCPInfo:dword

;   Find length of shorter name

        mov     cx,cbSubject        ; (cx) = # bytes in Subject name
        cmp     cx,cbDirEnt         ; find length of shorter name
        jb      nucmp10             ; Subject len <  DirEnt len
        mov     cx,cbDirEnt         ; Subject len >= DirEnt len

;   Move DirEnt name to special compare buffer (in segment with xlat tables)

nucmp10:push    cx                  ; save number of bytes to compare
        push    ds                  ; save data segment

        les     di,pbCaseCmpBuf     ; (es:di) -> special compare buffer
        lds     si,pbDirEnt         ; (ds:si) -> DirEnt name

        shr     cx,1                ; (cx)    =  # words to move,
                                    ;            carry = odd byte, if any
        rep movsw
        adc     cx,0                ; (cx) = 1, if 1 odd byte left to move
        rep movsb                   ; move last byte, if any

        pop     ds                  ; (ds) selector for data segment
        pop     cx                  ; (cx) = # bytes to compare

;   Set up pointers for loop

        les     di,pCPInfo                  ; (es:di) -> Code Page Info record
        mov     si,word ptr pbCaseCmpBuf    ; (si) = offset of spcl cmp buffer
        lds     bx,es:[di].pCaseTable       ; (ds:bx) -> XLAT table

        test    es:[di].bFlags,CPF_DBCS     ; check for DBCS
        jnz     nucmp50                     ; DBCS chars

        les     di,pbSubject                ; (es:di) -> Subject name

;   Now do upcase & compare, one byte at a time
;
;   (ds:si) -> Special compare buffer (DirEnt name)
;   (ds:bx) -> XLAT table
;   (es:di) -> Subject name
;   (cx)    =  bytes to process

        or      cx,cx               ; zero length?
        jz      nucmp35             ; yes, bail out

;   Top of loop

nucmp20:lodsb                       ; (al) = mixed-case character (DirEnt name),
                                    ; (si) incremented
        xlat                        ; (al) = upper-case character
        scasb                       ; compare to up-cased char (Subject name),
                                    ; (di) incremented
        loopz   nucmp20             ; match, continue loop
        jnz     nucmp30             ; no match, exit loop

;   Names compared up to minimum length - now check for unequal lengths

        mov     cx,cbSubject
        cmp     cx,cbDirEnt
        jb      nucmp35             ; Subject < DirEnt
        ja      nucmp40             ; Subject > DirEnt
        xor     ax,ax               ; all bytes equal, return 0
        jmp     short nucmp99

;   Names did not compare - first restore ss & enable interrupts

nucmp30:jb      nucmp40             ; Subject name > DirEnt name

nucmp35:mov     ax,-1               ; Subject name < DirEnt name
        jmp     short nucmp99       ; return ax < 0 (or > 127)

nucmp40:mov     ax,1                ; Subject name > DirEnt name
                                    ; return ax > 0
        jmp     short nucmp99       ; done, go return

;   DBCS character(s) present - must not upcase it(them)

;   (es:di) -> Code Page Info record
;   (ds:bx) -> XLAT table
;   (ds:si) -> special compare buffer

;   Save BP & set up to point to DBCS table

nucmp50:push    bp

        mov     ax,es:[di].oDBCSTable   ; (ds:ax) -> DBCS table
        les     di,pbSubject            ; (es:di) -> Subject name
        mov     bp,ax                   ; (ds:bp) -> DBCS table

        xor     ah,ah                   ; (ah) = 0

;   Now do upcase & compare, one byte at a time
;
;   (ds:si) -> Special compare buffer (DirEnt name)
;   (ds:bp) -> DBCS table
;   (ds:bx) -> XLAT table
;   (es:di) -> Subject name
;   (cx)    =  bytes to process
;   (TOS)   =  saved (BP)

;   Top of loop

nucmp55:lodsb                           ; (al) = byte of mixed-case DirEnt name,
                                        ; (si) is incremented
        xchg    ax,si                   ; (si) = byte value,
                                        ; (ax) = saved si offset
        test    byte ptr ds:[bp+si],1   ; check for DBCS character
        xchg    ax,si                   ; (al) = byte of mixed-case DirEnt name,
                                        ; (si) = offset in DirEnt name
        jnz     nucmp70                 ; DBCS lead byte, jump

        xlat                        ; (al) = upper-case character
        scasb                       ; compare to up-cased char (Subject name),
                                    ; (di) incremented
        loopz   nucmp55             ; match, continue loop
        jnz     nucmp80             ; no match, exit loop

;   Names compared up to lesser length - now check for unequal lengths

nucmp60:pop     bp                  ; restore (BP)
        mov     cx,cbSubject
        cmp     cx,cbDirEnt
        jb      nucmp90             ; Subject < DirEnt
        ja      nucmp95             ; Subject > DirEnt
        xor     ax,ax               ; all bytes equal, return 0
        jmp     short nucmp99

;   DBCS lead byte - don't upcase & compare 2 bytes

nucmp70:scasb                       ; compare lead byte
        jne     nucmp80             ; no match - done

;   DBCS lead bytes matched - now compare 2nd byte
        cmpsb                       ; compare 2nd byte
        jne     nucmp80             ; no match - done

;   DBCS character (2 bytes) matched - continue loop
        dec     cx                  ; (cx) = # bytes left + 1
        loop nucmp55                ; (cx) = # bytes left

;   no more bytes left
        jmp     short nucmp60       ; go finish up (common code)

;   Names did not compare

nucmp80:pop     bp
        jb      nucmp95             ; Subject name > DirEnt name

nucmp90:mov     ax,-1               ; Subject name < DirEnt name
        jmp     short nucmp99       ; return ax < 0 (or > 127)

nucmp95:mov     ax,1                ; Subject name > DirEnt name
                                    ; return ax > 0
                                    ; drop through, to return

nucmp99:ret                         ; return (ax)

NameUpCaseCmp&SEG  endp

ENDM
end
