AppleIIAsm-Collection/disks/disk5_strings/T.SUBSTRINGS.LIB
nathanriggs 16c1731e54 Strings 0.2.0 update
- bugfixes
- implemented required library
- commenting updates
- .min of every routine
2018-12-22 20:35:05 -05:00

845 lines
21 KiB
Plaintext

*
JMP SUBSTRINGSX
*
*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*
* *
* SUBSTRING ROUTINE LIBRARY *
* *
* AUTHOR: NATHAN RIGGS *
* CONTACT: NATHAN.RIGGS@ *
* OUTLOOK.COM *
* *
* VERSION: 0.2.0 *
* DATE: 30-OCT-2018 *
* ASSEMBLER: MERLIN 8 PRO *
* OS: DOS 3.3 *
* LICENSE: APACHE 2.0 *
* *
* THIS IS A LIBRARY FOR USING *
* SUBSTRING OPERATIONS. *
* *
*------------------------------*
* *
* LIST OF ROUTINES *
* *
* SUBPOS : FIND POS OF SUBSTR *
* SUBCOPY : COPY SUBSTRING *
* SUBDEL : DELETE SUBSTRING *
* SUBINS : INSERT SUBSTRING *
* *
*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*
*
*``````````````````````````````*
* SUBPOS :: SUBSTRING POSITION *
*- -*
* FIND THE POSITION OF A SUB- *
* STRING WITHIN ANOTHER STRING *
*- -*
* CLOBBERS: *
* *
* FLAGS: ????---- REG: AXYM *
*- -*
* CYCLES: ??? *
* SIZE: *
*- -*
* USAGE: *
* *
* LDA #>STR ; STRING ADDR *
* PHA *
* LDA #<STR *
* PHA *
* LDA #>SUB ; SUBSTR ADDR *
* PHA *
* LDA #<SUB *
* JSR SUBPOS *
*- -*
* ENTRY *
* *
* TOP OF STACK *
* *
* LOW BYTE OF RETURN ADDRESS *
* LOW BYTE OF SUBSTRING ADDR *
* HI BYTE OF SUBSTRING ADDR *
* LO BYTE OF STRING ADDR *
* HI BYTE OF STRING ADDRESS *
* HI BYTE OF RETURN ADDRESS *
*- -*
* EXIT *
* *
* TOP OF STACK *
* *
* LOW BYTE OF RETURN ADDRESS *
* HI BYTE OF RETURN ADDRESS *
* *
* .Y = CLOBBERED; TRASH *
* .X = CLOBBERED; TRASH *
* .A = INDEX OF SUBSTRING IF *
* FOUND; OTHERWISE, 0 *
* *
* [RETURN] = INDEX OF SUBSTR; *
* 0 IF NOT FOUND *
* [RETLEN] = 1 (INDEX LENGTH) *
*- -*
* ADAPTED FROM LEVANTHAL AND *
* WINTHROP'S /6502 ASSEMBLY *
* LANGUAGE ROUTINES/. MAY NOT *
* FALL UNDER APACHE 2.0 UNTIL *
* HEAVILY MODIFIED. *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
SUBPOS
*
** SAVE RETURN ADDRESS
*
PLA
STA RETADR
PLA
STA RETADR+1
*
** GET PARAMETERS
*
PLA
STA ADDR2
PLA
STA ADDR2+1
PLA
STA ADDR1
PLA
STA ADDR1+1
*
** RESTORE RETURN ADDRESS
*
LDA RETADR+1
PHA
LDA RETADR
PHA
*
:POS
LDY #0
LDA (ADDR1),Y ; GET LENGTH OF STRING
BEQ :NOTFND ; EXIT IF LENGTH = 0
STA :SLEN
LDA (ADDR2),Y ; GET SUBSTR LENGTH
BEQ :NOTFND ; EXIT IF SUB LENGTH = 0
STA :SUBLEN
*
** IF THE SUBSTR IS LONGER THAN STR, DECLARE THE
** SUBSTR NOT FOUND
*
LDA :SUBLEN
CMP :SLEN
BEQ :LENOK
BCS :NOTFND ; CANNOT FIND SUBSTR IF
; LONGER THAN STR
*
** START, SEARCH, CONTINUE UNTIL
** REMAINING STR SHORTER THAN SUBSTR
*
:LENOK
LDA #1
STA :SINDEX ; START LOOKING AT FIRST
; CHARACTER OF STRING
LDA :SLEN ; CONT UNTIL REMAINING STR
; TOO SHORT
SEC ; COUNT=STR LEN - SUB LEN+1
SBC :SUBLEN
STA :SCOUNT
INC :SCOUNT
*
** SEARCH FOR SUBSTRING IN STRING
*
:SLP1
LDA :SINDEX
STA :SIDX ; START STR AT INDEX
LDA #1
STA :SUBIDX ; START SUB IND AT 1
*
** LOOK FOR SUBSTRING BEGINNING AT INDEX
*
:CMPLP
LDY :SIDX
LDA (ADDR1),Y ; GET NEXT CHAR FROM STR
LDY :SUBIDX
CMP (ADDR2),Y ; COMPARE TO NEXT SUB CHAR
BNE :SLP2 ; BR IF SUB NOT HERE
LDY :SUBIDX
CPY :SUBLEN ; TEST IF WE ARE DONE
BEQ :FOUND ; BR IF ALL CHARS WERE EQUAL
INY ; ELSE INC TO NEXT CHAR
STY :SUBIDX
INC :SIDX
JMP :CMPLP ; CONTINUE
*
** ARRIVE HERE IF SUBSTRING NOT FOUND
*
:SLP2
INC :SINDEX ; INCREMENT INDEX
DEC :SCOUNT ; DEC COUNT
BNE :SLP1 ; BR IF NOT DONE
BEQ :NOTFND ; ELSE EXIT TO NOT FOUND
*
:FOUND
LDA :SINDEX ; FOUND, A = STARTING IDX
JMP :EXIT
*
:NOTFND
LDA #0 ; SUB NOT FOUND, A=0
:EXIT
STA RETURN
LDY #1
STY RETLEN
RTS
*
** DATA
*
:SLEN DS 1
:SUBLEN DS 1
:SINDEX DS 1
:SUBIDX DS 1
:SCOUNT DS 1
:SIDX DS 1
*
*``````````````````````````````*
* SUBCOPY :: COPY SUBSTRING *
*- -*
* COPY A SUBSTRING FROM A *
* STRING TO [RETURN]. *
*- -*
* CLOBBERS: *
* *
* FLAGS: ????---- REG: AXYM *
*- -*
* CYCLES: ??? *
* SIZE: *
*- -*
* USAGE: *
* *
* LDA #>STR ; SOURCE STRING *
* PHA *
* LDA #<STR *
* PHA *
* LDA #IND ; COPY START INDEX *
* PHA *
* LDA LEN ; LENGTH OF SUBSTR *
* PHA *
* LDA MLEN ; MAX LENGTH *
* PHA *
* JSR SUBCOPY *
*- -*
* ENTRY *
* *
* TOP OF STACK *
* *
* LOW BYTE OF RETURN ADDRESS *
* HI BYTE OF RETURN ADDRESS *
* MAX LENGTH OF DEST STRING *
* NUMBER OF BYTES TO COPY *
* STARTING INDEX TO COPY FROM *
* LO BYTE OF SRC STRING ADDR *
* HI BYTE OF SOURCE STR ADDR *
*- -*
* EXIT *
* *
* TOP OF STACK *
* *
* LOW BYTE OF RETURN ADDRESS *
* HI BYTE OF RETURN ADDRESS *
* *
* .Y = TRASH *
* .X = TRASH *
* .A = TRASH *
* *
* CARRY FLAG WILL BE 0 IF NO *
* ERRORS; ELSE, CARRY = 1 *
*- -*
* ADAPTED FROM LEVANTHAL AND *
* WINTHROP'S /6502 ASSEMBLY *
* LANGUAGE ROUTINES/. *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
SUBCOPY
*
** SAVE RETURN ADDRESS
*
PLA
STA RETADR
PLA
STA RETADR+1
*
** GET PARAMETERS
*
PLA
STA :MLEN
PLA
STA :SCNT
STA RETLEN
PLA
STA :SINDEX
PLA
STA ADDR1
PLA
STA ADDR1+1
*
LDA #<RETURN
STA ADDR2
LDA #>RETURN
STA ADDR2+1
*
** RESTORE RETURN ADDRESS
*
LDA RETADR+1
PHA
LDA RETADR
PHA
*
LDA #0
STA :S2LEN ; DESTINATION LENGTH = 0
STA :SCERR ; ASSUME NO ERRORS
*
** CHECK FOR ZERO BYTES TO COPY OR ZERO MAX SUBSTR LENGTH
*
LDA :SCNT
BEQ :OKEXIT ; BR IF 0 BYTES TO COPY,
; S2A WILL JUST HAVE ZERO LENGTH
LDA :MLEN
BEQ :EREXIT ; ERROR EXIT IF SUBSTR HAS
; ZERO MAX LENGTH
LDA :SINDEX
BEQ :EREXIT ; ERROR EXIT IF START IDX = 0
*
** CHECK IF SRC STR REACHES STARTING INDEX
*
LDY #0
LDA (ADDR1),Y ;
STA :S1LEN ; GET LENGTH OF SOURCE STRING
CMP :SINDEX ; COMPARE TO STARTING INDEX
BCC :EREXIT ; ERROR EXIT IF INDEX TOO BIG
*
** CHECK THAT WE DO NOT COPY BEYOND THE END OF
** THE SOURCE STRING.
** IF INDEX + COUNT -1 > SLEN THEN
** COUNT = SLEN - SINDEX + 1
*
LDA :SINDEX
CLC
ADC :SCNT
BCS :RECALC
TAX ; BR IF INDEX + COUNT > 255
DEX
CPX :S1LEN
BCC :CNT10K ; BR IF IND + CNT - 1 < S1LEN
BEQ :CNT10K ; OR EQUAL
*
** THE CALLER ASKED FOR TOO MANY CHARS SO
** JUST RETURN EVERYTHING BETWEEN INDEX AND
** END OF STRING. SO CNT = S1LEN - INDEX + 1
*
:RECALC
LDA :S1LEN ; RECALCULATE COUNT
SEC
SBC :SINDEX
STA :SCNT
INC :SCNT ; CNT = S1LEN - IND + 1
LDA #$0FF
STA :SCERR ; INDICATE TRUNCATION
*
** CHECK IF COUNT IS <= THE MAXIMUM LENGTH
** OF THE DEST STRING. IF NOT, THEN SET COUNT TO
** MAX LENGTH.
** IF COUNT > MAXLEN THEN
** COUNT = MAXLEN
*
:CNT10K
LDA :SCNT
CMP :MLEN ; IF CNT > M SUBSTR LEN ?
BCC :CNT20K ; BR IF CNT < MAXLEN
BEQ :CNT20K ; BR IF CNT = MAXLEN
LDA :MLEN
STA :SCNT ; ELSE CNT = MAXLEN
LDA #$0FF
STA :SCERR ; INDICATE DEST STR OVERFLOW
*
** EVERYTHING IS SET UP SO MOVE THE
** SUBSTRING TO THE DESTINATION STRING
*
:CNT20K
LDX :SCNT ; REG X WILL BE COUNTER
BEQ :EREXIT ; ERR IF 0
LDA #1 ; START WITH 1ST CHAR IN DEST
STA :S2LEN ; RUNNING DEST INDEX
; __SINDEX IS SRC INDEX
:MVLP
LDY :SINDEX
LDA (ADDR1),Y ; GET NEXT SRC CHAR
LDY :S2LEN
STA (ADDR2),Y ; MOVE NEXT CHAR TO DEST
INC :SINDEX ; INC SRC INDEX
INC :S2LEN ; INC DEST INDEX
DEX ; DECREMENT COUNTER
BNE :MVLP ; CONT UNTIL CNT = 0
DEC :S2LEN ; SUBSTR LEN=FINAL DEST IND-1
LDA :SCERR ; CHECK FOR ANY ERRORS
BNE :EREXIT ; BR IF STR TRUNCATED OR OVERFLOW
*
** GOOD EXIT
*
:OKEXIT
CLC
BCC :EXIT
*
** ERROR EXIT
*
:EREXIT
SEC
*
** STORE LENGTH BYTE IN FRONT OF SUBSTR
*
:EXIT
LDA :S2LEN
LDY #0
STA (ADDR2),Y
STA RETLEN
RTS
*
** DATA
*
:S1LEN DS 1
:S2LEN DS 1
:MLEN DS 1
:SCNT DS 1
:SINDEX DS 1
:SCERR DS 1
*
*``````````````````````````````*
* SUBDEL :: DELETE SUBSTRING *
*- -*
* DELETE A SUBSTRING FROM A *
* STRING. *
*- -*
* CLOBBERS: *
* *
* FLAGS: ????---- REG: AXYM *
*- -*
* CYCLES: ??? *
* SIZE: *
*- -*
* USAGE: *
* *
* LDA #>STR ; STRING ADDRESS *
* PHA *
* LDA #<STR *
* PHA *
* LDA IND ; DEL START INDEX *
* PHA *
* LDA LEN ; LENGTH OF DELETE *
* PHA *
* JSR SUBDEL *
*- -*
* ENTRY *
* *
* TOP OF STACK *
* *
* LOW BYTE OF RETURN ADDRESS *
* HI BYTE OF RETURN ADDRESS *
* NUMBER OF BYTES TO DELETE *
* STARTING INDEX OF DELETION *
* LO BYTE OF STRING ADDRESS *
* HI BYTE OF STRING ADDRESS *
*- -*
* EXIT *
* *
* TOP OF STACK *
* *
* LOW BYTE OF RETURN ADDRESS *
* HI BYTE OF RETURN ADDRESS *
* *
* .Y = TRASH *
* .X = TRASH *
* .A = TRASH *
* *
* IF NO ERRORS, CARRY = 0; *
* ELSE, CARRY = 1 *
*- -*
* ADAPTED FROM LEVANTHAL AND *
* WINTHROP'S /6502 ASSEMBLY *
* LANGUAGE ROUTINES/. *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
SUBDEL
*
** SAVE RETURN ADDRES
*
PLA
TAY
PLA
TAX
*
** GET PARAMTERS
*
PLA
STA :SCNT
PLA
STA :SINDEX
PLA
STA ADDR1
PLA
STA ADDR1+1
*
** RESTORE RETURN ADDRESS
*
TXA
PHA
TYA
PHA
*
** INITIALIZE ERROR INDICATOR TO 0
*
LDY #0
STY :SCERR
LDA (ADDR1),Y
STA :S1LEN ; GET STRING LENGTH
*
** CHECK FOR A NON-ZERO COUNT AND INDEX
*
LDA :SCNT
BEQ :OKEXIT
; GOOD EXIT IF NO DELETE
LDA :SINDEX
BEQ :ERREXIT ; ERR EXIT IF START = 0
*
** CHECK FOR STARTING INDEX WITHIN THE STRING
*
* EXIT IF IT IS NOT
*
LDA :S1LEN
CMP :SINDEX
BCC :ERREXIT
*
** BE SURE THE NUMBER OF CHARACTERS REQUESTED
** TO BE DELETED ARE PRESENT.
** IF NOT, THEN ONLY DELETE FROM THE INDEX
** TO THE END OF THE STRING.
*
LDA :SINDEX
CLC
ADC :SCNT
BCS :TRUNC ;TRUNCATE IF INDEX
; + COUNT > 255
STA :SIDX ; SAVE INDEX + COUNT AS
; THE SOURCE INDEX
TAX ; X = INDEX + COUNT
DEX
CPX :S1LEN
BCC :CNTOK ; BR IF IND + CNT - 1
; < __S1LEN
; ELSE JUST TRUNC STRING
BEQ :TRUNC ; TRUNC BUT NO ERROR--
; EXACTLY ENOUGH CHARS
LDA #$0FF
STA :SCERR ; INDICATE ERROR - NOT
; ENOUGH CHARS TO DELETE
*
** TRUNCATE THE STRING - NO COMPACTING NECESSARY
*
:TRUNC
LDX :SINDEX ; STRING LENGTH =
; START INDEX - 1
DEX
STX :S1LEN
LDA :SCERR
BEQ :OKEXIT
BNE :ERREXIT
*
** DELETE THE SUBSTRING BY COMPACTING
** MOVE ALL CHARS ABOVE THE DELETED AREA DOWN
*
:CNTOK
*
** CALCULATE NUMBER OF CHARS TO MOVE
** (SLEN - SIDX + 1)
*
LDA :S1LEN ; GET STR LENGTH
SEC
SBC :SIDX ; SUBTRACT START INDEX
TAX
INX
BEQ :OKEXIT ; ADD 1 TO INCLUDE LAST
; CHAR; BR IF CNT = 0
:MVLP
LDY :SIDX
LDA (ADDR1),Y ; GET NEXT CHAR
LDY :SINDEX
STA (ADDR1),Y ;MOVE IT DOWN
INC :SINDEX
INC :SIDX ; INC DEST, SRC INDEXES
DEX
BNE :MVLP ; CONT UNTIL CNT = 0
LDX :SINDEX
DEX ; START LENGTH = FINAL
; DEST INDEX -1
STX :S1LEN
*
** GOOD EXIT
*
:OKEXIT
CLC
BCC :EXIT
:ERREXIT
SEC
*
:EXIT
LDA :S1LEN
LDY #0
STA (ADDR1),Y ; SET LENGTH OF STRING
RTS
*
** DATA
*
:S1LEN DS 1
:SCNT DS 1
:SINDEX DS 1
:SIDX DS 1
:SCERR DS 1
*
*``````````````````````````````*
* SUBINS :: INSERT SUBSTRING *
*- -*
* INSERT A SUBSTRING INTO *
* ANOTHER STRING. *
*- -*
* CLOBBERS: *
* *
* FLAGS: ????---- REG: AXYM *
*- -*
* CYCLES: ??? *
* SIZE: *
*- -*
* USAGE: *
* *
* LDA #STR ; STRING TO INS TO *
* PHA *
* LDA #<STR *
* PHA *
* LDA IND ; INDEX TO START INS *
* PHA *
* LDA MLEN ; MAX LENGTH OF STR *
* PHA *
* LDA #>SUB ; SUBSTRING TO INS *
* PHA *
* LDA #<SUB *
* PHA *
* JST SUBINS *
*- -*
* ENTRY *
* *
* TOP OF STACK *
* *
* LOW BYTE OF RETURN ADDRESS *
* HI BYTE OF RETURN ADDRESS *
* LO BYTE OF SUBSTRING ADDRESS *
* HI BYTE OF SUBSTRING ADDRESS *
* MAX LENGTH OF FINAL STRING *
* STARTING INDEX FOR INSERTION *
* LO BYTE OF SRC STRING ADDR *
* HI BYTE OF SOURCE STR ADDR *
*- -*
* EXIT *
* *
* TOP OF STACK *
* *
* LOW BYTE OF RETURN ADDRESS *
* HI BYTE OF RETURN ADDRESS *
* *
* .Y = TRASH *
* .X = TRASH *
* .A = TRASH *
* *
* CARRY = 0 IF NO ERRORS; ELSE *
* CARRY = 1 *
*- -*
* ADAPTED FROM LEVANTHAL AND *
* WINTHROP'S /6502 ASSEMBLY *
* LANGUAGE ROUTINES/. *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
SUBINS
*
** GET RETURN ADDRESS
*
PLA
TAY
PLA
TAX
*
** GET PARAMETERS
*
PLA
STA ADDR2
PLA
STA ADDR2+1
PLA
STA :MLEN
PLA
STA :SINDEX
PLA
STA ADDR1
PLA
STA ADDR1+1
*
** RESTORE RETURN ADDRESS
*
TXA
PHA
TYA
PHA
*
*
** ASSUME NO ERRORS
*
LDA #0
STA :SCERR ; ASSUME NO ERR WILL BE FOUND
*
** GET SUBSTRING AND STRING LENGTHS
** IF SUB LENGTH = 0 THEN EXIT NO ERROR
*
LDY #0
LDA (ADDR1),Y
STA :S1LEN ; GET LENGTH OF STRING
LDA (ADDR2),Y
STA :S2LEN ; GET LENGTH OF SUB
BNE :IDX0
JMP :OKEXIT ; EXIT OF NO INSERT/ERR
*
** IF STARTING INDEX IS 0 THEN ERROR EXIT
*
:IDX0
LDA :SINDEX
BNE :CHKLEN ; BR OF INDEX NOT 0
JMP :EREXIT ; ELSE ERROR EXIT
*
** CHECK THAT THE RESULTING STRING AFTER THE
** INSERTION FITS IN THE SOURCE STRING. IF NOT
** THEN TRUNCATE THE SUBSTRING AND SET
** THE TRUNCATION FLAG.
*
:CHKLEN
LDA :S2LEN ; GET SUBSTR LENGTH
CLC
ADC :S1LEN
BCS :TRUNC ;TRUN IF S1+S2 LENGTH > 255
CMP :MLEN ;
BCC :IDXLEN ; BR IF S1+S2 LEN < MAX LENGTH
BEQ :IDXLEN ; BR IF EQUAL
*
** SUBSTRING DOES NOT FIT, SO TRUNCATE IT
*
:TRUNC
LDA :MLEN ; SUBSTR LEN = MLEN - STR LEN
SEC
SBC :S1LEN
BCC :EREXIT
BEQ :EREXIT ; ERR IF MLEN < STR LEN OR 0
; (ORIGINAL STRING WAS TOO LONG)
STA :S2LEN
LDA #$0FF
STA :SCERR ; INDICATE SUBSTR WAS TRUNCATED
*
** CHECK THAT INDEX IS WITHIN STRING. IF NOT, CONCAT
** SUBSTR ONTO THE END OF THE STRING.
*
:IDXLEN
LDA :S1LEN
CMP :SINDEX ;
BCS :LENOK ; BR IF INDEX WITHIN STR
LDX :S1LEN ; ELSE CONCAT SUB AT END OF STR
INX
STX :SINDEX ; START RIGHT AFTER END OF STR
LDA #$0FF
STA :SCERR ; INDICATE ERR IN INSERT
LDA :S1LEN
CLC
ADC :S2LEN
STA :S1LEN ; ADD LENGTHS TOGETHER
JMP :MVESUB ; PERFORM MOVE, NOTHING ELSE TODO
*
** OPEN UP A SPACE IN THE SOURCE STRING FOR THE
** SUBSTRING BY MOVING THE CHARACTERS FROM THE END
** OF THE SOURCE STRING DOWN TO INDEX, UP BY
** THE SIZE OF THE STRING.
*
:LENOK
*
** CALC NUMBER OF CHARS TO MOVE
** COUNT = STR LEN - START INDEX + 1
*
LDA :S1LEN
SEC
SBC :SINDEX
TAX
INX ; X= NUM OF CHARS TO MOV
*
** SET THE SOURCE INDEX AND CALC DEST INDEX
*
LDA :S1LEN
STA :SIDX ; SRC ENDS AT ORIG STR END
CLC
ADC :S2LEN
STA :SBIDX ; DEST ENDS FURTHER BY SUB LEN
STA :S1LEN ; SET NEW LENGTH TO THIS ALSO
*
:OPNLP
LDY :SIDX
LDA (ADDR1),Y
LDY :SBIDX
STA (ADDR1),Y ; MOVE IT UP IN MEM
DEC :SIDX
DEC :SBIDX ; DE DEST IDX, COUNTER
DEX
BNE :OPNLP ; CONT UNTIL COUNTER = 0
*
** MOVE THE SUBSTR INTO THE OPEN AREA
*
:MVESUB
LDA #1
STA :SIDX
; START AT ONE IN THE SUBSTR
; START AT INDEX IN THE STRING
LDX :S2LEN ; X = NUM OF CHARS TO MOVE
*
:MVELP
LDY :SIDX
LDA (ADDR2),Y ; GET NEXT CHAR
LDY :SINDEX
STA (ADDR1),Y
INC :SIDX ; INC SUBSTR INDEX
INC :SINDEX ; INC STR INDEX
DEX ; DEC COUNTER
BNE :MVELP ; CONT UNTIL COUNTER = 0
LDA :SCERR ; GET ERROR FLAG
BNE :EREXIT ; BR IF SUBSTR WAS TRUNCED
*
:OKEXIT
CLC
BCC :EXIT
:EREXIT
SEC ; ERROR EXIT
:EXIT
LDA :S1LEN
LDY #0
STA (ADDR1),Y
RTS
*
** DATA
*
:S1LEN DS 1
:S2LEN DS 1
:SUBLEN DS 1
:MLEN DS 1
:SINDEX DS 1
:SIDX DS 1
:SBIDX DS 1
:SCERR DS 1
*
SUBSTRINGSX
*