                                                                      COMMENT ~
 STRINGS.ASM -- String Manipulation Procedures

   From `BLUEBOOK of ASSEMBLY ROUTINES for the IBM PC & XT'
         by Christopher L. Morgan
         Copyright (C) 1984 by The Waite Group, Inc.

   Purpose: These routines manipulate strings.

   Contents:
   ---------
   COMPARE      --  Compare two strings
   LEXINSERT    --  Lexigraphically insert
   LEXSEARCH    --  Search a lexigraphically ordered list
   LOWERCASE    --  Convert to lower case
   SORTB        --  Bubble sort a string array
   STRINSERT    --  Insert one string in another
   STRSEARCH    --  Search for one string within another
   SWITCH       --  Switch two strings
   UPPERCASE    --  Convert to upper case
 _____________________________________________________________________________
                                                                              ~
DATAS   SEGMENT PUBLIC
        DUMMY   DB      5 DUP(?)
DATAS   ENDS
;_____________________________________________________________________________
CODES   SEGMENT
        PUBLIC LOWERCASE,UPPERCASE,STRSEARCH,STRINSERT
        PUBLIC LEXSEARCH,LEXINSERT,COMPARE,SWITCH,SORTB
        ASSUME CS:CODES,DS:DATAS
;____________________________I/O ROUTINES_____________________________________
;Routine to convert string to lower case
LOWERCASE       PROC    FAR
        PUSH    BX                              ;Save registers
        PUSH    CX
        PUSH    AX
;
;Get the length
        MOV     CX,[BX]                         ;First two bytes contain length
        INC     BX                              ;Point to beginning of text
        INC     BX
;
;Loop through the bytes of the string
LOWERCASE1:
        MOV     AL,[BX]                         ;Get the character
        CMP     AL,'A'                          ;Below the upper case chars?
        JB      LOWERCASE2                      ;If so, skip
        CMP     AL,'Z'                          ;Above the upper case chars?
        JA      LOWERCASE2                      ;If so, skip
        OR      AL,20H                          ;OR bit 5 into the byte
LOWERCASE2:
        MOV     [BX],AL                         ;Store the character
        INC     BX                              ;Point to next character
        LOOP    LOWERCASE1
        POP     AX                              ;Restore registers
        POP     CX
        POP     BX
        RET
LOWERCASE       ENDP
;------------------------------------------------------------------------------
;Routine to convert string to uppercase
;
UPPERCASE       PROC    FAR
        PUSH    BX                              ;Save registers
        PUSH    CX
        PUSH    AX
;
;Get the length
        MOV     CX,[BX]                         ;First two bytes contain length
        INC     BX                              ;Point to beginning of text
        INC     BX
;
;Loop through the bytes of the string
UPPERCASE1:
        MOV     AL,[BX]                         ;Get the character
        CMP     AL,'a'                          ;Below the lower case chars?
        JB      UPPERCASE2                      ;If so, skip
        CMP     AL,'z'                          ;Above the lower case chars?
        JA      UPPERCASE2                      ;If so, skip
        AND     AL,5FH                          ;Mask out bit number 5
UPPERCASE2:
        MOV     [BX],AL                         ;Store the character
        INC     BX                              ;Point to next character
        LOOP    UPPERCASE1
        POP     AX                              ;Restore registers
        POP     CX
        POP     BX
        RET
UPPERCASE       ENDP
;------------------------------------------------------------------------------
;Routine to search for one string within another
;
STRSEARCH       PROC    FAR
        PUSH    SI                              ;Save registers
        PUSH    DI
        PUSH    CX
;
;Get length of destination and point to first byte
        MOV     SI,DX                           ;Use source index
        LODSW                                   ;Get the length of destination
        MOV     CX,AX                           ;Use the length as a count
        MOV     DX,SI                           ;Text begins here
STRSEARCH1:
;
;Point indices to beginning of source and destination
        MOV     SI,BX                           ;Load source index
        MOV     DI,DX                           ;Load destination index
;
;Scan for a match
        MOV     AL,[SI+2]                       ;Get the first character
        CLD                                     ;Point forward
        REPNZ   SCASB                           ;Scan for match
        JCXZ    STRSEARCH2                      ;If no match, quit
;
;Got a match of first characters -- now check the entire string
        MOV     DX,DI                           ;Save current dest location
        DEC     DI                              ;Beginning of word
        LODSW                                   ;Get source length
        XCHG    CX,AX                           ;Use sourc cnt & save dest cnt
        REPZ    CMPSB                           ;Compare two strings
        JCXZ    STRSEARCH3                      ;It`s a match if no more source
;
;Continue the scan
        XCHG    CX,AX                           ;Use destination count
        JMP     STRSEARCH1                      ;Back for more scanning of dest
;
;No match is possible
STRSEARCH2:
        MOV     AL,0                            ;Unsuccessful outcome
        JMP     STRSEARCHXIT
;
;Found a match
STRSEARCH3:
        DEC     DX                              ;Point to beginning of match
        MOV     AL,0FFH                         ;Successful outcome
        JMP     STRSEARCHXIT
STRSEARCHXIT:
        POP     CX                              ;Restore registers
        POP     DI
        POP     SI
        RET
STRSEARCH       ENDP
;------------------------------------------------------------------------------
;Routine to insert one string within another
;
STRINSERT       PROC    FAR
        PUSH    SI                              ;Save registers
        PUSH    DI
        PUSH    CX
        PUSH    AX
;
;Find current end of destination string
        MOV     SI,BP                           ;Start of string
        ADD     SI,ES:[SI]                      ;Point to next to last byte
        INC     SI                              ;Adjust for length information
;
;Find new end of destination string and update length
        MOV     DI,SI                           ;Get old end of destination
        MOV     AX,[BX]                         ;Get length of source
        ADD     DI,DX                           ;New end of destination
        ADD     ES:[BP],AX                      ;New length of destination
;
;Move tail of destination string out of the way
        MOV     CX,SI                           ;SI + DX + 1 is the count
        SUB     CX,DX
        INC     CX
        STD                                     ;Backward direction
REP     MOVS    BYTE PTR[DI],ES:[SI]            ;Move the tail
;
;Move source string into place
        MOV     DI,DX                           ;Destination of move
        MOV     SI,BX                           ;Source of movw
        CLD                                     ;Forward direction
        LODSW                                   ;Length of source
        MOV     CX,AX                           ;The count
        REP     MOVSB                           ;Make the string move
STRINSERTXIT:
        POP     AX                              ;Restore registers
        POP     CX
        POP     DI
        POP     SI
        RET
STRINSERT       ENDP
;------------------------------------------------------------------------------
;Routine to search for a word in an ordered list of words
;
LEXSEARCH       PROC    FAR
;
        PUSH    SI                              ;Save register
        PUSH    DI
        PUSH    CX
;
;Point to beginning of list and get its length
        MOV     DI,BP                           ;Beginning of list
        MOV     CX,[DI]                         ;Get length
        INC     DI
        INC     DI
;
;Compare source word with words in the list
LEXSEARCH1:
        MOV     DX,DI                           ;Save beginning of dest word
        CLD                                     ;Forward direction
        MOV     SI,BX                           ;Point to beginning of source
        INC     SI
        INC     SI
;
;Compare source word with a word of the list
LEXSEARCH2:
;
;Check for end of the list
        JCXZ    LEXSEARCH5                      ;End of list -- insert it
;
;Set up <CR> as scan character
        MOV     AL,13                           ;ASCII <CR>
;
;Check for end of source word
        CMP     [SI],AL                         ;Source byte = <CR>?
        JE      LEXSEARCH4                      ;Found end of source word
;
;Check for end of destination word
        CMP     ES:[DI],AL                      ;Destination byte = <CR>?
        JE      LEXSEARCH3                      ;No match -- go to next word
;
;Compare character by character
        DEC     CX
        CMPSB                                   ;Check for match
        JE      LEXSEARCH2                      ;Matched -- check next char
        JB      LEXSEARCH5                      ;Too high -- this is the place
;
;Scan for next <CR>
LEXSEARCH3:
        REPNZ   SCASB                           ;Scan until <CR>
        JMP     LEXSEARCH1                      ;Next word
;
;End of source word was found
LEXSEARCH4:
        CMP     [DI],AL                         ;Dest character = <CR>?
        JE      LEXSEARCH6                      ;End of destination word?
;
;Found a spot to insert the word
LEXSEARCH5:
        MOV     AL,0FFH                         ;Success
        JMP     LEXSEARCHXIT
;
;Word already present
LEXSEARCH6:
        MOV     AL,00H                          ;Already there
        JMP     LEXSEARCHXIT
LEXSEARCHXIT:
        POP     CX                              ;Restore registers
        POP     DI
        POP     SI
        RET
LEXSEARCH       ENDP
;------------------------------------------------------------------------------
;Routine to insert a word in an ordered list of words
;
LEXINSERT       PROC    FAR
        PUSH    AX                              ;Save register
        CALL    LEXSEARCH                       ;Search for match
        CMP     AL,0                            ;Already there?
        JE      LEXINSERTXIT                    ;Skip, if so
        CALL    STRINSERT                       ;Insert the new word
LEXINSERTXIT:
        POP     AX                              ;Restore register
        RET
LEXINSERT       ENDP
;------------------------------------------------------------------------------
;Routine to compare two strings
;
COMPARE PROC    FAR
        PUSH    SI                              ;Save registers
        PUSH    DI
        PUSH    CX
        REPZ    CMPSB                           ;One compare does it !
        POP     CX                              ;Restore registers
        POP     DI
        POP     SI
COMPARE ENDP
;------------------------------------------------------------------------------
;Routine to switch two strings
;
SWITCH  PROC    FAR
        PUSH    SI                              ;Save registers
        PUSH    DI
        PUSH    CX
        PUSH    AX
        CLD                                     ;Forward direction
SWITCH1:
        MOV     AL,[DI]                         ;Get byte from destination
        MOVSB                                   ;Move from source to dest
        MOV     ES:[SI-1],AL                    ;Put byte in source
        LOOP    SWITCH1                         ;Loop back for more
        POP     AX                              ;Restore registers
        POP     CX
        POP     DI
        POP     SI
        RET
SWITCH  ENDP
;------------------------------------------------------------------------------
;Routine to sort a string array
;
SORTB   PROC    FAR
        PUSH    SI                              ;Save registers
        PUSH    DI
        PUSH    CX
        PUSH    AX
;
;Adjust count for one less than number of items
        DEC     CX                              ;Adjust the count
;
;Outer loop -- for SI = 1 to N-1
SORTB1:
        PUSH    CX                              ;Save the count
        MOV     DI,SI                           ;Destination points to source
;
;Inner loop -- for DI = SI+1 to N
SORTB2:
        PUSH    CX                              ;Save the count
        ADD     DI,DX                           ;Point to next destination
        MOV     CX,DX                           ;Entry length
        CALL    COMPARE                         ;Compare the strings
        JLE     SORTB3                          ;Skip if source <= destination
        CALL    SWITCH                          ;Switch if not
SORTB3:
        POP     CX                              ;Restore the count
        LOOP    SORTB2
        ADD     SI,DX                           ;Point to next source
        POP     CX                              ;Restore the count
        LOOP    SORTB1
SORTBXIT:
        POP     AX                              ;Restore registers
        POP     CX
        POP     DI
        POP     SI
        RET
SORTB   ENDP
;-----------------------------------------------------------------------------
CODES   ENDS
;
        END
;_____________________________________________________________________________
;>>>>> Physical EOF STRINGS.ASM <<<<<
