
; *******************************************************
; *                                                     *
; *     Turbo Pascal Runtime Library                    *
; *     String Support Routines                         *
; *                                                     *
; *     Copyright (C) 1988,90 Borland International     *
; *                                                     *
; *******************************************************

        TITLE   STRH

        INCLUDE SE.ASM

CODE    SEGMENT BYTE PUBLIC

        ASSUME  CS:CODE

; Publics

        PUBLIC  SLoad,SStore,SCopy,SConcat,SPos,SCompare
        PUBLIC  SChar,SPacked,SInsert,SDelete

; Load string
; In    [SP+4] = Destination pointer
;       [SP]   = Source pointer
; Out   SP     = SP+4

SLoad:

        CLD
        MOV     BX,SP
        MOV     DX,DS
        LES     DI,SS:[BX+8]
        LDS     SI,SS:[BX+4]
        LODSB
        STOSB
        MOV     CL,AL
        XOR     CH,CH
        REP     MOVSB
        MOV     DS,DX
        RETF    4

; Store string
; In    [SP+6] = Source pointer
;       [SP+2] = Destination pointer
;       [SP]   = Maximum length
; Out   SP     = SP+10

SStore:

        CLD
        MOV     BX,SP
        MOV     DX,DS
        LDS     SI,SS:[BX+10]
        LES     DI,SS:[BX+6]
        MOV     CX,SS:[BX+4]
        LODSB
        CMP     AL,CL
        JBE     @@1
        MOV     AL,CL
@@1:    STOSB
        MOV     CL,AL
        XOR     CH,CH
        REP     MOVSB
        MOV     DS,DX
        RETF    10
COMMENT~
; Copy standard function
; In    [SP+8] = Destination pointer
;       [SP+4] = Source pointer
;       [SP+2] = Index
;       [SP]   = Length
; Out   SP     = SP+8

SCopy:

        CLD
        MOV     BX,SP
        MOV     DX,DS
        LES     DI,SS:[BX+12]
        LDS     SI,SS:[BX+8]
        MOV     AL,[SI]
        XOR     AH,AH
        MOV     CX,SS:[BX+6]
        OR      CX,CX
        JG      @@1
        MOV     CX,1
@@1:    ADD     SI,CX
        SUB     AX,CX
        JB      @@3
        INC     AX
        MOV     CX,SS:[BX+4]
        OR      CX,CX
        JGE     @@2
        XOR     CX,CX
@@2:    CMP     AX,CX
        JBE     @@4
        MOV     AX,CX
        JMP     SHORT @@4
@@3:    XOR     AX,AX
@@4:    STOSB
        MOV     CX,AX
        REP     MOVSB
        MOV     DS,DX
        RETF    8
~
; Concat standard function
; In    [SP+4] = Destination pointer
;       [SP]   = Source pointer
; Out   SP     = SP+4

SConcat:

        CLD
        MOV     BX,SP
        MOV     DX,DS
        LES     DI,SS:[BX+8]
        LDS     SI,SS:[BX+4]
        MOV     CL,ES:[DI]
        XOR     CH,CH
        LODSB
        ADD     ES:[DI],AL
        JNC     @@1
        MOV     BYTE PTR ES:[DI],255
        MOV     AL,CL
        NOT     AL
@@1:    ADD     DI,CX
        INC     DI
        MOV     CL,AL
        REP     MOVSB
        MOV     DS,DX
        RETF    4

; Pos standard function
; In    [SP+4] = Source pointer
;       [SP]   = Destination pointer
; Out   AX     = Position
;       SP     = SP+8
COMMENT~
SPos:

        ARG     SourceP,DWORD,1
        ARG     DestP,DWORD,1

        ENTRY   FAR
        PUSH    DS
        LDS     SI,SourceP
        CLD
        LODSB
        OR      AL,AL
        JE      @@2
        MOV     DL,AL
        XOR     DH,DH
        LES     DI,DestP
        MOV     CL,ES:[DI]
        XOR     CH,CH
        SUB     CX,DX
        JB      @@2
        INC     CX
        INC     DI
@@1:    LODSB
        REPNE   SCASB
        JNE     @@2
        MOV     AX,DI
        MOV     BX,CX
        MOV     CX,DX
        DEC     CX
        REPE    CMPSB
        JE      @@3
        MOV     DI,AX
        MOV     CX,BX
        MOV     SI,SourceP.ofs
        INC     SI
        JMP     @@1
@@2:    XOR     AX,AX
        JMP     SHORT @@4
@@3:    DEC     AX
        SUB     AX,DestP.ofs
@@4:    POP     DS
        EXIT

; String compare
; In    [SP+4] = Source pointer
;       [SP]   = Destination pointer
; Out   ZF     = 1 if Source = Dest
;       CF     = 1 if Source < Dest
;       SP     = SP+8
SCompare:

        CLD
        MOV     BX,SP
        MOV     DX,DS
        LDS     SI,SS:[BX+8]
        LES     DI,SS:[BX+4]
        LODSB
        MOV     AH,ES:[DI]
        INC     DI
        MOV     CL,AL
        CMP     CL,AH
        JBE     @@1
        MOV     CL,AH
@@1:    OR      CL,CL
        JE      @@2
        XOR     CH,CH
        REP     CMPSB
        JNE     @@3
@@2:    CMP     AL,AH
@@3:    MOV     DS,DX
        RETF    8
~
; Convert character to string
; In    [SP+2] = Destination pointer
;       [SP]   = Character
; Out   SP     = SP+2

SChar:

        CLD
        MOV     BX,SP
        LES     DI,SS:[BX+6]
        MOV     AL,1
        STOSB
        MOV     AL,SS:[BX+4]
        STOSB
        RETF    2

; Convert packed string to string
; In    [SP+6] = Destination pointer
;       [SP+2] = Source pointer
;       [SP]   = Length
; Out   SP     = SP+6

SPacked:

        CLD
        MOV     BX,SP
        MOV     DX,DS
        LES     DI,SS:[BX+10]
        LDS     SI,SS:[BX+6]
        MOV     AX,SS:[BX+4]
        STOSB
        MOV     CX,AX
        REP     MOVSB
        MOV     DS,DX
        RETF    6
COMMENT~
; Insert standard procedure
; Insert(S,D,I) = Copy(D,1,I-1) + S + Copy(D,I,255)

SInsert:

        ARG     SourceP,DWORD,1
        ARG     DestP,DWORD,1
        ARG     DestLen,WORD,1
        ARG     Index,WORD,1
        LOC     Temp1,BYTE,256
        LOC     Temp2,BYTE,256

        ENTRY   FAR
        CMP     Index,1
        JGE     @@1
        MOV     Index,1
@@1:    LEA     DI,Temp1
        PUSH    SS
        PUSH    DI
        LES     DI,DestP
        PUSH    ES
        PUSH    DI
        MOV     AX,1
        PUSH    AX
        MOV     AX,Index
        DEC     AX
        PUSH    AX
        PUSH    CS
        CALL    SCopy
        LES     DI,SourceP
        PUSH    ES
        PUSH    DI
        PUSH    CS
        CALL    SConcat
        LEA     DI,Temp2
        PUSH    SS
        PUSH    DI
        LES     DI,DestP
        PUSH    ES
        PUSH    DI
        PUSH    Index
        MOV     AX,255
        PUSH    AX
        PUSH    CS
        CALL    SCopy
        PUSH    CS
        CALL    SConcat
        LES     DI,DestP
        PUSH    ES
        PUSH    DI
        PUSH    DestLen
        PUSH    CS
        CALL    SStore
        EXIT
; Delete standard procedure
; Delete(S,I,N) = Copy(S,1,I-1) + Copy(S,I+N,255)

SDelete:

        ARG     StrP,DWORD,1
        ARG     Index,WORD,1
        ARG     Count,WORD,1
        LOC     Temp1,BYTE,256
        LOC     Temp2,BYTE,256

        ENTRY   FAR
        CMP     Count,0
        JLE     @@2
        CMP     Index,0
        JLE     @@2
        CMP     Index,255
        JG      @@2
        CMP     Count,255
        JLE     @@1
        MOV     Count,255
@@1:    LEA     DI,Temp1
        PUSH    SS
        PUSH    DI
        LES     DI,StrP
        PUSH    ES
        PUSH    DI
        MOV     AX,1
        PUSH    AX
        MOV     AX,Index
        DEC     AX
        PUSH    AX
        PUSH    CS
        CALL    SCopy
        LEA     DI,Temp2
        PUSH    SS
        PUSH    DI
        LES     DI,StrP
        PUSH    ES
        PUSH    DI
        MOV     AX,Index
        ADD     AX,Count
        PUSH    AX
        MOV     AX,255
        PUSH    AX
        PUSH    CS
        CALL    SCopy
        PUSH    CS
        CALL    SConcat
        LES     DI,StrP
        PUSH    ES
        PUSH    DI
        MOV     AX,255
        PUSH    AX
        PUSH    CS
        CALL    SStore
@@2:    EXIT
~
;-------------------------------------------------------------------------------
; SCompare compares two strings and sets the zero and carry flag according to
; the result of the comparison. These flag are set as if two unsigned integers
; had been compared. The routine compares all the characters of the shorter
; string to the ones of the longer string, starting at index 1 in each string.
; The first mismatch determines the result of the comparison. If the characters
; compared are all equal, the string's length is compared: The longer string is
; bigger.
;
; On entry:  [SP+4]  Address of destination string
;            [SP+8]  Address of source string
;
; On exit:   ZF = 1  if Source = Destination
;            CF = 1  if Source < Destination
;-------------------------------------------------------------------------------

SCompare     PROC    FAR
             CLD                       ; auto increment for string instructions
             MOV     DX, DS            ; save callers data segment
             MOV     BX, SP            ; make new frame pointer
             LDS     SI, SS:[BX+8]     ; pointer to source
             LES     DI, SS:[BX+4]     ; pointer to destination
             LODSB                     ; source len, SI ptr to 1. char of source
             MOV     AH, ES:[DI]       ; length destination
             INC     DI                ; DI ptr to 1st char of destination
             MOV     CL, AL            ; duplicate length source
             SUB     CL, AH            ; length source - length destination
             SBB     CH, CH            ; CH = FF if length source < length dest.
             AND     CL, CH            ; CL = 0, if length source >= length dest
             ADD     CL, AH            ; CL = Min (length source, length dest)
             XOR     CH, CH            ; zero-extend to word, set ZF = 1
             REPE    CMPSB             ; comp. strings char by char until diff.
             JNE     $str_differ       ; strings differ
             CMP     AL, AH            ; strings equal, compare string length
$str_differ: MOV     DS, DX            ; restore caller's data segment
             RET     8                 ; pop parameters and return
SCompare     ENDP



;-------------------------------------------------------------------------------
; Copy standard function: Copy (VAR S: STRING; Index, Count: INTEGER): STRING
;
; Copy copies Count characters from S, starting at position Index to a 256 byte
; temporary string buffer. If there are less than Count characters in S after
; Index, the routine copies all the characters from Index to the end of S (note
; that this number can be zero).
;
; On entry:  [SP+4]  Count
;            [SP+6]  Index
;            [SP+8]  Address of source string S
;            [SP+12] Address of destination string
;
; On exit:   [SP]    Address of destination string
;-------------------------------------------------------------------------------

SCopy        PROC    FAR
             CLD                       ; auto increment for string instructions
             PUSH    DS                ; save caller's data segment
             MOV     BX, SP            ; make new frame pointer
             LES     DI, SS:[BX+14]    ; load pointer to destination
             LDS     SI, SS:[BX+10]    ; load pointer to source
             MOV     AX, SS:[BX+6]     ; get copy count
             MOV     BX, SS:[BX+8]     ; get index
             CWD                       ; DX = FFFF if count negative, else DX=0
             NOT     DX                ; DX = FFFF if count positive, else DX=0
             AND     AX, DX            ; Max (count, 0)
             DEC     BX                ; index - 1
             CMP     BH, 80h           ; index-1 < 0 ?
             SBB     DX, DX            ; DX = FFFF if index-1 pos., else DX = 0
             AND     BX, DX            ; Max (index-1, 0)
             MOV     CL, [SI]          ; source length
             INC     SI                ; pointer to 1st char of source
             XOR     CH, CH            ; zero-extend source length to word
             SUB     BX, CX            ; index - 1 - length
             SBB     DX, DX            ; DX=0, if index-1 > length, else DX=FFFF
             AND     BX, DX            ; - max # of chars that could be copied
             NEG     BX                ; maxcount
             SUB     AX, BX            ; count - maxcount
             SBB     DX, DX            ; DX=0, if count > maxcount, else DX=FFFF
             AND     AX, DX            ; count - maxcount = 0, if count>maxcount
             ADD     AX, BX            ; AX = Min (Count, MaxCount)
             SUB     CX, BX            ; CX = Min (Index-1, Length)
             ADD     SI, CX            ; pointer to first char to copy
             STOSB                     ; store length of result
             MOV     CX, AX            ; need it in CX for string instruction
             SHR     CX, 1             ; number of chars odd ?
             JNC     $even_copy        ; no, even
             MOVSB                     ; move single char
$even_copy:  REP     MOVSW             ; copy chars two at a time
             POP     DS                ; restore caller's data segment
             RET     8                 ; leave destination pointer on stack
SCopy        ENDP





;-------------------------------------------------------------------------------
; Delete standard procedure: Delete (VAR S: STRING; Index, Count: INTEGER);
;
; Delete deletes Count characters in S, beginning at position Index. The
; algorithm determines if the characters are effectively deleted from the end
; of the string, in which case it simply sets the string length to Index-1. If
; characters are to be deleted from somewhere in the middle of the string, it
; calculates the number of characters in the string after the last deleted one
; and copies these characters to position Index, decrementing the string length
; by Count.
;
; On entry:  [SP+4]  Count
;            [SP+6]  Index
;            [SP+8]  Pointer to S
;-------------------------------------------------------------------------------

SDelete      PROC    FAR
             PUSH    DS                ; save TURBO-Pascal data segment
             MOV     BX, SP            ; make new framepointer
             MOV     CX, SS:[BX+6]     ; CX = count
             MOV     AX, SS:[BX+8]     ; AX = index
             DEC     AX                ; index - 1
             CWD                       ; DX = FFFFh if AX < 0 (else DX = 0)
             AND     DX, AX            ; index ( else 0)
             SUB     AX, DX            ; 0 (else index) = new index
             ADD     DX, CX            ; index + count (else count) = new count
             JLE     $end_del          ; exit, if new count <= 0
             LDS     SI, SS:[BX+10]    ; address of string
             MOV     CL, [SI]          ; length of string
             XOR     CH, CH            ; clear msb of string length
             SUB     CX, AX            ; length - index + 1
             JLE     $end_del          ; exit, if <= 0,
             SUB     CX, DX            ; length-count-index+1 = # chars to move
             JG      $continue         ; continue, if # of chars > 0
             MOV     [SI], AL          ; new length = index - 1
             JMP     $end_del          ; done
$continue:   CLD                       ; auto-increment
             MOV     DI, DS            ; let both segment register
             MOV     ES, DI            ;  hold segment of string
             SUB     [SI], DL          ; new length = length - deleted chars
             ADD     SI, AX            ; char at index
             INC     SI                ;  becomes destination
             MOV     DI, SI            ;   of move
             ADD     SI, DX            ; char behind the last that will be del.
             SHR     CX, 1             ; number of chars to move odd ?
             JNC     $even_del         ; no
             MOVSB                     ; yes, move single char
$even_del:   REP     MOVSW             ; move the rest
$end_del:    POP     DS                ; restore TURBO-Pascal data segment
             RET     8                 ; return and pop parameters
SDelete      ENDP


;-------------------------------------------------------------------------------
; Insert standard proc.: Insert (VAR Obj, Target: STRING; MaxLen, Pos: INTEGER);
;
; Insert inserts string Obj in string Target at index Pos. The length of the
; resulting string Target may not exceed MaxLen. If the insertion point is at
; the end of Target, the routine appends Obj to Target. It does this by copying
; characters from Obj to the end of Target until all characters are copied or
; the maximum length MaxLen of Target has been reached. If Obj is to be inserted
; at the start or in the middle of Target, as indicated by Pos, the routine
; first creates a 'gap' in Target and then fills this gap with characters from
; Obj.
;
; On entry:  [SP+4]  Position
;            [SP+6]  Maximum length of target string
;            [SP+8]  Address of target string
;            [SP+12] Address of object string
;-------------------------------------------------------------------------------

SInsert      PROC    FAR
             PUSH    BP                ; save TURBO-Pascal framepointer
             PUSH    DS                ; save TURBO-Pascal data segment
             MOV     BP, SP            ; make new framepointer
             LDS     SI, [BP+16]       ; address of object string
             XOR     CX, CX            ; clear msb of length of object string
             OR      CL, [SI]          ; get length of object string
             JZ      $end_ins          ; exit, if length of object string zero
             MOV     DX, [BP+8]        ; DX=pos
             MOV     AX, [BP+10]       ; AX=maximum length of target string
             LES     DI, [BP+12]       ; address of target string
             MOV     BL, ES:[DI]       ; get current length of target string
             MOV     BH, CH            ; clear msb of length of target string
             DEC     DX                ; pos - 1
             CMP     DH, 80h           ; is pos-1 < 0 ?
             SBB     BP, BP            ; BP = FFFF if pos-1 > 0, else BP = 0
             AND     DX, BP            ; if pos-1 < 0 then set pos-1 = 0 (pos=1)
             SUB     DX, BX            ; pos-1 - targetlength
             SBB     BP, BP            ; BP = FFFF, if result negative, else 0
             AND     DX, BP            ; pos-1 - targetlength (0, if res. pos)
             ADD     DX, BX            ; pos-1, if result neg., else target len.
             MOV     BP, AX            ; maximum length of target string
             SUB     BP, DX            ; (maximum length - pos + 1) <= 0 ?
             JBE     $end_ins          ; yes, don't insert
             SUB     BP, CX            ; maximum length - pos + 1 - objectlength
             CLD                       ; select auto-increment
             JA      $make_gap         ; jump, if result positiv
             STOSB                     ; new targetlength will be maximum length
             ADD     CX, BP            ; adjust length of object string
             ADD     DI, DX            ; object string inserted after this char
             JMP     $fill             ; insert object
$make_gap:   MOV     DX, CX            ; save length of object string
             ADD     CX, BX            ; new targetlength = objectlen+targetlen
             SUB     CX, AX            ; new targetlength - maximum length
             SBB     BX, BX            ; BX=FFFF, if result negative, else BX=0
             AND     CX, BX            ; new targetlength - maximum length
             ADD     AX, CX            ; new targetlength = Min(TargLen, MaxLen)
             STOSB                     ; store it
             DEC     DI                ; correct pointer
             ADD     CX, BP            ; new targetlength - objectlength - pos+1
             ADD     DI, AX            ; this will be the last char in target
             XCHG    AX, SI            ; save offset of adress of object string
             MOV     SI, DI            ; now calculate
             SUB     SI, DX            ;  the starting position
             PUSH    DS                ; save segment of address of objectstring
             MOV     BX, ES            ; let both segment register point
             MOV     DS, BX            ; to the segment containing targetstring
             STD                       ; auto-decrement
             REP     MOVSB             ; create gap
             MOV     DI, SI            ; object will be inserted after this char
             MOV     CX, DX            ; get length of object string
             MOV     SI, AX            ; get offset of address of object string
             POP     DS                ; get segment of address of object string
             INC     DI                ; object string will be inserted here
             CLD                       ; auto-increment
$fill:       INC     SI                ; first char of object string
             SHR     CX, 1             ; number of chars to move odd ?
             JNC     $even_ins         ; no
             MOVSB                     ; yes, move a single byte
$even_ins:   REP     MOVSW             ; move the rest
$end_ins:    POP     DS                ; restore TURBO-Pascal data segment
             POP     BP                ; restore TURBO-Pascal frame pointer
             RET     12                ; return and pop parameters
SInsert      ENDP

             ALIGN   4




;-------------------------------------------------------------------------------
; Pos standard function: Pos (VAR Obj, Target: STRING): BYTE
;
; Pos searches the string Target for the first occurence of a string Obj. If Obj
; is found in Target, the index at which the first character of Obj appears in
; Target is returned. If Obj is not found in Target, zero is returned. The
; routine searches the first character of Obj in the first Len(Target)-Len(Obj)
; charcters of Target. Whenever there is a match, the following characters of
; Target are compared to the characters in Obj. If all characters match, Obj has
; been succesfully found in Target.
;
; On entry:  [SP+4] Address of Target
;            [SP+8] Address of Obj
;
; On exit:   AX     index at which Obj first found in Target (0 if not found)
;-------------------------------------------------------------------------------

SPos         PROC    FAR
             CLD                       ; auto-increment for string instructions
             PUSH    DS                ; save TURBO-Pascal data segment
             PUSH    BP                ; save TURBO-Pascal's frame pointer
             MOV     BP, SP            ; make new frame pointer
             LES     DI, [BP+8]        ; address of target string
             LDS     SI, [BP+12]       ; address of object string
             MOV     AX, [SI]          ; AL=length of object, AH=1. char of obj.
             INC     SI                ; adjust
             INC     SI                ;  string pointer
             XOR     CH, CH            ; init result
             DEC     AX                ; length - 1 (changes AH only when AL=0)
             MOV     CL, ES:[DI]       ; length of target string
             SUB     CL, AL            ; targetlength <= objectlength - 1 ?
             JBE     $not_found        ; yes, object not found
             MOV     BH, CL            ; save length of target string
             INC     DI                ; first char of target string
             XCHG    AL, AH            ; AL=1. char of object, AH=length of obj.
             MOV     BP, SI            ; save offset into object
$search:     REPNZ   SCASB             ; scan target string for 1. char of obj.
             JNZ     $not_found        ; exit, if not found
             MOV     BL, CL            ; save remaining length of target string
             MOV     CL, AH            ; number of char to be compared
             MOV     DX, DI            ; save offset into target string
             REPZ    CMPSB             ; compare remaining char of target & obj.
             MOV     SI, BP            ; restore offset into object string
             MOV     DI, DX            ; restore offset into target string
             MOV     CL, BL            ; restore remaining length of target str
             JNZ     $search           ; cont. search, if not all chars matched
             SUB     BH, BL            ; Pos = targetlength - remain. targetlen.
             MOV     CH, BH            ; Pos
$not_found:  POP     BP                ; restore TURBO-Pascal's frame pointer
             POP     DS                ; restore TURBO-Pascal data segment
             MOV     AL, CH            ; Pos
             XOR     AH, AH            ; clear msb
             RET     8                 ; return and pop parameters
SPos         ENDP



CODE    ENDS

        END
