								      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 <<<<<
