; =============================================================================
;
;                          Litos8 text string utility
;
; =============================================================================

; Text strings are allocated using MemAlloc function.

; -----------------------------------------------------------------------------
; Exported user functions (109):
; Misc (10):
;	CharHexToBin - convert ASCII HEX character to a number
;	CharDecToBin - convert ASCII decimal character to a number
;	IsDigit - check digit character
;	IsAlpha - check alpha character
;	UpperCase - convert to uppercase letter
;	LowerCase - convert to lowercase letter
;	UpperLowerCase - convert uppercase/lowercase letter
;	UpperCaseNat - convert to uppercase letter with national
;	LowerCaseNat - convert to lowercase letter with national
;	UpperLowerCaseNat - convert uppercase/lowercase letter with national
; Text managing (6):
;	TextZLength - get length of ASCIIZ string
;	TextLength - get length of text string
;	TextCheckInx - check if character index is valid
;	TextDup - duplicate text string
;	TextFree - delete text string
;	TextResize - resize text string
; New (4):
;	TextNew - allocate new text string
;	TextNewEmpty - allocate new empty text string
;	TextNewZ - create text string from ASCIIZ text
;	TextNewBuf - create text string from buffer
; Get/set character (4):
;	TextGetChar - get character from text
;	TextSetChar - set character in text
;	TextGetFirst - get first character from text
;	TextGetLast - get last character from text
; Add text/character (11):
;	TextAddText - add text string to end of text
;	TextAddBuf - add text string from buffer to end of text
;	TextAddTextZ - add ASCIIZ text to end of text
;	TextAddChar - add character to end of text
;	TextAddSpc - add space character to end of text
;	TextAddMinus - add minus character to end of text
;	TextAddNewLine - add new line CR/LF to end of text
;	TextAddDate - add date to end of text
;	TextAddTime - add time to end of text
;	TextAddDiskSize - add disk size
;	TextAddPathSep - add path separator
; Number (15):
;	TextAddDig - add one digit character to end of text
;	TextAdd2Dig - add two digit characters to end of text
;	TextAdd2DigSpc - add two digit characters with space to end of text
;	TextAddByte - add unsigned byte number to end of text
;	TextAddWord - add unsigned word number to end of text
;	TextAddDWord - add unsigned dword number to end of text
;	TextAddSByte - add signed byte number to end of text
;	TextAddSWord - add signed word number to end of text
;	TextAddSDWord - add signed dword number to end of text
;	TextAddHByte - add HEX byte to end of text
;	TextAddHWord - add HEX word to end of text
;	TextAddHDWord - add HEX dword to end of text
;	TextAddBByte - add BIN byte to end of text
;	TextAddBWord - add BIN word to end of text
;	TextAddBDWord - add BIN dword to end of text
; Delete (6):
;	TextDel - delete part of text
;	TextDelStart - delete start of text
;	TextDelFrom - delete rest of text from given position
;	TextDelEnd - delete end of text
;	TextDelChar - delete one character from text
;	TextDelPathSep - delete end path separator
; Extract text (4):
;	TextLeft - get left part of text
;	TextRight - get right part of text
;	TextFrom - get text from given position
;	TextMid - get middle part of text
; Conversions (7):
;	TextUpper - convert text to uppercase letters
;	TextLower - convert text to lowercase letters
;	TextUpLow - change uppercase/lowercase letters
;	TextUpperNat - convert text to uppercase letters with national chars
;	TextLowerNat - convert text to lowercase letters with national chars
;	TextUpLowNat - change uppercase/lowercase letters with national chars
;	TextCapital - capitalize words
; Compare (2):
;	TextEqu - compare text strings to equality
;	TextComp - compare text strings alphabeticaly
; Trim text (6):
;	TextTrimLeft - trim text from left
;	TextTrimRight - trim text from right
;	TextTrim - trim text from left and right
;	TextTrimMid - trim from the text
;	TextTrimList - trim text using list of forbidden characters
;	TextTrimUnlist - trim text using list of allowed characters
; Search (33):
;	TextFindChFirst - find first character in text
;	TextFindChNext - find next character in text
;	TextFindChLast - find last character in text
;	TextFindChPrev - find previous character in text
;	TextFind2ChFirst - find first two characters in text
;	TextFind2ChNext - find next two characters in text
;	TextFind2ChLast - find last two characters in text
;	TextFind2ChPrev - find previous two characters in text
;	TextFindFirst - find first string in text
;	TextFindNext - find next string in text
;	TextFindLast - find last string in text
;	TextFindPrev - find previous string in text
;	TextFindListFirst - find first character from list in text
;	TextFindListNext - find next character from list in text
;	TextFindUnlistFirst - find first character not in list in text
;	TextFindUnlistNext - find next character not in list in text
;	TextFindListLast - find last character from list in text
;	TextFindListPrev - find previous character from list in text
;	TextFindUnlistLast - find last character not in list in text
;	TextFindUnlistPrev - find previous character not in list in text
;	TextFind2ListFirst - find first character from 2-character list
;	TextFind2ListNext - find next character from 2-character list
;	TextFind3ListFirst - find first character from 3-character list
;	TextFind3ListNext - find next character from 3-character list
;	TextFind4ListFirst - find first character from 4-character list
;	TextFind4ListNext - find next character from 4-character list
;	TextFind2ListLast - find last character from 2-character list
;	TextFind2ListPrev - find previous character from 2-character list
;	TextFind3ListLast - find last character from 3-character list
;	TextFind3ListPrev - find previous character from 3-character list
;	TextFind4ListLast - find last character from 4-character list
;	TextFind4ListPrev - find previous character from 4-character list
;	TextSubstCh - substitute character
; Parse (1):
;	ParseInt - get unsigned number from text
; -----------------------------------------------------------------------------

		CODE_SECTION

; -----------------------------------------------------------------------------
;                   Convert ASCII HEX character to a number
; -----------------------------------------------------------------------------
; INPUT:	AL = HEX character ("0" to "9", "A" to "F" or "a" to "f")
; OUTPUT:	AL = number 0 to 15 (if NC) or AL not changed (if CY)
;		CY = invalid HEX character (AL not changed)
; -----------------------------------------------------------------------------

; ------------- check digit

CharHexToBin:	cmp	al,"0"		; check number MIN
		jb	short CharHexToBin9 ; invalid character
		cmp	al,"9"		; check number MAX
		jbe	short CharHexToBin7 ; character is OK

; ------------- check capital letter

		cmp	al,"A"		; check letter MIN
		jb	short CharHexToBin9 ; invalid character
		cmp	al,"F"		; check letter MAX
		jbe	short CharHexToBin6 ; character is OK

; ------------- check small letter

		cmp	al,"a"		; check letter MIN
		jb	short CharHexToBin9 ; invalid character
		cmp	al,"f"		; check letter MAX
		ja	short CharHexToBin8 ; invalid character

; ------------- convert small letter to BIN (it clears CF)

		sub	al,"0"+("a"-("9"+1)) ; convert ASCII to BIN
		ret			; here is NC

; ------------- convert capital letter to BIN (it clears CF)

CharHexToBin6:	sub	al,"0"+("A"-("9"+1)) ; convert ASCII to BIN
		ret			; here is NC

; ------------- convert ASCII digit to BIN (it clears CF)

CharHexToBin7:	sub	al,"0"		; convert ASCII to BIN
		ret			; here is NC

; ------------- invalid HEX character

CharHexToBin8:	stc			; set error flag
CharHexToBin9:	ret

; -----------------------------------------------------------------------------
;                   Convert ASCII decimal character to a number
; -----------------------------------------------------------------------------
; INPUT:	AL = ASCII decimal character ("0" to "9")
; OUTPUT:	AL = number 0 to 9 (if NC) or AL not changed (if CY)
;		CY = invalid decimal character (AL not changed)
; -----------------------------------------------------------------------------

; ------------- check digit

CharDecToBin:	cmp	al,"0"		; check number MIN
		jb	short CharDecToBin9 ; invalid character
		cmp	al,"9"		; check number MAX
		ja	short CharDecToBin8 ; invalid character

; ------------- convert ASCII digit to BIN (it clears CF)

		sub	al,"0"		; convert ASCII to BIN
		ret			; here is NC

; ------------- invalid HEX character

CharDecToBin8:	stc			; set error flag
CharDecToBin9:	ret

; -----------------------------------------------------------------------------
;                Check if character is digit character (0..9)
; -----------------------------------------------------------------------------
; INPUT:	AL = character
; OUTPUT:	CY = not digit character
; -----------------------------------------------------------------------------

IsDigit:	cmp	al,"0"		; is it digit character?
		jb	short IsDigit8	; not digit character
		cmp	al,"9"+1	; is it digit character?
		cmc			; CY = not digit character
IsDigit8:	ret

; -----------------------------------------------------------------------------
;                Check if character is alpha character (A..Z, a..z)
; -----------------------------------------------------------------------------
; INPUT:	AL = character
; OUTPUT:	CY = not alpha character
; -----------------------------------------------------------------------------

IsAlpha:	cmp	al,"A"		; is it capital alpha character?
		jb	short IsAlpha8	; not alpha character
		cmp	al,"Z"+1	; is it capital alpha character?
		jb	short IsAlpha6	; it is capital alpha character
		cmp	al,"a"		; is lower alpha character?
		jb	short IsAlpha8	; not alpha character
		cmp	al,"z"+1	; is it lower alpha character?
IsAlpha6:	cmc			; CY = not alpha character
IsAlpha8:	ret

; -----------------------------------------------------------------------------
;                Convert to uppercase letter (a..z -> A..Z)
; -----------------------------------------------------------------------------
; INPUT:	AL = character
; OUTPUT:	AL = uppercase letter (not national characters)
;		CY = not converted (not lowercase letter)
; -----------------------------------------------------------------------------

UpperCase:	cmp	al,"a"		; is lower alpha character?
		jb	short UpperLowerCase8 ; not lower alpha character
		cmp	al,"z"		; is it lower alpha character?
		ja	short UpperLowerCase8 ; not lower alpha character
UpperCase2:	sub	al,"a"-"A"	; convert to uppercase
		ret			; here is NC flag

; -----------------------------------------------------------------------------
;                Convert to lowercase letter (A..Z -> a..z)
; -----------------------------------------------------------------------------
; INPUT:	AL = character
; OUTPUT:	AL = lowercase letter (not national characters)
;		CY = not converted (not lowercase letter)
; -----------------------------------------------------------------------------

LowerCase:	cmp	al,"A"		; is upper alpha character?
		jb	short UpperLowerCase8 ; not upper alpha character
		cmp	al,"Z"		; is it upper alpha character?
		ja	short UpperLowerCase8 ; not upper alpha character
LowerCase2:	add	al,"a"-"A"	; convert to lowercase
		ret			; here is NC flag		

; -----------------------------------------------------------------------------
;         Convert lowercase/uppercase letter (A..Z -> a..z, a..z -> A..Z)
; -----------------------------------------------------------------------------
; INPUT:	AL = character
; OUTPUT:	AL = changed letter (not national characters)
;		CY = not converted (not letter)
; -----------------------------------------------------------------------------

UpperLowerCase:	cmp	al,"A"		; is upper alpha character?
		jb	short UpperLowerCase8 ; not upper alpha character
		cmp	al,"Z"		; is it upper alpha character?
		jbe	short LowerCase2 ; upper alpha character, convert

		cmp	al,"a"		; is lower alpha character?
		jb	short UpperLowerCase8 ; not lower alpha character
		cmp	al,"z"		; is it lower alpha character?
		jbe	short UpperCase2 ; lower alpha character, convert

UpperLowerCase8:stc			; CY not valid letter
		ret

; -----------------------------------------------------------------------------
;           Convert to uppercase letter with national characters
; -----------------------------------------------------------------------------
; INPUT:	AL = character
; OUTPUT:	AL = uppercase letter
;		CY = not converted (not letter)
; -----------------------------------------------------------------------------

; ------------- convert base ASCII characters

UpperCaseNat:	call	UpperCase	; convert ASCII characters
		jnc	short UpperCaseNat9 ; character converted OK

; ------------- push registers

		push	bx		; push BX
		push	cx		; push CX

; ------------- get characters from table -> CX

		mov	bx,[KeyMapLowUp] ; BX <- conversion table
UpperCaseNat2:	mov	cx,[bx]		; CL <- lower, CH <- upper
		stc			; preset CY error flag
		jcxz	UpperCaseNat8	; end of table

; ------------- check characters

		cmp	al,cl		; is it lower character?
		je	short UpperCaseNat6 ; convert to upper character

; ------------- check next character

		inc	bx
		inc	bx		; BX <- increase pointer
		jmp	short UpperCaseNat2 ; check next character

; ------------- convert lower -> upper (here is NC)

UpperCaseNat6:	mov	al,ch		; AL <- upper character

; ------------- pop registers

UpperCaseNat8:	pop	cx		; pop CX
		pop	bx		; pop BX
UpperCaseNat9:	ret

; -----------------------------------------------------------------------------
;           Convert to lowercase letter with national characters
; -----------------------------------------------------------------------------
; INPUT:	AL = character
; OUTPUT:	AL = lowercase letter
;		CY = not converted (not letter)
; -----------------------------------------------------------------------------

; ------------- convert base ASCII characters

LowerCaseNat:	call	LowerCase	; convert ASCII characters
		jnc	short LowerCaseNat9 ; character converted OK

; ------------- push registers

		push	bx		; push BX
		push	cx		; push CX

; ------------- get characters from table -> CX

		mov	bx,[KeyMapLowUp] ; BX <- conversion table
LowerCaseNat2:	mov	cx,[bx]		; CL <- lower, CH <- upper
		stc			; preset CY error flag
		jcxz	LowerCaseNat8	; end of table

; ------------- check characters

		cmp	al,ch		; is it upper character?
		je	short LowerCaseNat6 ; convert to lower character

; ------------- check next character

		inc	bx
		inc	bx		; BX <- increase pointer
		jmp	short LowerCaseNat2 ; check next character

; ------------- convert upper -> lower (here is NC)

LowerCaseNat6:	mov	al,cl		; AL <- lower character

; ------------- pop registers

LowerCaseNat8:	pop	cx		; pop CX
		pop	bx		; pop BX
LowerCaseNat9:	ret

; -----------------------------------------------------------------------------
;         Convert lowercase/uppercase letter with national characters
; -----------------------------------------------------------------------------
; INPUT:	AL = character
; OUTPUT:	AL = changed letter
;		CY = not converted (not letter)
; -----------------------------------------------------------------------------

; ------------- convert base ASCII characters

UpperLowerCaseNat:
		call	UpperLowerCase	; convert ASCII characters
		jnc	short UpperLowerNat9 ; character converted OK

; ------------- push registers

		push	bx		; push BX
		push	cx		; push CX

; ------------- get characters from table -> CX

		mov	bx,[KeyMapLowUp] ; BX <- conversion table
UpperLowerNat2:	mov	cx,[bx]		; CL <- lower, CH <- upper
		stc			; preset CY error flag
		jcxz	UpperLowerNat8	; end of table

; ------------- check characters

		cmp	al,cl		; is it lower character?
		je	short UpperLowerNat4 ; convert to upper character
		cmp	al,ch		; is it upper character?
		je	short UpperLowerNat6 ; convert to lower character

; ------------- check next character

		inc	bx
		inc	bx		; BX <- increase pointer
		jmp	short UpperLowerNat2 ; check next character

; ------------- convert lower -> upper (here is NC)

UpperLowerNat4:	mov	cl,ch		; CL <- upper character

; ------------- convert upper -> lower (here is NC)

UpperLowerNat6:	mov	al,cl		; AL <- lower character

; ------------- pop registers

UpperLowerNat8:	pop	cx		; pop CX
		pop	bx		; pop BX
UpperLowerNat9:	ret

; -----------------------------------------------------------------------------
;                    Get length of ASCIIZ string
; -----------------------------------------------------------------------------
; INPUT:	SI = pointer to ASCII string (it can be NULL)
; OUTPUT:	AX = length of ASCII string (0 if SI=NULL)
; -----------------------------------------------------------------------------

; ------------- push registers

TextZLength:	push	cx		; push CX
		push	si		; push SI

; ------------- text with NULL address

		xor	cx,cx		; CX <- 0
		or	si,si		; NULL address ?
		jz	short TextZLength8 ; NULL address, length = 0

; ------------- get text length

		dec	cx		; CX <- -1
TextZLength2:	inc	cx		; CX += 1
		lodsb			; AL <- load next character
		or	al,al		; end of string?
		jnz	short TextZLength2 ; next character

; ------------- pop registers

TextZLength8:	xchg	ax,cx		; AX <- length of string
		pop	si		; pop SI
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;                    Get length of text string
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure (it can be NULL)
; OUTPUT:	AX = length of text string (0 if BX=NULL)
; -----------------------------------------------------------------------------

TextLength:	xor	ax,ax		; AX <- 0
		or	bx,bx		; is pointer valid?
		jz	short TextLength2 ; pointer is not valid
		mov	ax,[bx+TEXT_Length] ; AX <- text length
TextLength2:	ret

; -----------------------------------------------------------------------------
;                     Check if character index is valid
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT
;		SI = character index
; OUTPUT:	CY = index is not valid (it is out of valid range)
; -----------------------------------------------------------------------------

TextCheckInx:	cmp	si,[bx+TEXT_Length] ; compare character index
		cmc			; CY = invalid character index
		ret

; -----------------------------------------------------------------------------
;                     Allocate new text string
; -----------------------------------------------------------------------------
; INPUT:	CX = length of text string
; OUTPUT:	AX = pointer to TEXT structure or NULL on error
;		CY = memory error (AX = NULL)
; -----------------------------------------------------------------------------

TextNew:	mov	ax,cx		; AX <- required text length
		inc	ax		; AX += 1
		inc	ax		; AX <- size of memory block
		call	MemAlloc	; allocate memory block
		jc	short TextNew4	; memory error
		xchg	ax,bx		; BX <- block address, AX <- push BX
		mov	[bx+TEXT_Length],cx ; set length of text
		xchg	ax,bx		; AX <- block address, BX <- pop BX
TextNew4:	ret

; -----------------------------------------------------------------------------
;                     Allocate new empty text string
; -----------------------------------------------------------------------------
; OUTPUT:	AX = pointer to TEXT structure or NULL on error
;		CY = memory error (AX = NULL)
; -----------------------------------------------------------------------------

TextNewEmpty:	push	cx		; push CX
		xor	cx,cx		; CX <- 0, required length
		call	TextNew		; allocate new string
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;                     Allocate new text string from buffer
; -----------------------------------------------------------------------------
; INPUT:	CX = length of text string
;		SI = pointer to source text
; OUTPUT:	AX = pointer to TEXT structure or NULL on error
;		CY = memory error (AX = NULL)
; -----------------------------------------------------------------------------

; ------------- push registers

TextNewBuf:	push	cx		; push CX
		push	si		; push SI
		push	di		; push DI

; ------------- allocate new text string -> AX

		mov	ax,cx		; AX <- required text length
		call	TextNew		; allocate new text string
		jc	short TextNewBuf8 ; memory error

; ------------- copy text

		mov	di,ax		; DI <- address of TEXT structure
		lea	di,[di+TEXT_Text] ; DI <- start of text
		shr	cx,1		; CX <- length / 2
		rep	movsw		; copy text - words
		adc	cx,cx		; CX <- odd byte (it clears CF)
		rep	movsb		; copy rest byte

; ------------- pop registers

TextNewBuf8:	pop	di		; pop DI
		pop	si		; pop SI
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;                     Create text string from ASCIIZ text
; -----------------------------------------------------------------------------
; INPUT:	SI = pointer to ASCIIZ text (it can be NULL)
; OUTPUT:	AX = new TEXT structure or NULL on error
;		CY = memory error (AX = NULL)
; -----------------------------------------------------------------------------

TextNewZ:	push	cx		; push CX
		call	TextZLength	; get length of ASCIIZ text
		xchg	ax,cx		; CX <- length of ASCIIZ text
		call	TextNewBuf	; create text from buffer
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;                           Duplicate text string
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure (may be CTEXT)
; OUTPUT:	AX = new TEXT structure or NULL on error
;		CY = memory error (AX = NULL)
; -----------------------------------------------------------------------------

; ------------- push registers

TextDup:	push	cx		; push CX
		push	si		; push SI
		push	di		; push DI

; ------------- allocate new memory block

		mov	ax,[bx+TEXT_Length] ; AX <- length of text
		inc	ax		; AX += 1
		inc	ax		; AX <- size of memory block
		mov	cx,ax		; CX <- size of memory block
		call	MemAlloc	; allocate memory block
		jc	short TextDup8	; memory error

; ------------- copy content

		mov	di,ax		; DI <- new memory block
		mov	si,bx		; SI <- old memory block
		shr	cx,1		; CX <- length / 2
		rep	movsw		; copy content by words
		adc	cx,cx		; CX <- odd byte (it clears CF)
		rep	movsb		; copy rest 1 byte

; ------------- pop registers

TextDup8:	pop	di		; pop DI
		pop	si		; pop SI
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;                          Delete text string
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
; OUTPUT:	BX = old address or NULL on error
;		CY = invalid pointer (BX = NULL)
; -----------------------------------------------------------------------------

TextFree:	xchg	ax,bx		; AX <- text string address
		call	MemFree		; free memory block
		xchg	ax,bx		; BX <- old address or NULL
		ret

; -----------------------------------------------------------------------------
;                           Resize text string
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
;		CX = required new length of text string
; OUTPUT:	BX = old or new pointer to TEXT structure
;		CY = memory error
; -----------------------------------------------------------------------------

TextResize:	push	ax		; push AX
		mov	ax,cx		; AX <- required new length
		inc	ax		; AX += 1
		inc	ax		; AX <- size of memory block
		call	MemResize	; resize memory block
		jc	short TextResize4 ; memory error
		xchg	ax,bx		; BX <- pointer to new text
		mov	[bx+TEXT_Length],cx ; set length of text
TextResize4:	pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                    Add text string to end of text
; -----------------------------------------------------------------------------
; INPUT:	AX = pointer to second TEXT structure
;		BX = pointer to TEXT structure
; OUTPUT:	BX = old or new pointer to TEXT structure
;		CY = memory error (text not changed)
; -----------------------------------------------------------------------------

; ------------- push registers

TextAddText:	push	cx		; push CX
		push	si		; push SI
		push	di		; push DI

; ------------- resize data buffer

		mov	cx,[bx+TEXT_Length] ; CX <- current length of text
		mov	si,ax		; SI <- second text
		mov	di,cx		; DI <- old length
		add	cx,[si+TEXT_Length] ; CX <- new text length
		call	TextResize	; resize text string
		jc	short TextAddText4 ; memory error

; ------------- copy text of second string (sets NC)

		mov	cx,[si+TEXT_Length]; CX <- length of second text
		lea	di,[bx+di+TEXT_Text] ; DI <- destination text
		lea	si,[si+TEXT_Text] ; SI <- source text
		shr	cx,1		; CX <- number of WORDS
		rep	movsw		; copy text in words
		adc	cx,cx		; CX <- last byte
		rep	movsb		; copy last byte

; ------------- pop registers

TextAddText4:	pop	di		; pop DI
		pop	si		; pop SI
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;                    Add text string from buffer to end of text
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
; 		CX = length of second text
;		SI = pointer to second text
; OUTPUT:	BX = old or new pointer to TEXT structure
;		CY = memory error (text not changed)
; -----------------------------------------------------------------------------

; ------------- push registers

TextAddBuf:	push	cx		; push CX
		push	si		; push SI
		push	di		; push DI

; ------------- resize data buffer

		mov	di,cx		; DI <- length of second text
		add	cx,[bx+TEXT_Length] ; CX <- new text length
		call	TextResize	; resize text string
		jc	short TextAddBuf4 ; memory error

; ------------- copy text of second string (sets NC)

		mov	cx,di		; CX <- length of second text
		mov	di,[bx+TEXT_Length] ; DI <- new length
		sub	di,cx		; DI <- old length
		lea	di,[bx+di+TEXT_Text] ; DI <- destination text
		shr	cx,1		; CX <- number of WORDS
		rep	movsw		; copy text in words
		adc	cx,cx		; CX <- last byte
		rep	movsb		; copy last byte

; ------------- pop registers

TextAddBuf4:	pop	di		; pop DI
		pop	si		; pop SI
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;                    Add ASCIIZ text to end of text
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
;		SI = pointer to second ASCIIZ text (it can be NULL)
; OUTPUT:	BX = old or new pointer to TEXT structure
;		CY = memory error (text not changed)
; -----------------------------------------------------------------------------

TextAddTextZ:	push	cx		; push CX
		xchg	ax,cx		; CX <- push AX
		call	TextZLength	; get length of ASCIIZ text
		xchg	ax,cx		; CX <- length of text, AX <- pop AX
		call	TextAddBuf	; add text from buffer
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;                    Add character to end of text
; -----------------------------------------------------------------------------
; INPUT:	AL = character
;		BX = pointer to TEXT structure
; OUTPUT:	BX = old or new pointer to TEXT structure
;		CY = memory error (text not changed)
; -----------------------------------------------------------------------------

; ------------- push registers

TextAddChar:	push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI

; ------------- resize text string

		mov	cx,[bx+TEXT_Length] ; CX <- current lenght of text
		mov	si,cx		; SI <- old length
		inc	cx		; increase text length
		call	TextResize	; resize text string
		jc	short TextAddChar8 ; memory error

; ------------- store new character

		mov	[bx+si+TEXT_Text],al ; store new character

; ------------- pop registers

TextAddChar8:	pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;                          Get character from text
; -----------------------------------------------------------------------------
; INPUT:	AL = default character, if index is out of range
;		BX = pointer to TEXT structure
;		SI = character index (0...)
; OUTPUT:	AL = character from the text (or default character if CY)
;		CY = error, index is out of range (AL not changed)
; -----------------------------------------------------------------------------

TextGetChar:	cmp	si,[bx+TEXT_Length] ; check character offset
		jae	short TextGetChar2 ; invalid character offset
		mov	al,[bx+TEXT_Text+si] ; AL <- character
TextGetChar2:	cmc			; CY = invalid character offset
		ret

; -----------------------------------------------------------------------------
;                          Set character in text
; -----------------------------------------------------------------------------
; INPUT:	AL = character
;		BX = pointer to TEXT structure
;		SI = character index (0...) (it may be out of range)
; OUTPUT:	CY = error, index is out of range (text not changed)
; -----------------------------------------------------------------------------

TextSetChar:	cmp	si,[bx+TEXT_Length] ; check character offset
		jae	short TextSetChar2 ; invalid character offset
		mov	[bx+TEXT_Text+si],al ; set character
TextSetChar2:	cmc			; CY = invalid character offset
		ret

; -----------------------------------------------------------------------------
;                          Get first character from text
; -----------------------------------------------------------------------------
; INPUT:	AL = default character, if no text
;		BX = pointer to TEXT structure
; OUTPUT:	AL = character from the text (or default character if CY)
;		CY = error, no text (AL not changed)
; -----------------------------------------------------------------------------

TextGetFirst:	push	si		; push SI
		xor	si,si		; SI <- index of first character
		call	TextGetChar	; get character from text
		pop	si		; pop SI
		ret

; -----------------------------------------------------------------------------
;                          Get last character from text
; -----------------------------------------------------------------------------
; INPUT:	AL = default character, if no text
;		BX = pointer to TEXT structure
; OUTPUT:	AL = character from the text (or default character if CY)
;		CY = error, no text (AL not changed)
; -----------------------------------------------------------------------------

TextGetLast:	push	si		; push SI
		mov	si,[bx+TEXT_Length] ; SI <- length of text
		dec	si		; SI <- index of last character
		call	TextGetChar	; get character from text
		pop	si		; pop SI
		ret

; -----------------------------------------------------------------------------
;                      Add space character to end of text
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
; OUTPUT:	BX = old or new pointer to TEXT structure
;		CY = memory error (text not changed)
; -----------------------------------------------------------------------------

TextAddSpc:	push	ax		; push AX
		mov	al," "		; AL <- space character
		call	TextAddChar	; add space character to end of text
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                      Add minus character to end of text
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
; OUTPUT:	BX = old or new pointer to TEXT structure
;		CY = memory error (text not changed)
; -----------------------------------------------------------------------------

TextAddMinus:	push	ax		; push AX
		mov	al,"-"		; AL <- minus character
		call	TextAddChar	; add minus character to end of text
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                      Add new line CR/LF to end of text
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
; OUTPUT:	BX = old or new pointer to TEXT structure
;		CY = memory error (text not changed)
; -----------------------------------------------------------------------------

TextAddNewLine:	push	ax		; push AX
		mov	al,CR		; AL <- CR character
		call	TextAddChar	; add character to end of text
		jc	short TextAddNewLine2 ; memory error
		mov	al,LF		; AL <- LF character
		call	TextAddChar	; add character to end of text
TextAddNewLine2:pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                   Add one digit character to end of text
; -----------------------------------------------------------------------------
; INPUT:	AL = number (in range 0 to 9)
;		BX = pointer to TEXT structure
; OUTPUT:	BX = old or new pointer to TEXT structure
;		CY = memory error (text not changed)
; -----------------------------------------------------------------------------

TextAddDig:	push	ax		; push AX
		add	al,'0'		; AL <- digit character
		call	TextAddChar	; add digit to end of text
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                   Add two digit characters to end of text
; -----------------------------------------------------------------------------
; INPUT:	AL = number (in range 0 to 99)
;		BX = pointer to TEXT structure
; OUTPUT:	BX = old or new pointer to TEXT structure
;		CY = memory error (text not changed)
; -----------------------------------------------------------------------------

; ------------- push registers

TextAdd2Dig:	push	ax		; push AX
		push	cx		; push CX
		push	si		; push SI

; ------------- resize data buffer

		mov	cx,[bx+TEXT_Length] ; CX <- current lenght of text
		mov	si,cx		; SI <- old length
		inc	cx		; increate text length
		inc	cx		; increate text length
		call	TextResize	; resize text string
		jc	short TextAdd2Dig2 ; memory error

; ------------- store digit characters

		aam			; AH <- first digit, AL <- second digit
		add	ax,'00'		; AX <- digit characters
		xchg	ah,al		; AL <- first digit, AH <- second digit
		mov	[bx+si+TEXT_Text],ax ; store new characters

; ------------- pop registers

TextAdd2Dig2:	pop	si		; pop SI
		pop	cx		; pop CX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                   Add two digit characters with space to end of text
; -----------------------------------------------------------------------------
; INPUT:	AL = number (in range 0 to 99)
;		BX = pointer to TEXT structure
; OUTPUT:	BX = old or new pointer to TEXT structure
;		CY = memory error (text not changed)
; -----------------------------------------------------------------------------

; ------------- push registers

TextAdd2DigSpc:	push	ax		; push AX
		push	cx		; push CX
		push	si		; push SI

; ------------- resize data buffer

		mov	cx,[bx+TEXT_Length] ; CX <- current lenght of text
		mov	si,cx		; SI <- old length
		inc	cx		; increate text length
		inc	cx		; increate text length
		call	TextResize	; resize text string
		jc	short TextAdd2DigSpc4 ; memory error

; ------------- store digit characters

		aam			; AH <- first digit, AL <- second digit
		add	ax,'00'		; AX <- digit characters
		xchg	ah,al		; AL <- first digit, AH <- second digit
		cmp	al,"0"		; unsignificant zero?
		jne	short TextAdd2DigSpc2 ; no zero
		mov	al," "		; substitute with space character
TextAdd2DigSpc2:mov	[bx+si+TEXT_Text],ax ; store new characters

; ------------- pop registers

TextAdd2DigSpc4:pop	si		; pop SI
		pop	cx		; pop CX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                   Add unsigned byte number to end of text
; -----------------------------------------------------------------------------
; INPUT:	AL = unsigned data byte
;		BX = pointer to TEXT structure
; OUTPUT:	BX = old or new pointer to TEXT structure
;		CY = memory error (text not changed)
; -----------------------------------------------------------------------------

TextAddByte:	push	ax		; push AX
		mov	ah,0		; AX = data word
		call	TextAddWord	; add unsigned word number
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                   Add signed byte number to end of text
; -----------------------------------------------------------------------------
; INPUT:	AL = signed data byte
;		BX = pointer to TEXT structure
; OUTPUT:	BX = old or new pointer to TEXT structure
;		CY = memory error (text not changed)
; -----------------------------------------------------------------------------

TextAddSByte:	or	al,al		; negative number?
		jns	short TextAddByte ; not negative number
		push	ax		; push AX
		mov	ah,0ffh		; AX = data word
		call	TextAddSWord	; add signed word number
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                   Add signed word number to end of text
; -----------------------------------------------------------------------------
; INPUT:	AX = signed data word
;		BX = pointer to TEXT structure
; OUTPUT:	BX = old or new pointer to TEXT structure
;		CY = memory error (text not changed)
; -----------------------------------------------------------------------------

TextAddSWord:	or	ax,ax		; negative number?
		jns	short TextAddWord ; not negative number
		call	TextAddMinus	; add minus character
		push	ax		; push AX
		neg	ax		; AX <- positive value
		call	TextAddWord	; add positive number
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                   Add unsigned word number to end of text
; -----------------------------------------------------------------------------
; INPUT:	AX = unsigned data word
;		BX = pointer to TEXT structure
; OUTPUT:	BX = old or new pointer to TEXT structure
;		CY = memory error (text not changed)
; -----------------------------------------------------------------------------

; ------------- push registers

TextAddWord:	push	ax		; push AX
		push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI
		push	di		; push DI

; ------------- decode number into stack

		xor	di,di		; DI <- character counter
		mov	si,10		; SI <- 10, divider
TextAddWord2:	xor	dx,dx		; DX <- 0
		div	si		; divide one digit
		add	dl,"0"		; DL <- convert to ASCII character
		push	dx		; push digit into stack
		inc	di		; DI <- increase digit counter
		or	ax,ax		; already zero ?
		jnz	short TextAddWord2 ; decode next digit

; ------------- resize data buffer

		mov	cx,[bx+TEXT_Length] ; CX <- current lenght of text
		mov	si,cx		; SI <- old length
		add	cx,di		; CX <- new text length
		call	TextResize	; resize text string
		mov	cx,di		; CX <- number of digits
		jc	short TextAddWord9 ; memory error

; ------------- store digits into stack (returns NC)

TextAddWord4:	pop	ax		; pop digit from the stack
		mov	[bx+si+TEXT_Text],al ; store new characters
		inc	si		; increase offset of character
		loop	TextAddWord4	; next digit

; ------------- pop registers

TextAddWord8:	pop	di		; pop DI
		pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	ax		; pop AX
		ret

; ------------- destroy digits from stack on error (returns CY)

TextAddWord9:	pop	dx		; destroy digit from the stack
		loop	TextAddWord9	; next character
		jmp	short TextAddWord8

; -----------------------------------------------------------------------------
;                   Add signed dword number to end of text
; -----------------------------------------------------------------------------
; INPUT:	DX:AX = signed data dword
;		BX = pointer to TEXT structure
; OUTPUT:	BX = old or new pointer to TEXT structure
;		CY = memory error (text not changed)
; -----------------------------------------------------------------------------

TextAddSDWord:	or	dx,dx		; negative number?
		jns	short TextAddDWord ; not negative number
		call	TextAddMinus	; add minus character
		push	ax		; push AX
		push	dx		; push DX
		neg	ax		; AX <- negate number LOW
		adc	dx,byte 0	; DX <- carry
		neg	dx		; DX <- negate number HIGH
		call	TextAddDWord	; add positive number
		pop	dx		; pop DX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                   Add unsigned dword number to end of text
; -----------------------------------------------------------------------------
; INPUT:	DX:AX = unsigned data dword
;		BX = pointer to TEXT structure
; OUTPUT:	BX = old or new pointer to TEXT structure
;		CY = memory error (text not changed)
; -----------------------------------------------------------------------------

; ------------- push registers

TextAddDWord:	push	ax		; push AX
		push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI
		push	di		; push DI

; ------------- decode number into stack

		xor	di,di		; DI <- character counter
		mov	si,10		; SI <- 10, divider
TextAddDWord2: 	xchg	ax,cx		; CX <- number LOW
		xchg	ax,dx		; AX <- number high
		xor	dx,dx		; DX <- 0
		div	si		; divide / 10
		xchg	ax,cx		; CX <- new high, AX <- low
		div	si		; divide / 10
		add	dl,"0"		; DL <- convert to ASCII character
		push	dx		; push digit into stack
		inc	di		; DI <- increase digit counter
		mov	dx,cx		; DX <- new high
		or	cx,ax		; check zero result
		jnz	short TextAddDWord2 ; decode next digit

; ------------- resize data buffer

		mov	cx,[bx+TEXT_Length] ; CX <- current lenght of text
		mov	si,cx		; SI <- old length
		add	cx,di		; CX <- new text length
		call	TextResize	; resize text string
		mov	cx,di		; CX <- number of digits
		jc	short TextAddDWord9 ; memory error

; ------------- store digits into stack (returns NC)

TextAddDWord4:	pop	ax		; pop digit from the stack
		mov	[bx+si+TEXT_Text],al ; store new characters
		inc	si		; increase offset of character
		loop	TextAddDWord4	; next digit

; ------------- pop registers

TextAddDWord8:	pop	di		; pop DI
		pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	ax		; pop AX
		ret

; ------------- destroy digits from stack on error (returns CY)

TextAddDWord9:	pop	dx		; destroy digit from the stack
		loop	TextAddDWord9	; next character
		jmp	short TextAddDWord8

; -----------------------------------------------------------------------------
;                        Add HEW dword to end of text
; -----------------------------------------------------------------------------
; INPUT:	DX:AX = data dword
;		BX = pointer to TEXT structure
; OUTPUT:	BX = old or new pointer to TEXT structure
;		CY = memory error
; -----------------------------------------------------------------------------

TextAddHDWord:	xchg	ax,dx		; AX <- high word, DX <- low word
		call	TextAddHWord	; add high word
		xchg	ax,dx		; AX <- low word, DX <- high word
		jc	short TextAddHByte8 ; memory error

; --- TextAddHWord must follow!

; -----------------------------------------------------------------------------
;                        Add HEX word to end of text
; -----------------------------------------------------------------------------
; INPUT:	AX = data word
;		BX = pointer to TEXT structure
; OUTPUT:	BX = old or new pointer to TEXT structure
;		CY = memory error
; -----------------------------------------------------------------------------

TextAddHWord:	xchg	al,ah		; AL <- high byte, AH <- low byte
		call	TextAddHByte	; add high byte
		xchg	al,ah		; AL <- low byte, AH <- high byte
		jc	short TextAddHByte8 ; memory error

; --- TextAddHByte must follow!

; -----------------------------------------------------------------------------
;                        Add HEX byte to end of text
; -----------------------------------------------------------------------------
; INPUT:	AL = data byte
;		BX = pointer to TEXT structure
; OUTPUT:	BX = old or new pointer to TEXT structure
;		CY = memory error (text not changed)
; -----------------------------------------------------------------------------

; alternate method:
;		add	al,90h
;		daa
;		adc	al,40h
;		daa

; ------------- push registers

TextAddHByte:	push	ax		; push AX
		push	cx		; push CX
		push	si		; push SI

; ------------- resize text

		mov	cx,[bx+TEXT_Length] ; CX <- current lenght of text
		mov	si,cx		; SI <- old length
		add	cx,byte 2	; CX <- new text length
		call	TextResize	; resize text string
		jc	short TextAddHByte6 ; memory error

; ------------- high nibble (first character)

		mov	cl,al		; AL <- save data byte
		shr	al,1
		shr	al,1
		shr	al,1
		shr	al,1		; AL <- high nibble
		cmp	al,10		; hex character A..F ?
		sbb	al,69h
		das			; AL <- hex digit
		xchg	ax,cx		; CL <- first digit, AL <- data byte

; ------------- low nibble (second character)

		and	al,0fh		; AL <- low nibble
		cmp	al,10		; hex character A..F ?
		sbb	al,69h
		das			; AL <- hex digit

; ------------- store characters

		mov	ah,al		; AH <- second character
		mov	al,cl		; AL <- first character
		mov	[bx+si+TEXT_Text],ax ; store new characters
		clc			; clear error flag

; ------------- pop registers

TextAddHByte6:	pop	si		; pop SI
		pop	cx		; pop CX
		pop	ax		; pop AX
TextAddHByte8:	ret

; -----------------------------------------------------------------------------
;                        Add BIN dword to end of text
; -----------------------------------------------------------------------------
; INPUT:	DX:AX = data dword
;		BX = pointer to TEXT structure
; OUTPUT:	BX = old or new pointer to TEXT structure
;		CY = memory error
; -----------------------------------------------------------------------------

TextAddBDWord:	xchg	ax,dx		; AX <- high word, DX <- low word
		call	TextAddBWord	; add high word
		xchg	ax,dx		; AX <- low word, DX <- high word
		jc	short TextAddBByte8 ; memory error

; --- TextAddBWord must follow!

; -----------------------------------------------------------------------------
;                        Add BIN word to end of text
; -----------------------------------------------------------------------------
; INPUT:	AX = data word
;		BX = pointer to TEXT structure
; OUTPUT:	BX = old or new pointer to TEXT structure
;		CY = memory error
; -----------------------------------------------------------------------------

TextAddBWord:	xchg	al,ah		; AL <- high byte, AH <- low byte
		call	TextAddBByte	; add high byte
		xchg	al,ah		; AL <- low byte, AH <- high byte
		jc	short TextAddBByte8 ; memory error

; --- TextAddBByte must follow!

; -----------------------------------------------------------------------------
;                        Add BIN byte to end of text
; -----------------------------------------------------------------------------
; INPUT:	AL = data byte
;		BX = pointer to TEXT structure
; OUTPUT:	BX = old or new pointer to TEXT structure
;		CY = memory error (text not changed)
; -----------------------------------------------------------------------------

; ------------- push registers

TextAddBByte:	push	ax		; push AX
		push	cx		; push CX
		push	si		; push SI

; ------------- resize text

		mov	cx,[bx+TEXT_Length] ; CX <- current lenght of text
		mov	si,cx		; SI <- old length
		add	cx,byte 8	; CX <- new text length
		call	TextResize	; resize text string
		jc	short TextAddBByte6 ; memory error

; ------------- decide bits (clear error flag NC)

		mov	ah,al		; AH <- data byte
		mov	cx,8		; CX <- number of bits
TextAddBByte2:	shl	ah,1		; check highest bit
		mov	al,"0"		; AL <- bit "0"
		adc	al,0		; change to bit "1"
		mov	[bx+si+TEXT_Text],al ; store new character
		inc	si		; increase number of characters
		loop	TextAddBByte2	; dispaly next bit

; ------------- pop registers

TextAddBByte6:	pop	si		; pop SI
		pop	cx		; pop CX
		pop	ax		; pop AX
TextAddBByte8:	ret

; -----------------------------------------------------------------------------
;                            Delete part of text
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
;		CX = length of text to delete
;		SI = offset of text to delete
; OUTPUT:	BX = old or new pointer to TEXT structure
; NOTES:	It limits length and offset to a valid range.
; -----------------------------------------------------------------------------

; ------------- push registers

TextDel:	push	ax		; push AX
		push	cx		; push CX
		push	si		; push SI
		push	di		; push DI

; ------------- check maximal start offset SI

		mov	ax,[bx+TEXT_Length] ; AX <- text length
		sub	ax,si		; AX <- remaining characters
		jbe	short TextDel8	; nothing to delete

; ------------- limit size of deleted data -> AX, remaining data -> CX

		cmp	ax,cx		; check length of text
		ja	short TextDel2	; text length is OK
		mov	cx,ax		; CX <- limit text length
TextDel2:	jcxz	TextDel8	; no data to delete
		sub	ax,cx		; AX <- remaining data
		xchg	ax,cx		; AX <- deleted size, CX <- remaining
		jz	short TextDel4	; no remaining data

; ------------- delete text

		lea	di,[bx+TEXT_Text+si] ; DI <- destination address
		mov	si,di		; SI <- destination address
		add	si,ax		; SI <- start of remainging text
		shr	cx,1		; CX <- number of WORDS
		rep	movsw		; copy text in words
		adc	cx,cx		; CX <- last byte
		rep	movsb		; copy last byte

; ------------- resize buffer

TextDel4:	mov	cx,[bx+TEXT_Length] ; CX <- length of text
		sub	cx,ax		; CX <- new length of text
		call	TextResize	; resize buffer
		mov	[bx+TEXT_Length],cx ; set new length (in case of errr)

; ------------- pop registers

TextDel8:	pop	di		; pop DI
		pop	si		; pop SI
		pop	cx		; pop CX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                         Delete start of text
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
;		CX = length of text to delete
; OUTPUT:	BX = old or new pointer to TEXT structure
; NOTES:	It limits length to a valid range.
; -----------------------------------------------------------------------------

TextDelStart:	push	si		; push SI
		xor	si,si		; SI <- 0, start position
		call	TextDel		; delete start of text
		pop	si		; pop SI
		ret

; -----------------------------------------------------------------------------
;                    Delete rest of text from given position
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
;		SI = offset of text to delete
; OUTPUT:	BX = old or new pointer to TEXT structure
; NOTES:	It limits offset to a valid range.
; -----------------------------------------------------------------------------

TextDelFrom:	push	cx		; push CX
		mov	cx,0ffffh	; CX <- number of characters to delete
		call	TextDel		; delete characters from text
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;                             Delete end of text
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
;		CX = length of text to delete
; OUTPUT:	BX = old or new pointer to TEXT structure
; NOTES:	It limits length to a valid range.
; -----------------------------------------------------------------------------

TextDelEnd:	push	si		; push SI
		mov	si,[bx+TEXT_Length] ; SI <- text length
		sub	si,cx		; SI <- start position
		jnc	short TextDelEnd4 ; position is OK
		xor	si,si		; SI <- 0, limit position
TextDelEnd4:	call	TextDel		; delete characters from text
		pop	si		; pop SI
		ret

; -----------------------------------------------------------------------------
;                        Delete one character from text
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
;		SI = index of character to delete
; OUTPUT:	BX = old or new pointer to TEXT structure
; NOTES:	It checks validity of character position.
; -----------------------------------------------------------------------------

TextDelChar:	push	cx		; push CX
		mov	cx,1		; CX <- lengt of 1 character
		call	TextDel		; delete character
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;                          Get middle part of text
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
;		CX = length of part of text
;		SI = start position of part of text
; OUTPUT:	AX = new TEXT structure or NULL on error
;		CY = memory error (AX = NULL)
; NOTES:	It limits length and position to a valid range.
; -----------------------------------------------------------------------------

; ------------- push registers

TextMid:	push	cx		; push CX
		push	si		; push SI
		push	di		; push DI

; ------------- limit start index -> SI

		mov	ax,[bx+TEXT_Length] ; AX <- text length
		cmp	si,ax		; check start offset
		jb	short TextMid2	; offset is OK
		mov	si,ax		; SI <- limit start offset

; ------------- limit length of text -> CX

TextMid2:	sub	ax,si		; AX <- remaining size
		cmp	ax,cx		; check length of text
		ja	short TextMid4	; text length is OK
		mov	cx,ax		; CX <- limit text length

; ------------- allocate new text string

TextMid4:	call	TextNew		; allocate new text string
		jc	short TextMid8	; memory error

; ------------- copy text (it clears CF)

		mov	di,ax		; DI <- new text string
		lea	di,[di+TEXT_Text] ; DI <- start of new text
		lea	si,[bx+TEXT_Text+si] ; SI <- start of part of text
		shr	cx,1		; CX <- length / 2
		rep	movsw		; copy content by words
		adc	cx,cx		; CX <- odd byte (it clears CF)
		rep	movsb		; copy rest 1 byte

; ------------- pop registers

TextMid8:	pop	di		; pop DI
		pop	si		; pop SI
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;                            Get left part of text
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
;		CX = length of left part of text
; OUTPUT:	AX = new TEXT structure or NULL on error
;		CY = memory error (AX = NULL)
; NOTES:	It limits length to a valid range.
; -----------------------------------------------------------------------------

TextLeft:	push	si		; push SI
		xor	si,si		; SI <- start position
		call	TextMid		; get middle part of text
		pop	si		; pop SI
		ret

; -----------------------------------------------------------------------------
;                  Get right part of text from given position
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
;		SI = start position of right part
; OUTPUT:	AX = new TEXT structure or NULL on error
;		CY = memory error (AX = NULL)
; NOTES:	It limits position to a valid range.
; -----------------------------------------------------------------------------

TextFrom:	push	cx		; push CX
		mov	cx,0ffffh	; CX <- maximum length
		call	TextMid		; get middle part of text
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;                            Get right part of text
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
;		CX = length of right part of text
; OUTPUT:	AX = new TEXT structure or NULL on error
;		CY = memory error (AX = NULL)
; NOTES:	It limits length to a valid range.
; -----------------------------------------------------------------------------

TextRight:	push	si		; push SI
		mov	si,[bx+TEXT_Length] ; SI <- length of text
		sub	si,cx		; SI <- start of text
		jnc	short TextRight2 ; position is OK
		xor	si,si		; SI <- 0, limit position
TextRight2:	call	TextMid		; get middle part of text
		pop	si		; pop SI
		ret

; -----------------------------------------------------------------------------
;              Convert text to uppercase letters (only a..z are affected)
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
; NOTES:	It does not convert national characters.
; -----------------------------------------------------------------------------

; ------------- push registers

TextUpper:	push	ax		; push AX
		push	si		; push SI
		push	cx		; push CX

; ------------- prepare registers

		mov	cx,[bx+TEXT_Length] ; CX <- length of text
		jcxz	TextUpper6	; no text
		lea	si,[bx+TEXT_Text] ; SI <- start of text

; ------------- convert string

TextUpper2:	lodsb			; AL <- load character
		call	UpperCase	; convert to uppercase
;		jc	short TextUpper4 ; not converted
		mov	[si-1],al	; save new character
TextUpper4:	loop	TextUpper2	; next character

; ------------- pop registers

TextUpper6:	pop	cx		; pop CX
		pop	si		; pop SI
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;        Convert text to uppercase letters with national characters
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
; -----------------------------------------------------------------------------

; ------------- push registers

TextUpperNat:	push	ax		; push AX
		push	si		; push SI
		push	cx		; push CX

; ------------- prepare registers

		mov	cx,[bx+TEXT_Length] ; CX <- length of text
		jcxz	TextUpperNat6	; no text
		lea	si,[bx+TEXT_Text] ; SI <- start of text

; ------------- convert string

TextUpperNat2:	lodsb			; AL <- load character
		call	UpperCaseNat	; convert to uppercase
;		jc	short TextUpperNat4 ; not converted
		mov	[si-1],al	; save new character
TextUpperNat4:	loop	TextUpperNat2	; next character

; ------------- pop registers

TextUpperNat6:	pop	cx		; pop CX
		pop	si		; pop SI
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;              Convert text to lowercase letters (only A..Z are affected)
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
; NOTES:	It does not convert national characters.
; -----------------------------------------------------------------------------

; ------------- push registers

TextLower:	push	ax		; push AX
		push	si		; push SI
		push	cx		; push CX

; ------------- prepare registers

		mov	cx,[bx+TEXT_Length] ; CX <- length of text
		jcxz	TextLower6	; no text
		lea	si,[bx+TEXT_Text] ; SI <- start of text

; ------------- convert string

TextLower2:	lodsb			; AL <- load character
		call	LowerCase	; convert to lowercase
;		jc	short TextLower4 ; not converted
		mov	[si-1],al	; save new character
TextLower4:	loop	TextLower2	; next character

; ------------- pop registers

TextLower6:	pop	cx		; pop CX
		pop	si		; pop SI
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;          Convert text to lowercase letters with national characters
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
; -----------------------------------------------------------------------------

; ------------- push registers

TextLowerNat:	push	ax		; push AX
		push	si		; push SI
		push	cx		; push CX

; ------------- prepare registers

		mov	cx,[bx+TEXT_Length] ; CX <- length of text
		jcxz	TextLowerNat6	; no text
		lea	si,[bx+TEXT_Text] ; SI <- start of text

; ------------- convert string

TextLowerNat2:	lodsb			; AL <- load character
		call	LowerCaseNat	; convert to lowercase
;		jc	short TextLowerNat4 ; not converted
		mov	[si-1],al	; save new character
TextLowerNat4:	loop	TextLowerNat2	; next character

; ------------- pop registers

TextLowerNat6:	pop	cx		; pop CX
		pop	si		; pop SI
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;      Change uppercase/lowercase letters (only a..z, A..Z are affected)
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
; NOTES:	It does not convert national characters.
;		It changes lowercase letters to uppercase and contrariwise.
; -----------------------------------------------------------------------------

; ------------- push registers

TextUpLow:	push	ax		; push AX
		push	si		; push SI
		push	cx		; push CX

; ------------- prepare registers

		mov	cx,[bx+TEXT_Length] ; CX <- length of text
		jcxz	TextUpLow8	; no text
		lea	si,[bx+TEXT_Text] ; SI <- start of text

; ------------- convert string

TextUpLow2:	lodsb			; AL <- load character
		call	IsAlpha		; check alpha character
		jc	short TextUpLow4 ; not alpha character
		xor	byte [si-1],"a"^"A" ; invert letter
TextUpLow4:	loop	TextUpLow2	; next character

; ------------- pop registers

TextUpLow8:	pop	cx		; pop CX
		pop	si		; pop SI
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;      Change uppercase/lowercase letters with national characters
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
; NOTES:	It changes lowercase letters to uppercase and contrariwise,
; -----------------------------------------------------------------------------

; ------------- push registers

TextUpLowNat:	push	ax		; push AX
		push	si		; push SI
		push	cx		; push CX

; ------------- prepare registers

		mov	cx,[bx+TEXT_Length] ; CX <- length of text
		jcxz	TextUpLowNat8	; no text
		lea	si,[bx+TEXT_Text] ; SI <- start of text

; ------------- convert string

TextUpLowNat2:	lodsb			; AL <- load character
		call	UpperLowerCaseNat ; convert to lowercase/uppercase
;		jc	short TextLowerNat4 ; not converted
		mov	[si-1],al	; save new character
TextUpLowNat4:	loop	TextUpLowNat2	; next character

; ------------- pop registers

TextUpLowNat8:	pop	cx		; pop CX
		pop	si		; pop SI
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;               Capitalize words (only a..z, A..Z are affected)
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
; NOTES:	It does not convert national characters.
;		It changes first letter of words to uppercase, other lowercase.
; -----------------------------------------------------------------------------

; ------------- push registers

TextCapital:	push	ax		; push AX
		push	si		; push SI
		push	cx		; push CX
		push	dx		; push DX

; ------------- prepare registers

		mov	cx,[bx+TEXT_Length] ; CX <- length of text
		jcxz	TextCapital8	; no text
		lea	si,[bx+TEXT_Text] ; SI <- start of text

; ------------- convert string

		mov	dh,0ffh		; DH <- 0ffh, flag of begin of word
TextCapital2:	lodsb			; AL <- load character
		call	IsAlpha		; check alpha character
		sbb	dl,dl		; DL <- 0 if alpha, 0ffh otherwise
		jnz	short TextCapital6 ; not alpha character
		and	dh,20h		; DH <- ~bit of lower/upper alpha
		or	al,20h		; set delete old lower/upper flag
		xor	al,dh		; AL <- correct lower/upper character
		mov	[si-1],al	; store new character
TextCapital6:	mov	dh,dl		; DH <- save flag of alpha character
		loop	TextCapital2	; next character

; ------------- pop registers

TextCapital8:	pop	dx		; pop DX
		pop	cx		; pop CX
		pop	si		; pop SI
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                    Compare text strings to equality
; -----------------------------------------------------------------------------
; INPUT:	AX = pointer to second TEXT structure
;		BX = pointer to first TEXT structure
; OUTPUT:	CY = strings are not equal
; NOTES:	Comparison is case sensitive.
; -----------------------------------------------------------------------------

; ------------- push registers

TextEqu:	push	cx		; push CX
		push	si		; push SI
		push	di		; push DI

; ------------- identical strings

		cmp	ax,bx		; compare pointers
		je	short TextEqu8	; identical strings

; ------------- compare length of string

		mov	di,ax		; DI <- secont text
		mov	cx,[bx+TEXT_Length] ; CX <- length of first text
		cmp	cx,[di+TEXT_Length] ; compare length
		jne	short TextEqu6	; different text length

; ------------- compare strings

		jcxz	TextEqu8	; zero length strings
		lea	si,[bx+TEXT_Text] ; SI <- first text
		lea	di,[di+TEXT_Text] ; DI <- second text
		shr	cx,1		; CX <- length in words
		repe	cmpsw		; compare strings in words
		jne	short TextEqu6	; string are not equal
		adc	cx,cx		; CX <- even byte
		repe	cmpsb		; compare even byte
		je	short TextEqu8	; string are equal

; ------------- pop registers

TextEqu6:	stc			; set error flag
TextEqu8:	pop	di		; pop DI
		pop	si		; pop SI
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;                    Compare text strings alphabetically
; -----------------------------------------------------------------------------
; INPUT:	AX = pointer to second TEXT structure
;		BX = pointer to first TEXT structure
; OUTPUT:	AX = first string is 1:greater (later), -1:smaller, 0:equal.
;		flags SF,ZF contains result (as "or ax,ax")
; NOTES:	Comparison is not case sensitive (not national characters).
; -----------------------------------------------------------------------------

; ------------- push registers

TextComp:	push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI
		push	di		; push DI

; ------------- identical strings

		cmp	ax,bx		; compare pointers
		je	short TextComp7	; identical strings

; ------------- prepare length of strings -> CX first, DX second

		mov	di,ax		; DI <- second text
		mov	cx,[bx+TEXT_Length] ; CX <- length of first text
		mov	dx,[di+TEXT_Length] ; DX <- length of second text

; ------------- prepare text pointers -> SI first, DI second

		lea	si,[bx+TEXT_Text] ; SI <- firt text
		lea	di,[di+TEXT_Text] ; DI <- second text

; ------------- check end of strings

TextComp2:	cmp	cx,dx		; identical length?
		je	short TextComp3	; identical length
		jcxz	TextComp5	; shorter first text is smaller
		or	dx,dx		; check second text
		jz	short TextComp6	; longer first text is greater
TextComp3:	jcxz	TextComp7	; strings are equal
		dec	cx		; CX <- count first counter
		dec	dx		; DX <- count second counter

; ------------- get character of second string -> AH

		mov	al,[di]		; AL <- load character of second string
		inc	di		; DI <- increase pointer
		call	UpperCase	; convert to uppercase letter
		mov	ah,al		; AH <- character of second string

; ------------- get character of first string -> AL

		lodsb			; AL <- load character of first string
		call	UpperCase	; convert to uppercase letter

; ------------- compare strings

		cmp	al,ah		; compare first and second string
		ja	short TextComp6	; string1 > string2
		je	short TextComp2	; equal, continue with next character

; ------------- string1 < string2

TextComp5:	xor	ax,ax		; AX <- 0
		dec	ax		; AX <- -1
		jmp	short TextComp8

; ------------- string1 > string2

TextComp6:	xor	ax,ax		; AX <- 0
		inc	ax		; AX <- 1
		jmp	short TextComp8

; ------------- strings are equal

TextComp7	xor	ax,ax		; AX <- 0

; ------------- pop registers

TextComp8:	pop	di		; pop DI
		pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		or	ax,ax		; test result
		ret

; -----------------------------------------------------------------------------
;   Trim text from the left and right (delete spaces and control characters)
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
; OUTPUT:	CY = memory error (text may be changed)
; -----------------------------------------------------------------------------

TextTrim:	call	TextTrimRight	; trim text from the right

; === TextTrimLeft must follow !

; -----------------------------------------------------------------------------
;   Trim text from the left (delete spaces and control characters from start)
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
; -----------------------------------------------------------------------------

; ------------- push registers

TextTrimLeft:	push	cx		; push CX
		push	si		; push SI

; ------------- prepare registers

		mov	cx,[bx+TEXT_Length] ; CX <- text length
		jcxz	TextTrimLeft8	; nothing to delete
		lea	si,[bx+TEXT_Text] ; SI <- start of text

; ------------- find first valid character

TextTrimLeft2:	cmp	byte [si],32	; check one byte
		ja	short TextTrimLeft4 ; it is valid character
		inc	si		; increase text pointer
		loop	TextTrimLeft2	; next byte
TextTrimLeft4:	mov	si,cx		; SI <- remaining bytes

; ------------- delete characters

		mov	cx,[bx+TEXT_Length] ; CX <- text length
		sub	cx,si		; CX <- number of bytes to delete
		jcxz	TextTrimLeft8	; nothing to delete
		call	TextDelStart	; delete start of text

; ------------- pop registers

TextTrimLeft8:	pop	si		; pop SI
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;   Trim text from the right (delete spaces and control characters from end)
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
; -----------------------------------------------------------------------------

; ------------- push registers

TextTrimRight:	push	cx		; push CX
		push	si		; push SI

; ------------- prepare registers

		mov	cx,[bx+TEXT_Length] ; CX <- text length
		jcxz	TextTrimRight8	; nothing to delete
		mov	si,cx		; SI <- text length
		lea	si,[bx+TEXT_Text+si] ; SI <- end of text

; ------------- find first valid character

TextTrimRight2:	dec	si		; decrease text pointer
		cmp	byte [si],32	; check one byte
		ja	short TextTrimRight4 ; it is valid character
		loop	TextTrimRight2	; next byte
TextTrimRight4:	mov	si,cx		; SI <- remaining bytes

; ------------- delete characters

		mov	cx,[bx+TEXT_Length] ; CX <- text length
		sub	cx,si		; CX <- number of bytes to delete
		jcxz	TextTrimRight8	; nothing to delete
		call	TextDelEnd	; delete end of text

; ------------- pop registers

TextTrimRight8:	pop	si		; pop SI
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;               Trim spaces and control characters from the text
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
; -----------------------------------------------------------------------------

; ------------- push registers

TextTrimMid:	push	ax		; push AX
		push	cx		; push CX
		push	si		; push SI
		push	di		; push DI

; ------------- prepare registers

		mov	cx,[bx+TEXT_Length] ; CX <- text length
		jcxz	TextTrimMid8	; nothing to delete
		lea	si,[bx+TEXT_Text] ; SI <- source pointer
		mov	di,si		; DI <- destination pointer

; ------------- trim spaces and control characters

TextTrimMid2:	lodsb			; AL <- load one character
		cmp	al,33		; check one character
		stosb			; store character
		sbb	di,byte 0	; return pointer if invalid character
		loop	TextTrimMid2	; next byte

; ------------- resize data buffer

		cmp	si,di		; text changed?
		je	short TextTrimMid8 ; text not changed
		lea	ax,[bx+TEXT_Text] ; AX <- start of text
		sub	di,ax		; DI <- new length
		mov	cx,di		; CX <- new length
		call	TextResize	; resize data buffer
		mov	[bx+TEXT_Length],cx ; set new length (in case of errr)

; ------------- pop registers

TextTrimMid8:	pop	di		; pop DI
		pop	si		; pop SI
		pop	cx		; pop CX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                   Prepare bitmap of character list
; -----------------------------------------------------------------------------
; INPUT:	AX = pointer to TEXT with list of characters
;		DI = pointer to 32-byte array in the stack
;		BP = 0 (forbidden characters) or 0ffffh (allowed characters)
; -----------------------------------------------------------------------------

; ------------- push registers

TextPrepMap:	push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI
		push	di		; push DI
		mov	bx,di		; BX <- 32-byte array
		mov	si,ax		; SI <- list of characters

; ------------- clear bitmap array

		mov	cx,32/2		; CX <- size of array/2
		mov	ax,bp		; AX <- 0 or 0ffffh
		rep	stosw		; clear bitmap array

; ------------- prepare list of characters

		mov	dx,[si+TEXT_Length] ; DX <- length of list
		lea	si,[si+TEXT_Text] ; SI <- start of list

; ------------- initialize bitmap array

TextPrepMap2:	lodsb			; AL <- load one character
		mov	ah,0		; AX = character
		mov	cl,3		; CL <- number of shifts
		shr	ax,cl		; AX = offset of byte in mask
		add	ax,bx		; AX <- address in bitmap array
		xchg	ax,di		; DI <- address in bitmap array
		mov	al,[si-1]	; AL <- character
		and	al,7		; AL <- offset in byte
		xchg	ax,cx		; CL <- offset in byte
		mov	al,B0		; AL <- bit 0
		shl	al,cl		; AL <- bit mask
		or	[di],al		; set bit
		and	ax,bp		; clear bit if forbidden chars
		xor	[di],al		; clear bit if forbidden chars
		dec	dx		; list counter
		jnz	short TextPrepMap2 ; prepare next character

; ------------- pop registers

		pop	di		; pop DI
		pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX
		ret

; ------------- macro: check one character from bitmap
; %1: inc or dec to shift SI register
; AX,DX,DI = destroy, SI = source pointer, SP=pointer to bitmap
; output: NZ = bit is set

%macro		TEXTCHECKMAP 1

		mov	al,[si]		; AL <- load one character
		mov	dx,cx		; DX <- push CX
		mov	ah,0		; AX = character
		mov	cl,3		; CL <- number of shifts
		shr	ax,cl		; AX = offset of byte in mask
		add	ax,sp		; AX <- address in bitmap array
		xchg	ax,di		; DI <- address in bitmap array
		mov	al,[si]		; AL <- character
		and	al,7		; AL <- offset in byte
		xchg	ax,cx		; CL <- offset in byte
		mov	al,B0		; AL <- bit 0
		shl	al,cl		; AL <- bit mask
		%1	si		; shift SI register
		test	[di],al		; test bit
		mov	cx,dx		; CX <- pop CX
%endmacro

; -----------------------------------------------------------------------------
;                  Trim text using list of allowed characters
; -----------------------------------------------------------------------------
; INPUT:	AX = pointer to TEXT with list of allowed characters
;		BX = pointer to TEXT structure
; -----------------------------------------------------------------------------

; ------------- push registers

TextTrimUnlist:	push	bp		; push BP
		xor	bp,bp		; BP <- 0
		dec	bp		; BP <- 0ffffh, flag - allowed chars
		jmp	short TextTrimList1

; -----------------------------------------------------------------------------
;                  Trim text using list of forbidden characters
; -----------------------------------------------------------------------------
; INPUT:	AX = pointer to TEXT with list of forbidden characters
;		BX = pointer to TEXT structure
; -----------------------------------------------------------------------------

; ------------- push registers

TextTrimList:	push	bp		; push BP
		xor	bp,bp		; BP <- 0, flag - forbidden chars
TextTrimList1:	push	ax		; push AX
		push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI
		push	di		; push DI
		sub	sp,32		; local variable - 32-byte array

; ------------- prepare bitmap of character list

		mov	di,sp		; DI <- bitmap in stack
		call	TextPrepMap	; prepare bitmap

; ------------- prepare registers

		mov	cx,[bx+TEXT_Length] ; CX <- text length
		jcxz	TextTrimList8	; nothing to delete
		lea	si,[bx+TEXT_Text] ; SI <- source pointer
		mov	di,si		; DI <- destination pointer

; ------------- load and check one character

TextTrimList4:	mov	bp,di		; BP <- push DI
		TEXTCHECKMAP inc	; check character
		mov	di,bp		; DI <- pop DI
		jnz	short TextTrimList6; forbidden character has been found

; ------------- store character

		mov	al,[si-1]	; AL <- character
		stosb			; store character
TextTrimList6:	loop	TextTrimList4	; next byte

; ------------- resize data buffer

		cmp	si,di		; text changed?
		je	short TextTrimList8 ; text not changed
		lea	ax,[bx+TEXT_Text] ; AX <- start of text
		sub	di,ax		; DI <- new length
		mov	cx,di		; CX <- new length
		call	TextResize	; resize data buffer
		mov	[bx+TEXT_Length],cx ; set new length (in case of errr)

; ------------- pop registers

TextTrimList8:  add	sp,32		; return SP
		pop	di		; pop DI
		pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	ax		; pop AX
		pop	bp		; pop BP
		ret

; -----------------------------------------------------------------------------
;              Find first/next character in text in forward direction
; -----------------------------------------------------------------------------
; INPUT:	AL = character to find
;		BX = pointer to TEXT structure
;		SI = start offset (TextFindChFirst sets SI to 0)
; OUTPUT:	SI = offset of found character (or SI = -1 if not found)
;		CY = character not found (SI = -1)
; -----------------------------------------------------------------------------

; ------------- find first occurrence of character

TextFindChFirst:xor	si,si		; SI <- 0, find first occurrence

; ------------- push registers

TextFindChNext:	push	cx		; push CX
		push	di		; push DI

; ------------- get text length -> CX and text address -> DI

		mov	cx,[bx+TEXT_Length] ; CX <- text length
		lea	di,[bx+TEXT_Text+si] ; DI <- start pointer

; ------------- find data

		sub	cx,si		; CX <- remaining data
		jbe	short TextFindChErr ; data not found
		repne	scasb		; find data
		jne	short TextFindChErr ; data not found

; ------------- get offset of data

		inc	cx		; CX <- return found data
		mov	si,[bx+TEXT_Length] ; SI <- text length
		sub	si,cx		; SI <- data offset (it sets NC)

; ------------- OK: pop registers (here is NC)

		pop	di		; pop DI
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;              Find last/previous character in text in reverse direction
; -----------------------------------------------------------------------------
; INPUT:	AL = character to find
;		BX = pointer to TEXT structure
;		SI = start offset (TextFindChLast sets SI to 0ffffh)
; OUTPUT:	SI = offset of found character (or SI = -1 if not found)
;		CY = character not found (SI = -1)
; -----------------------------------------------------------------------------

; ------------- find last occurrence of character

TextFindChLast:	xor	si,si		; SI <- 0
		dec	si		; SI <- 0ffffh, find last occurrence

; ------------- push registers

TextFindChPrev:	push	cx		; push CX
		push	di		; push DI

; ------------- get text length -> CX

		mov	cx,[bx+TEXT_Length] ; CX <- text length
		jcxz	TextFindChErr	; no text

; ------------- limit maximal offset -> SI

		cmp	si,cx		; check maximal offset
		jb	short TextFindChPrev2 ; offset is OK
		mov	si,cx		; SI <- text length
		dec	si		; SI <- offset of last byte

; ------------- get text address -> DI

TextFindChPrev2:lea	di,[bx+TEXT_Text+si] ; DI <- start pointer

; ------------- find data

		mov	cx,si		; CX <- remaining data
		inc	cx		; CX <- number of bytes
		std			; set direction down
		repne	scasb		; find data
		cld			; set direction up
		jne	short TextFindChErr ; data not found

; ------------- get offset of data (here is NC)

		mov	si,cx		; SI <- remaining bytes

; ------------- OK: pop registers (here is NC)

		pop	di		; pop DI
		pop	cx		; pop CX
		ret

; ------------- data not found (here jumps from other functions, too)

TextFindChErr:	xor	si,si		; SI <- 0
		dec	si		; SI <- -1, data not found
		stc			; set error flag
		
; ------------- ERROR: pop registers

		pop	di		; pop DI
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;           Find first/next two characters in text in forward direction
; -----------------------------------------------------------------------------
; INPUT:	AL = first character to find
;		AH = second character to find
;		BX = pointer to TEXT structure
;		SI = start offset (TextFind2ChFirst sets SI to 0)
; OUTPUT:	SI = offset of found characters (or SI = -1 if not found)
;		CY = characters not found (SI = -1)
; -----------------------------------------------------------------------------

; ------------- find first occurrence of 2 characters

TextFind2ChFirst:
		xor	si,si		; SI <- 0, find first occurrence

; ------------- push registers

TextFind2ChNext:push	cx		; push CX
		push	di		; push DI

; ------------- get text length -> CX and text address -> DI

		mov	cx,[bx+TEXT_Length] ; CX <- text length
		lea	di,[bx+TEXT_Text+si] ; DI <- start pointer
		jcxz	TextFindChErr	; no text
		dec	cx		; CX -= 1, without last byte
		sub	cx,si		; CX <- remaining data
		jbe	short TextFindChErr ; data not found

; ------------- find data

TextFind2ChNxt2:repne	scasb		; find data
		jne	short TextFindChErr ; data not found
		cmp	ah,[di]		; check second character
		jne	short TextFind2ChNxt2 ; continue searching

; ------------- get offset of data

		inc	cx		; CX <- return found data
		inc	cx		; CX <- return found data
TextFind2ChOK:	mov	si,[bx+TEXT_Length] ; SI <- text length
		sub	si,cx		; SI <- data offset (it sets NC)

; ------------- OK: pop registers (here is NC)

		pop	di		; pop DI
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;         Find last/previous two characters in text in reverse direction
; -----------------------------------------------------------------------------
; INPUT:	AL = first character to find
;		AH = second character to find
;		BX = pointer to TEXT structure
;		SI = start offset (TextFind2ChLast sets SI to 0ffffh)
; OUTPUT:	SI = offset of found characters (or SI = -1 if not found)
;		CY = characters not found (SI = -1)
; -----------------------------------------------------------------------------

; ------------- find last occurrence of 2 characters

TextFind2ChLast:xor	si,si		; SI <- 0
		dec	si		; SI <- 0ffffh, find last occurrence

; ------------- push registers

TextFind2ChPrev:push	cx		; push CX
		push	di		; push DI

; ------------- get text length -> CX

		mov	cx,[bx+TEXT_Length] ; CX <- text length
		jcxz	TextFindChErr	; no text
		dec	cx		; CX -= 1, without last byte

; ------------- limit maximal offset -> SI

		cmp	si,cx		; check maximal offset
		jb	short TextFind2ChPrv2 ; offset is OK
		mov	si,cx		; SI <- text length
		dec	si		; SI <- offset of last 2 bytes

; ------------- get text address -> DI

TextFind2ChPrv2:lea	di,[bx+TEXT_Text+si] ; DI <- start pointer

; ------------- find data

		mov	cx,si		; CX <- remaining data
		inc	cx		; CX <- number of bytes
TextFind2ChPrv4:std			; set direction down
		repne	scasb		; find data
		cld			; set direction up
		jne	short TextFindChErr ; data not found
		cmp	ah,[di+2]	; check second character
		jne	short TextFind2ChPrv4 ; continue searching

; ------------- get offset of data (here is NC)

		mov	si,cx		; SI <- remaining bytes

; ------------- OK: pop registers (here is NC)

		pop	di		; pop DI
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;    Find first/next character from 2-character list in forward direction
; -----------------------------------------------------------------------------
; INPUT:	AL = character 1
;		AH = character 2
;		BX = pointer to TEXT structure
;		SI = start offset (TextFind2ListFirst sets SI to 0)
; OUTPUT:	SI = offset of found character (or SI = -1 if not found)
;		CY = character not found (SI = -1)
; -----------------------------------------------------------------------------

; ------------- find first occurrence of character

TextFind2ListFirst:
		xor	si,si		; SI <- 0, find first occurrence

; ------------- push registers

TextFind2ListNext:
		push	cx		; push CX
		push	di		; push DI

; ------------- get text length -> CX and text address -> DI

		mov	cx,[bx+TEXT_Length] ; CX <- text length
		lea	di,[bx+TEXT_Text+si] ; DI <- start pointer
		jcxz	TextFindChErr	; no text
		sub	cx,si		; CX <- remaining data
		jbe	short TextFindChErr ; data not found

; ------------- find data

TextFind2LNext2:cmp	[di],al		; check character 1
TextFind2ChOK2:	je	short TextFind2ChOK ; character found OK
		cmp	[di],ah		; check character 2
		je	short TextFind2ChOK ; character found OK
		inc	di		; increase text pointer
		loop	TextFind2LNext2	; next character
TextFind2LNext4:jmp	short TextFindChErr ; not found

; -----------------------------------------------------------------------------
;    Find first/next character from 3-character list in forward direction
; -----------------------------------------------------------------------------
; INPUT:	AL = character 1
;		AH = character 2
;		DL = character 3
;		BX = pointer to TEXT structure
;		SI = start offset (TextFind3ListFirst sets SI to 0)
; OUTPUT:	SI = offset of found character (or SI = -1 if not found)
;		CY = character not found (SI = -1)
; -----------------------------------------------------------------------------

; ------------- find first occurrence of character

TextFind3ListFirst:
		xor	si,si		; SI <- 0, find first occurrence

; ------------- push registers

TextFind3ListNext:
		push	cx		; push CX
		push	di		; push DI

; ------------- get text length -> CX and text address -> DI

		mov	cx,[bx+TEXT_Length] ; CX <- text length
		lea	di,[bx+TEXT_Text+si] ; DI <- start pointer
		jcxz	TextFindChErr	; no text
		sub	cx,si		; CX <- remaining data
		jbe	short TextFindChErr ; data not found

; ------------- find data

TextFind3LNext2:cmp	[di],al		; check character 1
		je	short TextFind2ChOK ; character found OK
		cmp	[di],ah		; check character 2
		je	short TextFind2ChOK ; character found OK
		cmp	[di],dl		; check character 3
		je	short TextFind2ChOK ; character found OK
		inc	di		; increase text pointer
		loop	TextFind3LNext2	; next character
		jmp	short TextFind2LNext4 ; not found

; -----------------------------------------------------------------------------
;    Find first/next character from 4-character list in forward direction
; -----------------------------------------------------------------------------
; INPUT:	AL = character 1
;		AH = character 2
;		DL = character 3
;		DH = character 4
;		BX = pointer to TEXT structure
;		SI = start offset (TextFind4ListFirst sets SI to 0)
; OUTPUT:	SI = offset of found character (or SI = -1 if not found)
;		CY = character not found (SI = -1)
; -----------------------------------------------------------------------------

; ------------- find first occurrence of character

TextFind4ListFirst:
		xor	si,si		; SI <- 0, find first occurrence

; ------------- push registers

TextFind4ListNext:
		push	cx		; push CX
		push	di		; push DI

; ------------- get text length -> CX and text address -> DI

		mov	cx,[bx+TEXT_Length] ; CX <- text length
		lea	di,[bx+TEXT_Text+si] ; DI <- start pointer
		jcxz	TextFind2LNext4	; no text
		sub	cx,si		; CX <- remaining data
		jbe	short TextFind2LNext4 ; data not found

; ------------- find data

TextFind4LNext2:cmp	[di],al		; check character 1
		je	short TextFind2ChOK2 ; character found OK
		cmp	[di],ah		; check character 2
		je	short TextFind2ChOK2 ; character found OK
		cmp	[di],dl		; check character 3
		je	short TextFind2ChOK2 ; character found OK
		cmp	[di],dh		; check character 4
		je	short TextFind2ChOK2 ; character found OK
		inc	di		; increase text pointer
		loop	TextFind4LNext2	; next character
		jmp	short TextFind2LNext4 ; not found

; -----------------------------------------------------------------------------
;    Find last/prev character from 2-character list in reverse direction
; -----------------------------------------------------------------------------
; INPUT:	AL = character 1
;		AH = character 2
;		BX = pointer to TEXT structure
;		SI = start offset (TextFind2ListLast sets SI to 0ffffh)
; OUTPUT:	SI = offset of found character (or SI = -1 if not found)
;		CY = character not found (SI = -1)
; -----------------------------------------------------------------------------

; ------------- find last occurrence of character

TextFind2ListLast:
		xor	si,si		; SI <- 0
		dec	si		; SI <- 0ffffh

; ------------- limit offset -> SI

TextFind2ListPrev:
		cmp	si,[bx+TEXT_Length] ; check offset
		jb	short TextFind2LPrev2 ; offset is OK
		mov	si,[bx+TEXT_Length] ; SI <- text length
		jmp	short TextFind2LPrev4

; ------------- find data

TextFind2LPrev2:cmp	[bx+TEXT_Text+si],al ; check character 1
		je	short TextFind2LPrev8 ; character found OK
		cmp	[bx+TEXT_Text+si],ah ; check character 2
		je	short TextFind2LPrev8 ; character found OK
TextFind2LPrev4:sub	si,byte 1	; decrease text pointer
		jnc	short TextFind2LPrev2 ; next character
TextFind2LPrev8:ret

; -----------------------------------------------------------------------------
;    Find last/prev character from 3-character list in reverse direction
; -----------------------------------------------------------------------------
; INPUT:	AL = character 1
;		AH = character 2
;		DL = character 3
;		BX = pointer to TEXT structure
;		SI = start offset (TextFind2ListLast sets SI to 0ffffh)
; OUTPUT:	SI = offset of found character (or SI = -1 if not found)
;		CY = character not found (SI = -1)
; -----------------------------------------------------------------------------

; ------------- find last occurrence of character

TextFind3ListLast:
		xor	si,si		; SI <- 0
		dec	si		; SI <- 0ffffh

; ------------- limit offset -> SI

TextFind3ListPrev:
		cmp	si,[bx+TEXT_Length] ; check offset
		jb	short TextFind3LPrev2 ; offset is OK
		mov	si,[bx+TEXT_Length] ; SI <- text length
		jmp	short TextFind3LPrev4

; ------------- find data

TextFind3LPrev2:cmp	[bx+TEXT_Text+si],al ; check character 1
		je	short TextFind3LPrev8 ; character found OK
		cmp	[bx+TEXT_Text+si],ah ; check character 2
		je	short TextFind3LPrev8 ; character found OK
		cmp	[bx+TEXT_Text+si],dl ; check character 3
		je	short TextFind3LPrev8 ; character found OK
TextFind3LPrev4:sub	si,byte 1	; decrease text pointer
		jnc	short TextFind3LPrev2 ; next character
TextFind3LPrev8:ret

; -----------------------------------------------------------------------------
;    Find last/prev character from 4-character list in reverse direction
; -----------------------------------------------------------------------------
; INPUT:	AL = character 1
;		AH = character 2
;		DL = character 3
;		DH = character 4
;		BX = pointer to TEXT structure
;		SI = start offset (TextFind2ListLast sets SI to 0ffffh)
; OUTPUT:	SI = offset of found character (or SI = -1 if not found)
;		CY = character not found (SI = -1)
; -----------------------------------------------------------------------------

; ------------- find last occurrence of character

TextFind4ListLast:
		xor	si,si		; SI <- 0
		dec	si		; SI <- 0ffffh

; ------------- limit offset -> SI

TextFind4ListPrev:
		cmp	si,[bx+TEXT_Length] ; check offset
		jb	short TextFind4LPrev2 ; offset is OK
		mov	si,[bx+TEXT_Length] ; SI <- text length
		jmp	short TextFind4LPrev4

; ------------- find data

TextFind4LPrev2:cmp	[bx+TEXT_Text+si],al ; check character 1
		je	short TextFind4LPrev8 ; character found OK
		cmp	[bx+TEXT_Text+si],ah ; check character 2
		je	short TextFind4LPrev8 ; character found OK
		cmp	[bx+TEXT_Text+si],dl ; check character 3
		je	short TextFind4LPrev8 ; character found OK
		cmp	[bx+TEXT_Text+si],dh ; check character 4
		je	short TextFind4LPrev8 ; character found OK
TextFind4LPrev4:sub	si,byte 1	; decrease text pointer
		jnc	short TextFind4LPrev2 ; next character
TextFind4LPrev8:ret

; -----------------------------------------------------------------------------
;                          Substitute character
; -----------------------------------------------------------------------------
; INPUT:	AL = old character (to search for)
;		AH = new character (to substitute with)
;		BX = pointer to TEXT structure
; -----------------------------------------------------------------------------

; ------------- push registers

TextSubstCh:	push	cx		; push CX
		push	di		; push DI

; ------------- get text length -> CX and text address -> DI

		mov	cx,[bx+TEXT_Length] ; CX <- text length
		lea	di,[bx+TEXT_Text] ; DI <- start pointer

; ------------- substitute character

TextSubstCh2:	jcxz	TextSubstCh8	; no text
TextSubstCh4:	repne	scasb		; find character
		jne	TextSubstCh8	; not found
		mov	[di-1],ah	; substitute character
		jmp	short TextSubstCh2 ; continue

; ------------- pop registers

TextSubstCh8:	pop	di		; pop DI
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;                     Find first/next text in forward direction
; -----------------------------------------------------------------------------
; INPUT:	AX = pointer to TEXT to find
;		BX = pointer to TEXT where to search
;		SI = start offset (TextFindFirst sets SI to 0)
; OUTPUT:	SI = offset of found text (or SI = -1 if text not found)
;		CY = text not found (SI = -1)
; -----------------------------------------------------------------------------

; ------------- find first occurrence of the text

TextFindFirst:	xor	si,si		; SI <- 0, find first occurrence

; ------------- push registers

TextFindNext:	push	ax		; push AX
		push	cx		; push CX
		push	dx		; push DX
		push	di		; push DI
		push	bp		; push BP

; ------------- limit start offset -> SI (for case of empty text to find)

		cmp	si,[bx+TEXT_Length] ; check start offset
		jb	short TextFindNext1 ; offset is OK
		mov	si,[bx+TEXT_Length] ; SI <- limit start offset

; ------------- find 1 or 2 characters, TEXT to find -> DI, length-2 -> BP

TextFindNext1:	mov	di,ax		; DI <- text to find
		mov	bp,[di+TEXT_Length] ; BP <- length of text to find
		or	bp,bp		; zero length?
		jz	short TextFindNext9 ; zero length, always return NC
		mov	al,[di+TEXT_Text] ; AL <- first character
		dec	bp		; length 1?
		jz	short TextFindNext2 ; text is one character
		mov	ah,[di+TEXT_Text+1] ; AH <- second character
		dec	bp		; length 2?
		jnz	short TextFindNext3 ; text is too long
		call	TextFind2ChNext	; find two characters
		jmp	short TextFindNext9
TextFindNext2:	call	TextFindChNext	; find one character
		jmp	short TextFindNext9

; ------------- get text length -> CX and text address -> DI
                
TextFindNext3:	lea	dx,[di+TEXT_Text+2] ; DX <- start of text to find + 2
		mov	cx,[bx+TEXT_Length] ; CX <- length of text
		sub	cx,bp		; CX <- without text length - 2
		dec	cx		; CX <- without first byte
		lea	di,[bx+TEXT_Text+si] ; DI <- start pointer
		sub	cx,si		; CX <- remaining data
		jbe	short TextFindNext7 ; text not found

; ------------- find begin of data

TextFindNext4:	repne	scasb		; find data
		jne	short TextFindNext7 ; data not found
		cmp	ah,[di]		; check second byte
		jne	short TextFindNext4 ; text not equal

; ------------- compare rest of text

		push	di		; push DI
		push	cx		; push CX

		mov	si,dx		; SI <- start of text + 2
		inc	di		; DI <- start of text + 2
		mov	cx,bp		; CX <- text length - 2
		shr	cx,1		; CX <- text length in WORDs
		repe	cmpsw		; compare texts in WORDs
		jne	short TextFindNext6 ; texts not equal
		mov	cx,bp		; CX <- text length - 2
		and	cx,byte 1	; CX <- rest of text in last DWORD
		repe	cmpsb		; compare byte in last WORD

TextFindNext6:	pop	cx		; pop CX
		pop	di		; pop DI
		jne	short TextFindNext4 ; text not equal

; ------------- get offset of data

		add	cx,bp		; CX <- offset in text
		add	cx,byte 2	; CX <- including 2 characters
		mov	si,[bx+TEXT_Length] ; SI <- text length
		sub	si,cx		; SI <- data offset (it sets NC)
		jmp	short TextFindNext9

; ------------- text not found

TextFindNext7:	xor	si,si		; SI <- 0
		dec	si		; SI <- -1, data not found
		stc			; set error flag

; ------------- pop registers

TextFindNext9:	pop	bp		; pop BP
		pop	di		; pop DI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                   Find last/previous text in reverse direction
; -----------------------------------------------------------------------------
; INPUT:	AX = pointer to TEXT to find
;		BX = pointer to TEXT where to search
;		SI = start offset (TextFindLast sets SI to 0ffffh)
; OUTPUT:	SI = offset of found text (or SI = -1 if text not found)
;		CY = text not found (SI = -1)
; -----------------------------------------------------------------------------

; ------------- find last occurrence of the text

TextFindLast:	xor	si,si		; SI <- 0
		dec	di		; SI <- 0ffffh, find last occurrence

; ------------- push registers

TextFindPrev:	push	ax		; push AX
		push	cx		; push CX
		push	dx		; push DX
		push	di		; push DI
		push	bp		; push BP

; ------------- limit start offset -> SI (for case of empty text to find)

		cmp	si,[bx+TEXT_Length] ; check start offset
		jb	short TextFindPrev1 ; offset is OK
		mov	si,[bx+TEXT_Length] ; SI <- limit start offset

; ------------- find 1 or 2 characters, TEXT to find -> DI, length-2 -> BP

TextFindPrev1:	mov	di,ax		; DI <- text to find
		mov	bp,[di+TEXT_Length] ; BP <- length of text to find
		or	bp,bp		; zero length?
		jz	short TextFindPrev9 ; zero length, always return NC
		mov	al,[di+TEXT_Text] ; AL <- first character
		dec	bp		; length 1?
		jz	short TextFindPrev2 ; text is one character
		mov	ah,[di+TEXT_Text+1] ; AH <- second character
		dec	bp		; length 2?
		jnz	short TextFindPrev3 ; text is too long
		call	TextFind2ChPrev	; find two characters
		jmp	short TextFindPrev9
TextFindPrev2:	call	TextFindChPrev	; find one character
		jmp	short TextFindPrev9

; ------------- get text length -> CX
                
TextFindPrev3:	lea	dx,[di+TEXT_Text+2] ; DX <- start of text to find + 2
		mov	cx,[bx+TEXT_Length] ; CX <- length of text
		sub	cx,bp		; CX <- without text length - 2
		dec	cx		; CX <- without first byte

; ------------- limit maximal offset -> SI

		cmp	si,cx		; check maximal offset
		jb	short TextFindPrev4 ; offset is OK
		mov	si,cx		; SI <- text length
		dec	si		; SI <- offset of last byte

; ------------- get text address -> DI

TextFindPrev4:	lea	di,[bx+TEXT_Text+si] ; DI <- start pointer
		mov	cx,si		; CX <- remaining data
		inc	cx		; CX <- number of bytes

; ------------- find begin of data

TextFindPrev5:	std			; set direction down
		repne	scasb		; find data
		cld			; set direction up
		jne	short TextFindPrev7 ; data not found
		cmp	ah,[di+2]	; check second byte
		jne	short TextFindPrev5 ; text not equal

; ------------- compare rest of text

		push	di		; push DI
		push	cx		; push CX

		mov	si,dx		; SI <- start of text + 2
		add	di,byte 3	; DI <- start of text + 2
		mov	cx,bp		; CX <- text length - 2
		shr	cx,1		; CX <- text length in WORDs
		repe	cmpsw		; compare texts in WORDs
		jne	short TextFindPrev6 ; texts not equal
		mov	cx,bp		; CX <- text length - 2
		and	cx,byte 1	; CX <- rest of text in last DWORD
		repe	cmpsb		; compare byte in last WORD

TextFindPrev6:	pop	cx		; pop CX
		pop	di		; pop DI
		jne	short TextFindPrev5 ; text not equal

; ------------- get offset of data (here is NC)

		mov	si,cx		; SI <- data offset
		jmp	short TextFindPrev9

; ------------- text not found

TextFindPrev7:	xor	si,si		; SI <- 0
		dec	si		; SI <- -1, data not found
		stc			; set error flag

; ------------- pop registers

TextFindPrev9:	pop	bp		; pop BP
		pop	di		; pop DI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;     Find first/next character not from list in text in forward direction
; -----------------------------------------------------------------------------
; INPUT:	AX = pointer to TEXT with list of characters to skip
;		BX = pointer to TEXT structure
;		SI = start offset (TextFindUnlistFirst sets SI to 0)
; OUTPUT:	SI = offset of found character (or SI = -1 if not found)
;		CY = character not found (SI = -1)
; -----------------------------------------------------------------------------

; ------------- find first occurrence of not characters

TextFindUnlistFirst:
		xor	si,si		; SI <- 0, find first occurrence

; ------------- push registers

TextFindUnlistNext:
		push	bp		; push BP
		xor	bp,bp		; BP <- 0, flag - forbidden chars
		jmp	short TextFindLNext1

; -----------------------------------------------------------------------------
;        Find first/next character from list in text in forward direction
; -----------------------------------------------------------------------------
; INPUT:	AX = pointer to TEXT with list of characters
;		BX = pointer to TEXT structure
;		SI = start offset (TextFindListFirst sets SI to 0)
; OUTPUT:	SI = offset of found character (or SI = -1 if not found)
;		CY = character not found (SI = -1)
; -----------------------------------------------------------------------------

; ------------- find first occurrence of character

TextFindListFirst:
		xor	si,si		; SI <- 0, find first occurrence

; ------------- push registers

TextFindListNext:
		push	bp		; push BP
		xor	bp,bp		; BP <- 0
		dec	bp		; BP <- 0ffffh, flag - allowed chars

TextFindLNext1:	push	ax		; push AX
		push	cx		; push CX
		push	dx		; push DX
		push	di		; push DI
		sub	sp,32		; local variable - 32-byte array

; ------------- prepare bitmap of character list

		mov	di,sp		; DI <- bitmap in stack
		call	TextPrepMap	; prepare bitmap

; ------------- get text length -> CX and text address -> DI

		mov	cx,[bx+TEXT_Length] ; CX <- text length
		lea	di,[bx+TEXT_Text+si] ; DI <- start pointer
		sub	cx,si		; CX <- remaining data
		jbe	short TextFindLNext3 ; data not found

; ------------- find data

		mov	si,di		; SI <- text
TextFindLNext2:	TEXTCHECKMAP inc	; check character
		je	short TextFindLNext4 ; character found OK
		loop	TextFindLNext2	; next character

; ------------- data not found (here jumps from other functions, too)

TextFindLNext3:	xor	si,si		; SI <- 0
		dec	si		; SI <- -1, data not found
		add	sp,32		; return SP
		stc			; set error flag
		jmp	short TextFindLNext8

; ------------- get offset of data

TextFindLNext4:	mov	si,[bx+TEXT_Length] ; SI <- text length
		sub	si,cx		; SI <- data offset
		add	sp,32		; return SP (sets NC)

; ------------- pop registers

TextFindLNext8:	pop	di		; pop DI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	ax		; pop AX
		pop	bp		; pop BP
		ret

; -----------------------------------------------------------------------------
;    Find last/previous character not from list in text in reverse direction
; -----------------------------------------------------------------------------
; INPUT:	AX = pointer to TEXT with list of characters to skip
;		BX = pointer to TEXT structure
;		SI = start offset (TextFindUnlistLast sets SI to 0ffffh)
; OUTPUT:	SI = offset of found character (or SI = -1 if not found)
;		CY = character not found (SI = -1)
; -----------------------------------------------------------------------------

; ------------- find last occurrence of not characters

TextFindUnlistLast:
		xor	si,si		; SI <- 0
		dec	si		; SI <- 0ffffh, find last occurrence

; ------------- push registers

TextFindUnlistPrev:
		push	bp		; push BP
		xor	bp,bp		; BP <- 0, flag - forbidden chars
		jmp	short TextFindLPrev1

; -----------------------------------------------------------------------------
;    Find last/previous character from list in text in reverse direction
; -----------------------------------------------------------------------------
; INPUT:	AX = pointer to TEXT with list of characters
;		BX = pointer to TEXT structure
;		SI = start offset (TextFindListLast sets SI to 0ffffh)
; OUTPUT:	SI = offset of found character (or SI = -1 if not found)
;		CY = character not found (SI = -1)
; -----------------------------------------------------------------------------

; ------------- find last occurrence of character

TextFindListLast:
		xor	si,si		; SI <- 0
		dec	si		; SI <- 0ffffh, find last occurrence

; ------------- push registers

TextFindListPrev:
		push	bp		; push BP
		xor	bp,bp		; BP <- 0
		dec	bp		; BP <- 0ffffh, flag - allowed chars

TextFindLPrev1:	push	ax		; push AX
		push	cx		; push CX
		push	dx		; push DX
		push	di		; push DI
		sub	sp,32		; local variable - 32-byte array

; ------------- prepare bitmap of character list

		mov	di,sp		; DI <- bitmap in stack
		call	TextPrepMap	; prepare bitmap

; ------------- get text length -> CX

		mov	cx,[bx+TEXT_Length] ; CX <- text length
		jcxz	TextFindLPrev4	; no text

; ------------- limit maximal offset -> SI

		cmp	si,cx		; check maximal offset
		jb	short TextFindLPrev2 ; offset is OK
		mov	si,cx		; SI <- text length
		dec	si		; SI <- offset of last byte

; ------------- get text address -> DI

TextFindLPrev2:	lea	di,[bx+TEXT_Text+si] ; DI <- start pointer

; ------------- find data

		mov	cx,si		; CX <- remaining data
		inc	cx		; CX <- number of bytes
		mov	si,di		; SI <- text
TextFindLPrev3:	TEXTCHECKMAP dec	; check character
		je	short TextFindLPrev6 ; character found OK
		loop	TextFindLPrev3	; next character

; ------------- data not found (here jumps from other functions, too)

TextFindLPrev4:	xor	si,si		; SI <- 0
		dec	si		; SI <- -1, data not found
		add	sp,32		; return SP
		stc			; set error flag
		jmp	short TextFindLPrev8

; ------------- get offset of data

TextFindLPrev6:	mov	si,cx		; SI <- remaining bytes
		dec	si
		add	sp,32		; return SP (sets NC)

; ------------- pop registers

TextFindLPrev8:	pop	di		; pop DI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	ax		; pop AX
		pop	bp		; pop BP
		ret

; -----------------------------------------------------------------------------
;                           Add date to text
; -----------------------------------------------------------------------------
; INPUT:	AH = date format (DATEFORM_EURO,...)
;		BX = pointer to TEXT structure
;		DL = day
;		DH = month
;		SI = year
; OUTPUT:	BX = old or new pointer to TEXT structure
;		CY = memory error or invalid date format
; -----------------------------------------------------------------------------

; ------------- push registers

TextAddDate:	push	ax		; push AX

; ------------- Europe format day.month.year

		cmp	ah,DATEFORM_EURO ; Europe format?
		jne	short TextAddDate2 ; no

		mov	al,dl		; AL <- day
		call	TextAddByte	; add day
		jc	short TextAddDate8 ; memory error
		mov	al,"."		; separator
		call	TextAddChar	; add separator
		jc	short TextAddDate8 ; memory error

		mov	al,dh		; AL <- month
		call	TextAddByte	; add month
		jc	short TextAddDate8 ; memory error
		mov	al,"."		; separator
		call	TextAddChar	; add separator
		jc	short TextAddDate8 ; memory error

		mov	ax,si		; AX <- year
		call	TextAddWord	; add year
		jmp	short TextAddDate8

; ------------- USA format month/day/year

TextAddDate2:	cmp	ah,DATEFORM_USA ; USA format?
		jne	short TextAddDate4 ; no

		mov	al,dh		; AL <- month
		call	TextAddByte	; add month
		jc	short TextAddDate8 ; memory error
		mov	al,"/"		; separator
		call	TextAddChar	; add separator
		jc	short TextAddDate8 ; memory error

		mov	al,dl		; AL <- day
		call	TextAddByte	; add day
		jc	short TextAddDate8 ; memory error
		mov	al,"/"		; separator
		call	TextAddChar	; add separator
		jc	short TextAddDate8 ; memory error

		mov	ax,si		; AX <- year
		call	TextAddWord	; add year
		jmp	short TextAddDate8

; ------------- Japan format year-month-day

TextAddDate4:	cmp	ah,DATEFORM_JAPAN ; Japan format?
		stc			; set error flag
		jne	short TextAddDate8 ; no

		mov	ax,si		; AX <- year
		call	TextAddWord	; add year
		jc	short TextAddDate8 ; memory error
		mov	al,"-"		; separator
		call	TextAddChar	; add separator
		jc	short TextAddDate8 ; memory error

		mov	al,dh		; AL <- month
		call	TextAdd2Dig	; add month
		jc	short TextAddDate8 ; memory error
		mov	al,"-"		; separator
		call	TextAddChar	; add separator
		jc	short TextAddDate8 ; memory error

		mov	al,dl		; AL <- day
		call	TextAdd2Dig	; add day

; ------------- pop registers

TextAddDate8:	pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                           Add time to text (in format HH:MM:SS)
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
;		CL = minute
;		CH = hour
;		DH = second
; OUTPUT:	BX = old or new pointer to TEXT structure
;		CY = memory error
; -----------------------------------------------------------------------------

; ------------- push registers

TextAddTime:	push	ax		; push AX

; ------------- add hour

		mov	al,ch		; AL <- hour
		call	TextAddByte	; add hour
		jc	short TextAddTime8 ; memory error
		mov	al,":"		; separator
		call	TextAddChar	; add separator
		jc	short TextAddTime8 ; memory error

; ------------- add minute

		mov	al,cl		; AL <- minute
		call	TextAdd2Dig	; add minute
		jc	short TextAddTime8 ; memory error
		mov	al,":"		; separator
		call	TextAddChar	; add separator
		jc	short TextAddTime8 ; memory error

; ------------- add second

		mov	al,dh		; AL <- second
		call	TextAdd2Dig	; add second

; ------------- pop registers

TextAddTime8:	pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                                 Add disk size
; -----------------------------------------------------------------------------
; INPUT:	DX:AX = number of 512 B sectors
;		BX = pointer to TEXT structure
; OUTPUT:	BX = old or new pointer to TEXT structure
;		CY = memory error
; -----------------------------------------------------------------------------

; ------------- push registers

TextAddDiskSize:push	ax		; push AX
		push	dx		; push DX
		push	si		; push SI

; ------------- prepare KB or MB

		mov	si,TextKB	; SI <- text "KB"
		or	dx,dx		; big disk?
		jnz	short TextAddDiskSiz2 ; big disk
		cmp	ax,1000h	; small disk < 2 MB
		jb	short TextAddDiskSiz4 ; small disk
TextAddDiskSiz2:mov	si,TextMB	; SI <- text "MB"

; ------------- convert sectors to MB

		mov	al,ah
		mov	ah,dl
		mov	dl,dh
		mov	dh,0		; DX:AX <- number of sectors >> 8

		shr	dx,1
		rcr	ax,1		; shift >> 9 (7F0000h)
		shr	dx,1
		rcr	ax,1		; shift >> 10 (3F0000h)
TextAddDiskSiz4:shr	dx,1
		rcr	ax,1		; shift >> 11 (1F0000h)

; ------------- decode disk size

		call	TextAddDWord	; add size
		xchg	ax,si		; AX <- text "MB" or "KB"
		call	TextAddText	; add text "MB" or "KB"

; ------------- pop registers

		pop	si		; pop SI
		pop	dx		; pop DX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                               Add path separator
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
; OUTPUT:	BX = old or new pointer to TEXT structure
;		CY = memory error
; -----------------------------------------------------------------------------

TextAddPathSep:	push	ax		; push AX
		mov	al,0		; AL <- default character
		call	TextGetLast	; get last character -> AL
		cmp	al,PATHCHAR	; path separator?
		je	short TextAddPathSep2 ; separator is OK
		cmp	al,PATHCHAR2	; alternative path separator?
		je	short TextAddPathSep2 ; separator is OK
		mov	al,PATHCHAR	; AL <- path separator
		call	TextAddChar	; add character
TextAddPathSep2:pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                      Delete end path separator (except root)
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
; -----------------------------------------------------------------------------

TextDelPathSep:	push	si		; push SI

		mov	si,[bx+TEXT_Length] ; SI <- text length
		cmp	si,byte 2	; check minimal length
		jb	short TextDelPathSep2 ; leave root

		cmp	byte [bx+TEXT_Text+si-1],PATHCHAR ; check last char
		je	short TextDelPathSep2 ; path separator
		cmp	byte [bx+TEXT_Text+si-1],PATHCHAR2 ; check last char
		jne	short TextDelPathSep4 ; not path separator
TextDelPathSep2:cmp	byte [bx+TEXT_Text+si-2],":" ; is it root?
		je	short TextDelPathSep4 ; root

		dec	word [bx+TEXT_Length] ; delete last character

TextDelPathSep4:pop	si		; pop S
		ret

; -----------------------------------------------------------------------------
;                        Get unsigned number from text
; -----------------------------------------------------------------------------
; INPUT:	SI = text
;		CX = text length
; OUTPUT:	DX:AX = number
;		CY = invalid character (not digit) or overflow
; -----------------------------------------------------------------------------

; ------------- push registers

ParseInt:	push	bx		; push BX
		push	cx		; push CX
		push	si		; push SI
		push	di		; push DI

; ------------- prepare registers

		xor	di,di		; DI <- accumulator HIGH
		xor	bx,bx		; BX <- accumulator LOW
		jcxz	ParseInt9	; no character

; ------------- load next character

ParseInt2:	lodsb			; AL <- character
		sub	al,"0"		; AL <- digit
		jc	short ParseInt9 ; invalid character
		cmp	al,9		; check max. value
		ja	short ParseInt8 ; invalid character

; ------------- shift accumulator

		cbw			; AX <- digit
		xchg	ax,di		; DI <- push digit, AX <- accum. HIGH
		mov	dx,10		; DX <- multiplier
		mul	dx		; DX:AX <- accumulator HIGH * 10
		or	dx,dx		; check overflow
		jnz	short ParseInt8 ; overflow
		xchg	ax,di		; DI <- accumulator HIGH, AX <- digit

		xchg	ax,bx		; AX <- accumulator LOW, BX <- digit
		mov	dx,10		; DX <- multiplier
		mul	dx		; DX:AX <- accumulator LOW * 10

		add	bx,ax		; BX <- new accumulator LOW
		adc	di,dx		; DI <- new accumulator HIGH
		jc	short ParseInt9 ; error
		loop	ParseInt2	; next character
		jmp	short ParseInt9

; ------------- result

ParseInt8:	stc			; set error flag
ParseInt9:	xchg	ax,bx		; AX <- accumulator LOW
		mov	dx,di		; DX <- accumulator HIGH

; ------------- pop registers

		pop	di		; pop DI
		pop	si		; pop SI
		pop	cx		; pop CX
		pop	bx		; pop BX
		ret

; -----------------------------------------------------------------------------
;                            Constant data
; -----------------------------------------------------------------------------

		CONST_SECTION

TextKB:		CTEXT	"KB"
TextMB:		CTEXT	"MB"
