AppleIIAsm-Collection/documentation/AppleIIAsm Library Collecti.../0.6.1/34.0 Detailed_Reference_D5_...

107 KiB

Disk 5: Strings and Substrings


Part I: The Strings Collection

The fifth disk of the AppleIIAsm library is dedicated to manipulating strings, with a substantial portion of the macros and subroutines dealing with substring functions. Since roughly half of these deal with whole strings and half deal with substrings, the strings collection is further divided as such in terms of both macros and demos: whole string functions and substring functions. This collection includes macros and substrings for the following:

  • Finding a substring position
  • Copying a substring
  • Deleting a substring
  • Inserting a substring
  • Finding a tokenized substring
  • Counting the number of tokens in a string
  • Comparing strings
  • Concatenating strings
  • Trimming strings
  • String conversion to uppercase
  • String conversion to lowercase
  • String reversal
  • Sentence-based capitalization

Strings Components

The Strings collection contains the following components:

  • A header file that includes a number of hooks and vectors to be used by the macros and subroutines in the collection.
  • Macros and subroutines used to manipulate strings and substrings in variety of ways. These can be further paired for greater functionality, for instance by using SDEL and SINS to write a substring replacing routine. Currently, the macros are divided into two files: MAC.STRINGS.ASM for whole string operations and MAC.SUBSTRINGS.ASM for substring operations.
  • Demonstrations of all of the macros in the strings collection. This is divided into two files, the DEMO.STRINGS.ASM file and the DEMO.SUBSTRINGS.ASM file.

Header File

Condition Value
Name File: HEAD.STRINGS.ASM
Type Header File
Author Nathan Riggs
Last Revision 03-APR-2021
Assembler Merlin 8 Pro
OS Apple DOS 3.3
Purpose Provide appropriate hooks and routines for the Strings collection
Dependencies none
Bytes 0
Notes Repeatedly used subroutines by the library may be placed here in the future
See Also none

DETAILS

Currently, the Strings Collection header file only includes a single vector, which serves as an alternative entry point to COUT.

LISTING 5.00: HEAD.STRINGS.ASM Source

*
*``````````````````````````````*
* HOOKS.STRINGS                *
*                              *
* THIS FILE CONTAINS ALL OF    *
* THE HOOKS REQUIRED BY THE    *
* STRING LIBRARY.              *
*                              *
* AUTHOR:    NATHAN RIGGS      *
* CONTACT:   NATHAN.RIGGS@     *
*            OUTLOOK.COM       *
*                              *
* DATE:      19-SEP-2019       *
* ASSEMBLER: MERLIN 8 PRO      *
* OS:        DOS 3.3           *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
SCOUT1   EQU   $FDF0
*


Macros and Subroutines

The Strings Collection is further divided into two basic sections: functions for manipulating whole strings and those used for manipulating substrings. By combining the various macros provided, more complex (and more contemporary) routines may be created, such as a substring replace function, a substring search function, and so on.


Whole String Macros

The macros contained here are dedicated to operations on whole strings. Technically, of course, all strings are whole strings; what is meant by "whole string operation" here is that the entire string being operated on is passed back after the macro is called, albeit transformed in some way.

LISTING 5.10: MAC.STRINGS.ASM Header Source

*
*``````````````````````````````*
* MAC.STRINGS                  *
*                              *
* THIS FILE CONTAINS ALL OF    *
* THE MACROS RELATED TO STRING *
* MANIPULATION.                *
*                              *
* AUTHOR:    NATHAN RIGGS      *
* CONTACT:   NATHAN.RIGGS@     *
*            OUTLOOK.COM       *
*                              *
* DATE:      11-APR-2021       *
* ASSEMBLER: MERLIN 8 PRO      *
* OS:        DOS 3.3           *
*                              *
* SUBROUTINE FILES USED        *
*                              *
* SUB.STRCAT.ASM               *
* SUB.STRCOMP.ASM              *
* SUB.STRTRIM.ASM              *
* SUB.STRUPPER.ASM             *
* SUB.STRLOWER.ASM             *
* SUB.STRREV.ASM               *
* SUB.STRCAP.ASM               *
*                              *
* LIST OF MACROS               *
*                              *
* SCMP : STRING COMPARE        *
* SCAT : STRING CONCATENATE    *
* STRIM: STRING TRIM           *
* SUP  : STRING UPPERCASE      *
* SLO  : STRING LOWERCASE      *
* SREV :  STRING REVERSE       *
* SCAP : STRING CAPITALIZATION *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*


THE SCMP MACRO

SUMMARY

Condition Value
Name SCMP
Type Macro
File MAC.STRINGS.ASM
Author Nathan Riggs
Last Revision 12-APR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Compare two strings
Input ]1 = First string to compare
]2 = Second string to compare
Output none
Dependencies SUB.STRCOMP.ASM
Flags Destroyed NZCV
Cycles 110+
Bytes 67 bytes
Notes None
See Also none

DETAILS

The SCMP macro accepts two strings as input and compares them, returning the results via the status register as a regular CMP instruction would do. In particular, the zero flag and the carry flag are set or unset based on the comparison of the strings as follows:

  • If the strings match, then the zero flag is set to one
  • If the strings do not match, then the zero flag is set to zero
  • If the strings do match up to the length of the shortest string, then a further comparison is done to test the lengths. Then:
    • The carry flag is set to zero if the first string length is less than the second string length
    • The carry flag is set to one if the first string length is greater than or equal to the length of the second string

LISTING 5.11: The SCMP Macro Source

*
*``````````````````````````````*
* SCMP          (NATHAN RIGGS) *
*                              *
* COMPARES TWO STRINGS AND     *
* CHANGES THE ZERO FLAG TO 1   *
* IF THE STRINGS ARE EQUAL. IF *
* UNEQUAL, THE MACRO THEN      *
* COMPARES THE LENGTHS; IF THE *
* FIRST IS LESS THAN SECOND,   *
* THE CARRY FLAG IS SET TO 0.  *
* OTHERWISE, IT IS SET TO 1.   *
*                              *
* PARAMETERS                   *
*                              *
*  ]1 = 1ST STRING TO COMPARE  *
*  ]2 = 2ND STRING TO COMPARE  *
*                              *
* CYCLES: 110+                 *
* SIZE: 67 BYTES               *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
SCMP     MAC
         STY   SCRATCH    ; {3C2B} BACKUP .Y REGISTER
         _MSTR ]1;WPAR1   ; {15C14B}
         _MSTR ]2;WPAR2   ; {15C14B}
         JSR   STRCMP     ; {74C26B}
         LDY   SCRATCH    ; {3C2B} RELOAD .Y
         <<<
*


THE STRCOMP SUBROUTINE

SUMMARY

Condition Value
Name STRCOMP
Type Subroutine
File SUB.STRCOMP.ASM
Author Nathan Riggs
Last Revision 12-APR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Compare two strings
Input WPAR1 = First string
WPAR2 = Second strings
Output none
Dependencies none
Flags Destroyed NZCV
Cycles 68+
Bytes 32
Notes none
See Also SCMP

DETAILS

The STRCOMP subroutine is provided the addresses of two strings with a preceding length-byte, then runs a comparison on them. The results are passed back via the Carry and Zero flags of the status register, as such:

  • If the strings match, then the zero flag is set to one

  • If the strings do not match, then the zero flag is set to zero

  • If the strings do match up to the length of the shortest string, then a further comparison is done to test the lengths. Then:

    • The carry flag is set to zero if the first string length is less than the second string length
    • The carry flag is set to one if the first string length is greater than or equal to the length of the second string

LISTING 5.12: The STRCOMP Subroutine Source

*``````````````````````````````*
* STRCMP        (NATHAN RIGGS) *
*                              *
* COMPARES A STRING TO ANOTHER *
* STRING AND SETS THE FLAGS    *
* ACCORDINGLY:                 *
*                              *
* Z = 1 IF STRINGS MATCH       *
* Z = 0 IF STRINGS DON'T MATCH *
*                              *
* IF THE STRINGS MATCH UP TO   *
* THE LENGTH OF THE SHORTEST   *
* STRING, THE STRING LENGTHS   *
* ARE THEN COMPARED AND THE    *
* CARRY FLAG IS SET AS SUCH:   *
*                              *
* C = 0 IF 1ST STRING < 2ND    *
* C = 1 IF 1ST STRING >= 2ND   *
*                              *
* INPUT:                       *
*                              *
*  WPAR1 = 1ST STRING ADDRESS  *
*  WPAR2 = 2ND STRING ADDRESS  *
*                              *
* DESTROY: NZCIDV              *
*          ^^^  ^              *
*                              *
* CYCLES: 68+                  *
* SIZE: 32 BYTES               *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]STR1    EQU   WPAR1      ; ZP POINTER TO 1ST STRING
]STR2    EQU   WPAR2      ; ZP POINTER TO 2ND STRING
*
STRCMP
*
         LDY   #0         ; {3C2B} RESET .Y COUNTER
         LDA   (]STR1),Y  ; {5C2B} GET LENGTH OF 1ST STRING
         CMP   (]STR2),Y  ; {5C2B} IF STR1 LENGTH < STR2 LENGTH
         BCC   :BEGCMP    ; {3C2B} THEN BEGIN COMPARISON; ELSE
         LDA   (]STR2),Y  ; {5C2B} USE STR2 LENGTH INSTEAD
:BEGCMP
         TAX              ; {2C1B} X IS LENGTH OF SHORTER STRING
         BEQ   :TSTLEN    ; {3C2B} IF LENGTH IS 0, TEST LENGTH
         LDY   #1         ; {3C2B} ELSE SET .Y TO 1ST CHAR
:CMPLP
         LDA   (]STR1),Y  ; {5C2B} GET INDEXED CHAR OF 1ST STRING
         CMP   (]STR2),Y  ; {5C2B} COMPARE TO INDEXED CHAR OF 2ND
         BNE   :EXIT      ; {3C2B} EXIT IF THE CHARS ARE NOT EQUAL
                          ; Z,C WILL BE PROPERLY SET
         INY              ; {2C1B} INCREASE CHARACTER INDEX
         DEX              ; {2C1B} DECREMENT COUNTER
         BNE   :CMPLP     ; {3C2B} CONT UNTIL ALL CHARS CHECKED
:TSTLEN
         LDY   #0         ; {3C2B} NOW COMPARE LENGTHS
         LDA   (]STR1),Y  ; {5C2B} GET LENGTH OF 1ST STRING
         CMP   (]STR2),Y  ; {5C2B} SET OR CLEAR THE FLAGS
:EXIT
         RTS              ; {6C1B}


THE SCAT MACRO

SUMMARY

Condition Value
Name SCAT
Type Macro
File MAC.STRINGS.ASM
Author Nathan Riggs
Last Revision 12-APR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Concatenate one string to another
Input ]1 = First string
]2 = Second string
Output none
Dependencies SUB.STRCAT.ASM
Flags Destroyed NZCV
Cycles 170+
Bytes 113 bytes
Notes None
See Also STRCAT

DETAILS

The STRCAT macro concatenates two strings, with the string at the second given address attached to the end of the string at the first given address. Like with all of the whole string macros, a new copy of the concatenated strings is passed back via RETURN, with its length in RETLEN. The length is also available in the **.A ** register.

LISTING 5.13: The STRCAT Macro Source

*
*``````````````````````````````*
* SCAT          (NATHAN RIGGS) *
*                              *
* CONCATENATE TWO STRINGS      *
*                              *
* PARAMETERS                   *
*                              *
*  ]1 = FIRST STRING           *
*  ]2 = SECOND STRING          *
*                              *
* CYCLES: 170+                 *
* SIZE: 113 BYTES              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
SCAT     MAC
         STY   SCRATCH    ; {3C2B} BACKUP .Y
         _MSTR ]1;WPAR1   ; {15C14B}
         _MSTR ]2;WPAR2   ; {15C14B}
         JSR   STRCAT     ; {134C81B}
         LDY   SCRATCH    ; {3C2B} RESTORE .Y
         <<<
*


THE STRCAT SUBROUTINE

SUMMARY

Condition Value
Name STRCAT
Type Subroutine
File SUB.STRCAT.ASM
Author Nathan Riggs
Last Revision 12-APR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Compare two strings
Input WPAR1 = First string
WPAR2 = Second string
Output none
Dependencies none
Flags Destroyed NZCV
Cycles 128+
Bytes 78
Notes none
See Also SCAT

DETAILS

The STRCAT subroutine appends (or concatenates) a given second string to a first string. The resulting new string is stored in RETURN, with the length stored in RETLEN.

LISTING 5.14: The STRCAT Subroutine Source

*``````````````````````````````*
* STRCAT        (NATHAN RIGGS) *
*                              *
* CONCATENATE TWO STRINGS AND  *
* STORE THE NEW STRING IN      *
* RETURN, WITH THE LENGTH BYTE *
* AT RETLEN.                   *
*                              *
* NOTE THAT THE WHOLE STRING   *
* IS ACTUALLY PLACED IN RETLEN *
* TO ACCOUNT FOR THE LENGTH    *
* BYTE THAT PRECEDES IT.       *
*                              *
* INPUT:                       *
*                              *
*  WPAR1 = 1ST STRING ADDRESS  *
*  WPAR2 = 2ND STRING ADDRESS  *
*                              *
* DESTROY: NZCIDV              *
*          ^^^  ^              *
*                              *
* CYCLES: 128+                 *
* SIZE: 78 BYTES               *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]S1LEN   EQU   VARTAB+1   ; FIRST STRING LENGTH
]S2LEN   EQU   VARTAB+3   ; SECOND STRING LENGTH
]INDEX   EQU   WPAR3      ; ADDRESS TO PLACE 2ND STRING
]STR2    EQU   WPAR2      ; POINTER TO 2ND STRING
]STR1    EQU   WPAR1      ; POINTER TO 1ST STRING
*
STRCAT
*
         LDY   #0         ; {3C2B} CLEAR INDEX POINTER
         LDA   (]STR1),Y  ; {5C2B} GET LENGTH OF 1ST STRING
         STA   ]S1LEN     ; {4C3B} STORE IN 1ST STRING LENGTH
         LDA   (]STR2),Y  ; {5C2B} GET LENGTH OF 2ND STRING
         STA   ]S2LEN     ; {4C3B} STORE 2ND STRING LENGTH
*
** DETERMINE NUMBER OF CHAR
*
         LDA   ]S2LEN     ; {4C3B} GET 2ND STRING LENGTH
         CLC              ; {2C1B} CLEAR CARRY
         ADC   ]S1LEN     ; {4C3B} ADD TO LENGTH OF 1ST STRING
         STA   RETLEN     ; {4C3B} SAVE SUM OF TWO LENGTHS
         BCC   :DOCAT     ; {3C2B} NO OVERFLOW, JUST CONCATENATE
         LDA   #255       ; {3C2B} OTHERWISE, 255 IS MAX
         STA   RETLEN     ; {4C3B}
*
:DOCAT
*
         LDY   #0         ; {4C3B} OFFSET INDEX BY 1
:CAT1
         INY              ; {2C1B}
         LDA   (]STR1),Y  ; {5C2B} LOAD 1ST STRING INDEXED CHAR
         STA   RETLEN,Y   ; {5C2B} STORE IN RETURN AT SAME INDEX
         CPY   ]S1LEN     ; {4C3B} IF .Y < 1ST STRING LENGTH
         BNE   :CAT1      ; {3C2B} THEN LOOP UNTIL FALSE
*
         TYA              ; {2C1B} TRANSFER COUNTER TO .A
         CLC              ; {2C1B} CLEAR CARRY
         ADC   #<RETLEN   ; {4C3B} ADD LOW BYTE OF RETLEN ADDRESS
         STA   ]INDEX     ; {4C3B} STORE AS NEW ADDRESS LOW BYTE
         LDA   #0         ; {3C2B} NOW ADJUST HIGH BYTE
         ADC   #>RETLEN+1 ; {4C3B} OF NEW ADDRESS
         STA   ]INDEX+1   ; {4C3B} AND STORE HIGH BYTE
         CLC              ; {2C1B} RESET CARRY
         LDY   #0         ; {3C2B}
*
:CAT2
         INY              ; {2C1B}
         LDA   (]STR2),Y  ; {5C2B} LOAD 2ND STRING INDEXED CHAR
         STA   (]INDEX),Y ; {5C2B} STORE AT NEW ADDRESS
         CPY   RETLEN     ; {4C3B} IF .Y < 2ND STRING LENGTH
         BEQ   :EXIT      ; {3C2B}
         BNE   :CAT2      ; {3C2B} LOOP UNTIL FALSE
:EXIT
         LDA   RETLEN     ; {4C3B} RETURN NEW LENGTH IN .A
         RTS              ; {6C2B}


THE STRIM MACRO

SUMMARY

Condition Value
Name STRIM
Type Macro
File MAC.STRINGS.ASM
Author Nathan Riggs
Last Revision 12-APR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Trim a token from the beginning and
end of a string
Input ]1 = String
]2 = Token
Output none
Dependencies SUB.STRTRIM.ASM
Flags Destroyed NZCV
Cycles 123+
Bytes 80
Notes None
See Also STRTRIM

DETAILS

The STRIM macro trims a single character (like a space) from the beginning and end of a string, if said character is present on either end. The new string is stored in RETURN with its length in RETLEN, which is also passed back via the .A register.

LISTING 5.15: The STRIM Macro Source

*
*``````````````````````````````*
* STRIM         (NATHAN RIGGS) *
*                              *
* CONCATENATE TWO STRINGS      *
*                              *
* PARAMETERS                   *
*                              *
*  ]1 = STRING ADDRESS         *
*  ]2 = DELIMITER              *
*                              *
* CYCLES: 123+                 *
* SIZE: 80 BYTES               *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
STRIM    MAC
         _MSTR ]1;WPAR1   ; {15C14B}
         LDA   ]2         ; {4C3B}
         STA   WPAR2      ; {3C2B}
         JSR   STRTRIM    ; {101C61B}
         <<<
*


THE STRTRIM SUBROUTINE

SUMMARY

Condition Value
Name STRTRIM
Type Subroutine
File SUB.STRTRIM.ASM
Author Nathan Riggs
Last Revision 12-APR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Compare two strings
Input WPAR1 = String
WPAR2 = Token
Output none
Dependencies none
Flags Destroyed NZCV
Cycles 95+
Bytes 58
Notes none
See Also STRIM

DETAILS

The STRTRIM subroutine trims a specified token from the beginning and end of a string at a given address. Once executed, the new string is held in RETURN, with its length held in RETLEN. This length is also passed back via the .A register.

LISTING 5.16: The STRTRIM Subroutine Source

*``````````````````````````````*
* STRTRIM       (NATHAN RIGGS) *
*                              *
* STRTRIM CUTS THE SPECIFIED   *
* CHARACTER FROM THE FRONT AND *
* END OF THE GIVEN STRING, IF  *
* IT EXISTS.                   *
*                              *
* INPUT:                       *
*                              *
*  WPAR1 = STRING ADDRESS      *
*  WPAR2 = DELIMITER           *
*                              *
* DESTROY: NZCIDV              *
*          ^^^  ^              *
*                              *
* CYCLES: 95+                  *
* SIZE: 58 BYTES               *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]STR     EQU   WPAR1      ; STRING ADDRESS
]DELIM   EQU   WPAR2      ; DELIMITER
]RET     EQU   WPAR3      ; RETURN AREA
]LEN     EQU   BPAR1      ; STRING LENGTH
*
STRTRIM
         LDY   #0         ; {3C2B} RESET .Y COUNTER
         LDA   (]STR),Y   ; {5C3B} GET STRING LENGTH
         STA   ]LEN       ; {3C2B} STORE IN TEMP VARIABLE
         TYA              ; {2C1B} RESET .A
         TAX              ; {2C1B} RESET .X
:FIRST
         LDY   #1         ; {3C2B} LOAD FIRST CHAR OF STRING
         LDA   (]STR),Y   ; {5C3B}
         CMP   ]DELIM     ; {3C2B} COMPARE TO THE DELIMITER
         BNE   :NOFIRST   ; {3C2B} IF NO MATCH, SKIP TO :NOFIRST
         LDX   #255       ; {3C2B} IF MATCHED, SET X TO -1
         LDY   #1         ; {3C2B} AND SET Y TO 1
         JMP   :COPY      ; {3C3B} JUMP OVER TO COPYING
:NOFIRST
         LDX   #255       ; {3C2B} SET .X TO -1
         LDY   #0         ; {3C2B} SET .Y TO 0
:COPY
         INY              ; {2C1B} INCREASE .Y
         INX              ; {2C1B} INCREASAE .X
         LDA   (]STR),Y   ; {5C3B} LOAD CHAR FROM STRING AT .Y
         STA   RETURN,X   ; {5C3B} STORE IN RETURN AT .X
         CPY   ]LEN       ; {3C2B} IF .Y != STRING LENGTH
         BNE   :COPY      ; {3C2B} THEN RELOOP COPYING
:LAST
         LDY   ]LEN       ; {3C2B} LOAD LENGTH INTO .Y
         LDA   (]STR),Y   ; {5C3B} LOAD LAST CHAR OF STRING
         CMP   ]DELIM     ; {3C2B} IF NOT EQUAL TO DELIMITER
         BNE   :EXIT      ; {3C2B} THEN GOTO EXIT
         DEC   ]LEN       ; {2C1B} OTHERWISE, DECREASE LENGTH
:EXIT
         LDY   ]LEN       ; {3C2B} LOAD (POSSIBLY) ALTERED LENGTH
         STY   RETLEN     ; {4C3B} STORE IN RETLEN
         TYA              ; {2C1B} PASS LENGTH IN .A REGISTER
         RTS              ; {6C1B}


THE STRUP MACRO

SUMMARY

Condition Value
Name STRUP
Type Macro
File MAC.STRINGS.ASM
Author Nathan Riggs
Last Revision 12-APR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Turn a string into its
uppercase equivalent.
Input ]1 = String
Output none
Dependencies SUB.STRUPPER.ASM
Flags Destroyed NZCV
Cycles 114+
Bytes 64
Notes None
See Also SUB.STRUPPER.ASM

DETAILS

The STRUP macro accepts a string or address to a string and transforms it into a string that represents its uppercase equivalent. This new string is passed back in RETURN, with the length held in RETLEN. The length is also available in the .A register.

LISTING 5.17: The STRUP Macro Source

*
*``````````````````````````````*
* STRUP         (NATHAN RIGGS) *
*                              *
* CONVERTS A STRING TO ITS     *
* UPPERCASE EQUIVALENT.        *
*                              *
* PARAMETERS                   *
*                              *
*  ]1 = STRING ADDRESS         *
*                              *
* CYCLES: 114+                 *
* SIZE: 64 BYTES               *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
STRUP    MAC
         _MSTR ]1;WPAR1   ; {15C14B}
         JSR   STRUPPER   ; {99C50B}
         <<<
*


THE STRUPPER SUBROUTINE

SUMMARY

Condition Value
Name STRUPPER
Type Subroutine
File SUB.STRUPPER.ASM
Author Nathan Riggs
Last Revision 12-APR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Convert a string to uppercase
Input WPAR1 = String address
Output none
Dependencies none
Flags Destroyed NZCV
Cycles 93+
Bytes 47
Notes none
See Also STRUP

DETAILS

The STRUPPER subroutine accepts a string address and passes back a string via RETURN that is an all-uppercase equivalent of the string, with its length stored in both RETLEN and in the .A register.

LISTING 5.18: The STRUPPER Subroutine Source

*``````````````````````````````*
* STRUPPER      (NATHAN RIGGS) *
*                              *
* THIS SUBROUTINE ACCEPTS A    *
* STRING AND PASSES BACK A NEW *
* STRING THAT IS ITS UPPERCASE *
* EQUIVALENT.                  *
*                              *
* INPUT:                       *
*                              *
*  WPAR1 = STRING ADDRESS      *
*                              *
* DESTROY: NZCIDV              *
*          ^^^  ^              *
*                              *
* CYCLES: 93+                  *
* SIZE: 47 BYTES               *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]STR     EQU   WPAR1      ; STRING ADDRESS
]LEN     EQU   BPAR1      ; STRING LENGTH
]RET     EQU   WPAR2      ; REUTURN ADDRESS
*
STRUPPER
*
         LDA   #0         ; {3C2B} RESET .A REGISTER
         TAX              ; {2C1B} RESET .X
         TAY              ; {2C1B} RESET .Y
         LDA   (]STR),Y   ; {5C2B} GET THE STRING LENGTH
         STA   ]LEN       ; {3C2B} AND STORE IN ]LEN
         INC   ]LEN       ; {5C2B} TEMPORARILY INCREASE LENGTH
         LDY   #255       ; {3C2B} COUNTER = -1
:COPYLP
         INY              ; {2C1B} INCREASE INDEX COUNTER
         LDA   (]STR),Y   ; {5C2B} LOAD CHARACTER FROM STRING
         STA   RETLEN,Y   ; {5C2B} STORE IN RETURN AREA AT INDEX
         CPY   ]LEN       ; {3C2B} IF .Y != STRING LENGTH
         BNE   :COPYLP    ; {3C2B} KEEP LOOPING UNTIL ALL COPIED
*
         LDY   #0         ; {3C2B} RESET INDEX COUNTER
         DEC   ]LEN       ; {5C2B} LENGTH BACK TO NORMAL
:MAINLP
         INY              ; {2C1B} INCREASE INDEX COUNTER
         LDA   RETLEN,Y   ; {5C2B} LOAD CHARACTER FROM RETURN
         CMP   #225       ; {3C2B} IS IT < LOWERCASE A?
         BCC   :RELOOP    ; {3C2B} IF SO, LOOP TO NEXT CHAR
         CMP   #251       ; {3C2B} IS IT >= LOWERCASE Z?
         BCS   :RELOOP    ; {3C2B} IF SO, LOOP TO NEXT CHAR
         SEC              ; {2C1B} ELSE, WE FOUND A LOWERCASE LETTER
         SBC   #32        ; {3C2B} SO SUBTRACT 32 TO GET UPPERCASE
         STA   RETLEN,Y   ; {5C2B} AND REPLACE THE LETTER IN RETURN
:RELOOP  CPY   ]LEN       ; {3C2B} NOW CHECK IF Y = LENGTH
         BNE   :MAINLP    ; {3C2B} AND IF NOT, CONTINUE LOOP
:EXIT
         TYA              ; {2C1B} SEND BACK LENGTH IN .A
         RTS              ; {6C1B}


THE SLO MACRO

SUMMARY

Condition Value
Name SLO
Type Macro
File MAC.STRINGS.ASM
Author Nathan Riggs
Last Revision 12-APR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Turn a string into its
lowercase equivalent.
Input ]1 = String
Output none
Dependencies SUB.STRLOWER.ASM
Flags Destroyed NZCV
Cycles 113+
Bytes 64
Notes None
See Also SUB.STRLOWER.ASM

DETAILS

The STRLO macro accepts a string or a string address and returns the lowercase equivalent of the string in RETURN, with its length passed back in both RETLEN and the .A register.

LISTING 5.19: The SLO Macro Source

*
*``````````````````````````````*
* STRLO         (NATHAN RIGGS) *
*                              *
* CONVERTS A STRING TO ITS     *
* LOWERCASE EQUIVALENT.        *
*                              *
* PARAMETERS                   *
*                              *
*  ]1 = STRING ADDRESS         *
*                              *
* CYCLES: 113+                 *
* SIZE: 64 BYTES               *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
STRLO    MAC
         _MSTR ]1;WPAR1   ; {15C14B}
         JSR   STRLOWER   ; {98C50B}
         <<<
*

THE STRLOWER SUBROUTINE

SUMMARY

Condition Value
Name STRLOWER
Type Subroutine
File SUB.STRLOWER.ASM
Author Nathan Riggs
Last Revision 12-APR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Convert a string to lowercase
Input WPAR1 = String address
Output none
Dependencies none
Flags Destroyed NZCV
Cycles 92+
Bytes 47
Notes none
See Also SLO

DETAILS

The STRLOWER subroutine accepts an address that holds a string and converts the string to its lowercase equivalent, storing the new string in RETURN with its preceding length-byte in RETLEN. The length is also passed back via the .A register.

LISTING 5.20: The STRLOWER Subroutine Source

*``````````````````````````````*
* STRLOWER      (NATHAN RIGGS) *
*                              *
* THIS SUBROUTINE ACCEPTS A    *
* STRING AND PASSES BACK A NEW *
* STRING THAT IS ITS LOWERCASE *
* EQUIVALENT.                  *
*                              *
* INPUT:                       *
*                              *
*  WPAR1 = STRING ADDRESS      *
*                              *
* DESTROY: NZCIDV              *
*          ^^^  ^              *
*                              *
* CYCLES: 92+                  *
* SIZE: 47 BYTES               *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]STR     EQU   WPAR1      ; STRING ADDRESS
]LEN     EQU   BPAR1      ; STRING LENGTH
]RET     EQU   WPAR2      ; REUTURN ADDRESS
*
STRLOWER
*
         LDA   #0         ; {3C2B} RESET .A REGISTER
         TAX              ; {2C1B} RESET .X
         TAY              ; {2C1B} RESET .Y
         LDA   (]STR),Y   ; {5C2B} GET THE STRING LENGTH
         STA   ]LEN       ; {3C2B} AND STORE IN ]LEN
         INC   ]LEN       ; {5C2B} TEMPORARILY INCREASE LENGTH
         LDY   #255       ; {3C2B} COUNTER = -1
:COPYLP
         INY              ; {2C1B} INCREASE INDEX COUNTER
         LDA   (]STR),Y   ; {5C2B} LOAD CHARACTER FROM STRING
         STA   RETLEN,Y   ; {5C2B} STORE IN RETURN AREA AT INDEX
         CPY   ]LEN       ; {3C2B} IF .Y != STRING LENGTH
         BNE   :COPYLP    ; {3C2B} KEEP LOOPING UNTIL ALL COPIED
*
         LDY   #0         ; {3C2B} RESET INDEX COUNTER
         DEC   ]LEN       ; {5C2B} LENGTH BACK TO NORMAL
:MAINLP
         INY              ; {2C1B} INCREASE INDEX COUNTER
         LDA   RETLEN,Y   ; {5C2B} LOAD CHARACTER FROM RETURN
         CMP   #193       ; {3C2B} IS IT < UPPERCASE A?
         BCC   :RELOOP    ; {3C2B} IF SO, LOOP TO NEXT CHAR
         CMP   #219       ; {3C2B} IS IT >= UPPERCASE Z?
         BCS   :RELOOP    ; {3C2B} IF SO, LOOP TO NEXT CHAR
         CLC              ; {2C1B} ELSE, FOUND AN UPPERCASE LETTER
         ADC   #32        ; {3C2B} SO ADD 32 TO GET LOWERCASE
         STA   RETLEN,Y   ; {5C2B} AND REPLACE THE LETTER IN RETURN
:RELOOP  CPY   ]LEN       ; {3C2B} NOW CHECK IF Y = LENGTH
         BNE   :MAINLP    ; {3C2B} AND IF NOT, CONTINUE LOOP
:EXIT
         TYA              ; {2C1B} SEND BACK LENGTH IN .A
         RTS              ; {6C1B}


THE SREV MACRO

SUMMARY

Condition Value
Name SREV
Type Macro
File MAC.STRINGS.ASM
Author Nathan Riggs
Last Revision 12-APR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Reverse a string.
Input ]1 = String
Output none
Dependencies SUB.STRREV.ASM
Flags Destroyed NZCV
Cycles 70+
Bytes 41
Notes None
See Also SUB.STRREV.ASM

DETAILS

The SREV macro accepts a string or a string's address and passes back a copy of that string in reverse. This new string is passed back via RETURN, with the length-byte held in RETLEN. The length can also be retrieved in the .A register.

LISTING 5.21: The SREV Macro Source

*
*``````````````````````````````*
* SREV          (NATHAN RIGGS) *
*                              *
*                              *
* PARAMETERS                   *
*                              *
*  ]1 = STRING ADDRESS         *
*                              *
* CYCLES: 70+                  *
* SIZE: 41 BYTES               *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
SREV     MAC
         _MSTR ]1;WPAR1   ; {14C13B}
         JSR   STRREV     ; {56C28B}
         <<<


THE STRREV SUBROUTINE

SUMMARY

Condition Value
Name STRREV
Type Subroutine
File SUB.STRREV.ASM
Author Nathan Riggs
Last Revision 12-APR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose reverse a string
Input WPAR1 = String address
Output none
Dependencies none
Flags Destroyed NZCV
Cycles 50+
Bytes 25
Notes none
See Also SREV

DETAILS

The STRREV subroutine accepts a string address and then passes back a copy of the string reversed in RETURN. The length is held in both RETLEN and in the .A register.

LISTING 5.22: The STRREV Subroutine Source

*``````````````````````````````*
* STRREV        (NATHAN RIGGS) *
*                              *
* REVERSE A STRING.            *
*                              *
* INPUT:                       *
*                              *
*  WPAR1 = STRING ADDRESS      *
*                              *
* DESTROY: NZCIDV              *
*          ^^^  ^              *
*                              *
* CYCLES: 50+                  *
* SIZE: 25 BYTES               *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]STR     EQU   WPAR1      ; STRING ADDRESS
]REV     EQU   RETLEN     ; REVERSE STRING ADDRESS
]LEN     EQU   BPAR1      ; STRING LENGTH
*
STRREV
         LDY   #0         ; {3C2B} CLEAR .Y REGISTER
         LDX   #0         ; {3C2B} CLEAR .X REGISTER
         LDA   (]STR),Y   ; {5C2B} LOAD STRING LENGTH
         STA   ]LEN       ; {3C2B} AND STORE IN ]LEN
         TAY              ; {2C1B} TRANSFER LENGTH TO .Y INDEX
         INY              ; {2C1B} INCREASE BY ONE
*
:LP1
         INX              ; {2C1B} INCREASE .X COUNTER
         DEY              ; {2C1B} DECREASE .Y COUNTER
         LDA   (]STR),Y   ; {5C2B} LOAD CHAR FROM BACK OF STRING
         STA   ]REV,X     ; {5C2B} STORE IN REVERSE ADDRESS
         CPX   ]LEN       ; {3C2B} COMPARE .X COUNTER TO LENGTH
         BNE   :LP1       ; {3C2B} IF !=, THEN RELOOP
:EXIT
         TXA              ; {2C1B} MOVE LENGTH TO .A REGISTER
         STX   RETLEN     ; {4C3B} ALSO STORE IN RETLEN
         RTS              ; {6C1B}


THE SCAP MACRO

SUMMARY

Condition Value
Name SCAP
Type Macro
File MAC.STRINGS.ASM
Author Nathan Riggs
Last Revision 12-APR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Capitalize characters in a string
based on punctuation.
Input ]1 = String
Output none
Dependencies SUB.STRCAP.ASM
Flags Destroyed NZCV
Cycles 192+
Bytes 117
Notes None
See Also SUB.STRCAP.ASM

DETAILS

The SCAP macro accepts a string or string address and returns a copy of the string with the appropriate letters capitalized, including the first letter of the string. This can be used in conjunction with the SLO macro to get a properly formatted copy of a string that was previously in all uppercase.

The new string is passed back via RETURN with its length in RETLEN.

LISTING 5.23: The SCAP Macro Source

*
*``````````````````````````````*
* SCAP          (NATHAN RIGGS) *
*                              *
* CAPITALIZES THE APPROPRIATE  *
* CHARACTERS IN A LOWERCASE    *
* STRING.                      *
*                              *
* PARAMETERS                   *
*                              *
*  ]1 = STRING ADDRESS         *
*                              *
* CYCLES: 192+                 *
* SIZE: 117 BYTES              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
SCAP     MAC
         _MSTR ]1;WPAR1   ; {15C14B}
         JSR   STRCAP     ; {177C103B}
         <<<
*


THE STRCAP SUBROUTINE

SUMMARY

Condition Value
Name STRCAP
Type Subroutine
File SUB.STRCAP.ASM
Author Nathan Riggs
Last Revision 12-APR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose capitalize the beginning of
sentences in a string
Input WPAR1 = String
Output none
Dependencies none
Flags Destroyed NZCV
Cycles 171+
Bytes 100
Notes none
See Also SCAP

DETAILS

The STRCAP subroutine accepts an address that points to a string, then creates a copy of that string in RETURN with the first letter of every sentence capitalized, including the first letter of the string. The length is passed back via RETLEN as well as in the **.A ** register.

LISTING 5.24: The STRCAP Subroutine Source

*``````````````````````````````*
* STRCAP        (NATHAN RIGGS) *
*                              *
* THIS SUBROUTINE TAKES A      *
* STRING OF LOWERCASE LETTERS  *
* AND CAPITALIZES LETTERS WHEN *
* IT IS APPROPRIATE--AFTER A   *
* PERIOD, ETC.                 *
*                              *
* INPUT:                       *
*                              *
*  WPAR1 = STRING ADDRESS      *
*                              *
* DESTROY: NZCIDV              *
*          ^^^  ^              *
*                              *
* CYCLES: 171+                 *
* SIZE: 100 BYTES              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]STR     EQU   WPAR1      ; STRING ADDRESS
]LEN     EQU   BPAR1      ; STRING LENGTH
]FLAG    EQU   WPAR2      ; CAPITALIZATION FLAG
*
STRCAP
*
         LDA   #0         ; {3C2B} RESET .A REGISTER
         TAX              ; {2C1B} RESET .X
         TAY              ; {2C1B} RESET .Y
         STA   ]FLAG      ; {3C2B}
         LDA   (]STR),Y   ; {5C2B} GET THE STRING LENGTH
         STA   ]LEN       ; {3C2B} AND STORE IN ]LEN
         INC   ]LEN       ; {5C2B} TEMPORARILY INCREASE LENGTH
         LDY   #255       ; {3C2B} COUNTER = -1
:COPYLP
         INY              ; {2C1B} INCREASE INDEX COUNTER
         LDA   (]STR),Y   ; {5C2B} LOAD CHARACTER FROM STRING
         STA   RETLEN,Y   ; {5C2B} STORE IN RETURN AREA AT INDEX
         CPY   ]LEN       ; {3C2B} IF .Y != STRING LENGTH
         BNE   :COPYLP    ; {3C2B} KEEP LOOPING UNTIL ALL COPIED
*
         LDY   #0         ; {3C2B} RESET INDEX COUNTER
         DEC   ]LEN       ; {5C2B} LENGTH BACK TO NORMAL
:MAINLP
         INY              ; {2C1B} INCREASE INDEX COUNTER
         LDA   RETLEN,Y   ; {5C2B} GET CHARACTER FROM STRING
         CMP   #"."       ; {3C2B} IS IT A PERIOD?
         BEQ   :SETFLAG   ; {3C2B} IF SO, SET CAPITAL FLAG
         CMP   #"?"       ; {3C2B} IS IT A QUESTION MARK?
         BEQ   :SETFLAG   ; {3C2B} IF SO, SET CAPITAL FLAG
         CMP   #"!"       ; {3C2B} IS IT AN EXCLAMATION MARK?
         BEQ   :SETFLAG   ; {3C2B} IF SO, SET CAPITAL FLAG
*
         CMP   #225       ; {3C2B} COMPARE TO LOWERCASE A
         BCC   :RELOOP    ; {3C2B} IF ASCII < THAT, DO NOT REPLACE
         CMP   #251       ; {3C2B} COMPARE TO LOWERCASE Z
         BCS   :RELOOP    ; {3C2B} IF ASCII >= THAT, DON'T REPLACE
*
         LDX   ]FLAG      ; {3C2B} LOAD CAPITALIZATION FLAG
         CPX   #1         ; {3C2B} COMPARE TO #1 (SET)
         BNE   :RELOOP    ; {3C2B} IF NOT SET, LOOP TO NEXT CHAR
         SEC              ; {2C1B} IF SET, THEN SET CARRY
         SBC   #32        ; {3C2B} SUBTRACT #32 ASCII
         STA   RETLEN,Y   ; {5C2B} STORE OVER TOP LOWERCASE
         LDX   #0         ; {3C2B} CLEAR .X REGISTER
         STX   ]FLAG      ; {3C2B} AND CLEAR CAPITALIZATION FLAG
:RELOOP  CPY   ]LEN       ; {3C2B} NOW CHECK IF Y = LENGTH
         BNE   :MAINLP    ; {3C2B} AND IF NOT, CONTINUE LOOP
         JMP   :EXIT      ; {3C3B} IF SO, JUMP TO EXIT
:SETFLAG
         LDX   #1         ; {3C2B} LOAD #1 IN .X REGISTER
         STX   ]FLAG      ; {3C2B} STORE IN FLAG (MEANING IT IS SET)
         JMP   :RELOOP    ; {3C3B} LOOP TO NEXT CHARACTER
:EXIT
         LDY   #1         ; {3C2B} LOAD FIRST LETTER OF STRING
         LDA   RETLEN,Y   ; {5C2B}
         CMP   #225       ; {3C2B} COMPARE TO LOWERCASE A
         BCC   :EXIT2     ; {3C2B} IF < LC A, EXIT FOR REAL
         CMP   #251       ; {3C2B} COMPARE TO LOWERCASE Z
         BCS   :EXIT2     ; {3C2B} IF >= LC Z, EXIT FOR REAL
         SEC              ; {2C1B} OTHERWISE, SET THE CARRY
         SBC   #32        ; {3C2B} AND SUBTRACT 32 FROM ASCII
         STA   RETLEN,Y   ; {5C2B} AND STORE OVER TOP FIRST CHAR
:EXIT2
         LDA   ]LEN       ; {3C2B} SEND BACK LENGTH IN .A
         RTS              ; {6C1B}


Substring Macros

The macros contained here are dedicated to operations on substrings, which we define as strings pulled from parts of another string. Put together, the macros and subroutines available here should be enough to create more complicated routines for substring manipulation, such as searching and replacing, counting substring repetitions, and so on.

LISTING 5.30: MAC.SUBSTRINGS.ASM Header Source

*``````````````````````````````*
* MAC.SUBSTRINGS.ASM           *
*                              *
* THIS FILE CONTAINS ALL OF    *
* THE MACROS RELATED TO        *
* SUBSTRING MANIPULATION.      *
*                              *
* AUTHOR:    NATHAN RIGGS      *
* CONTACT:   NATHAN.RIGGS@     *
*            OUTLOOK.COM       *
*                              *
* DATE:      12-APR-2021       *
* ASSEMBLER: MERLIN 8 PRO      *
* OS:        DOS 3.3           *
*                              *
* SUBROUTINE FILES USED        *
*                              *
* SUB.SUBCOPY.ASM              *
* SUB.SUBDEL.ASM               *
* SUB.SUBINS.ASM               *
* SUB.SUBPOS.ASM               *
* SUB.SUBCHARCNT.ASM           *
* SUB.SUBTOK.ASM               *
*                              *
* LIST OF MACROS               *
*                              *
* SPOS : FIND SUBSTRING POS    *
* SCOP : SUBSTRING COPY        *
* SDEL : SUBSTRING DELETE      *
* SINS : SUBSTRING INSERT      *
* STOK : TOKENIZED SUBSTRING   *
* SCNT : CHARACTER COUNT       *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*


THE SPOS MACRO

SUMMARY

Condition Value
Name SPOS
Type Macro
File MAC.SUBSTRINGS.ASM
Author Nathan Riggs
Last Revision 12-APR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Find the position of a substring
in another string.
Input ]1 = Source string
]2 = Substring
Output none
Dependencies SUB.SUBPOS.ASM
Flags Destroyed NZCV
Cycles 205+
Bytes 136
Notes None
See Also SUB.SUBPOS.ASM

DETAILS

The SPOSmacro finds the position of a given substring within a larger string. The one byte index is stored in RETURN as well as in the .A register.

LISTING 5.31: The SCPOS Macro Source

*
*``````````````````````````````*
* SPOS          (NATHAN RIGGS) *
*                              *
* FIND THE POSITION OF A SUB-  *
* STRING IN A GIVEN STRING.    *
*                              *
* PARAMETERS                   *
*                              *
*  ]1 = SOURCE STRING          *
*  ]2 = SUBSTRING              *
*                              *
* CYCLES: 205+                 *
* SIZE: 136 BYTES              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
SPOS     MAC
         _MSTR ]1;WPAR2   ; {14C13B}
         _MSTR ]2;WPAR1   ; {14C13B}
         JSR   SUBPOS     ; {177C110B}
         <<<
*


THE SUBPOS SUBROUTINE

SUMMARY

Condition Value
Name SUBPOS
Type Subroutine
File SUB.SUBPOS.ASM
Author Nathan Riggs
Last Revision 12-APR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose find index of substring
Input WPAR1 = Substring address
WPAR2 = String address
Output none
Dependencies none
Flags Destroyed NZCV
Cycles 171+
Bytes 107
Notes none
See Also SPOS

DETAILS

The SUBPOS macro finds the index position of a substring within a string, both of which are pointed to by the subroutine's arguments. The position is a single byte length, and is held in RETURN and in the .A register. If no substring is found, then a value of #255 ($FF) is returned.

LISTING 5.32: The SUBPOS Subroutine Source

*``````````````````````````````*
* SUBPOS        (NATHAN RIGGS) *
*                              *
* RETURNS THE POSITION OF A    *
* SUBSTRING IN A GIVEN STRING. *
* IF NO SUBSTRING IS FOUND,    *
* THEN #255 IS PASSED BACK     *
* INSTEAD.                     *
*                              *
* INPUT:                       *
*                              *
*  WPAR1 = SUBSTRING (ADDRESS) *
*  WPAR2 = STRING ADDRESS      *
*                              *
* DESTROYS: NZCIDV             *
*           ^^^  ^             *
*                              *
* CYCLES: 171+                 *
* SIZE: 107 BYTES              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]STRIND  EQU   VARTAB     ; STRING INDEX
]SUBIND  EQU   VARTAB+1   ; SUBSTRING INDEX
]CNT     EQU   VARTAB+2   ; COUNTER
]STRLEN  EQU   VARTAB+3   ; STRING LENGTH
]SUBLEN  EQU   VARTAB+4   ; SUBSTRING LENGTH
]SIDX    EQU   VARTAB+6   ; STRING INDEX
]SUB     EQU   WPAR1      ; ZP SUBSTRING ADDR PTR
]STR     EQU   WPAR2      ; ZP STRING ADDR POINTER
*
SUBPOS
         LDY   #0         ; {3C2B} RESET INDEX COUNTER
         LDA   (]STR),Y   ; {5C2B} GET LENGTH OF STRING
         BEQ   :NOTFND    ; {3C2B} EXIT IF LENGTH = 0
         STA   ]STRLEN    ; {4C3B} STORE STRING LENGTH
         LDA   (]SUB),Y   ; {5C2B} GET SUBSTR LENGTH
         BEQ   :NOTFND    ; {3C2B} EXIT IF SUB LENGTH = 0
         STA   ]SUBLEN    ; {4C3B} STORE SUBSTRING LENGTH
         LDA   ]SUBLEN    ; {4C3B} IF SUBSTRING LENGTH IS
         CMP   ]STRLEN    ; {4C3B} > STRING LENGTH, DECLARE
         BEQ   :LENOK     ; {3C2B} THE STRING NOT FOUND
         BCS   :NOTFND    ; {3C2B} OTHERWISE, CONTINUE
:LENOK
         LDA   #1         ; {3C2B} SET STRING INDEX TO
         STA   ]STRIND    ; {4C3B} THE FIRST CHARACTER
         LDA   ]STRLEN    ; {4C3B} GET STRING LENGTH
         SEC              ; {2C1B} SET CARRY
         SBC   ]SUBLEN    ; {4C3B} SUBTRACT SUBSTRING LENGTH
         STA   ]CNT       ; {4C3B} STORE AS COUNTER
         INC   ]CNT       ; {5C2B} INCREASE BY 1
:SLP1
         LDA   ]STRIND    ; {4C3B}
         STA   ]SIDX      ; {4C3B}
         LDA   #1         ; {3C2B} START SUBSTRING INDEX
         STA   ]SUBIND    ; {4C3B} AT 1
:CMPLP
         LDY   ]SIDX      ; {4C3B} LOAD STRING INDEX TO .7
         LDA   (]STR),Y   ; {5C2B} GET NEXT CHAR FROM STR
         LDY   ]SUBIND    ; {4C3B} LOAD SUBSTRING INDEX TO .Y
         CMP   (]SUB),Y   ; {5C2B} COMPARE TO NEXT SUB CHAR
         BNE   :SLP2      ; {3C2B} NOT A MATCH; BRANCH
         CPY   ]SUBLEN    ; {4C3B} TEST IF SUB INDEX = SUB LENGTH
         BEQ   :FOUND     ; {3C2B} IF SO, FOUND THE SUBSTRING
         INY              ; {2C1B} ELSE INC TO NEXT CHAR
         STY   ]SUBIND    ; {4C3B} STORE NEW SUBSTRING INDEX
         INC   ]SIDX      ; {5C2B} INCREASE STRING INDEX
         JMP   :CMPLP     ; {3C3B} LOOP UNTIL DONE
:SLP2
         INC   ]STRIND    ; {5C2B} INCREMENT INDEX
         DEC   ]CNT       ; {5C2B} DEC COUNT
         BNE   :SLP1      ; {3C2B} LOOP BACK IF UNFINISHED
         BEQ   :NOTFND    ; {3C2B} ELSE EXIT TO NOT FOUND
:FOUND
         LDA   ]STRIND    ; {4C3B} FOUND, STORE INDEX IN .A
         JMP   :EXIT      ; {3C3B}
:NOTFND
         LDA   #255       ; {3C2B} SUB NOT FOUND, .A = 255
:EXIT
         STA   RETURN     ; {4C3B} STORE INDEX OR 255 IN RETURN
         LDY   #1         ; {3C2B} STORE BYTE LENGTH OF 1
         STY   RETLEN     ; {4C3B} INTO RETLEN
         LDY   ]SUBLEN    ; {4C3B}
         RTS              ; {6C1B}


THE SCPY MACRO

SUMMARY

Condition Value
Name SCPY
Type Macro
File MAC.SUBSTRINGS.ASM
Author Nathan Riggs
Last Revision 12-APR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Copy a substring from another string
Input ]1 = Source string
]2 = Substring index
]3 = Substring length
Output none
Dependencies SUB.SUBCOPY.ASM
Flags Destroyed NZCV
Cycles 91+
Bytes 71
Notes None
See Also SUB.SUBCOPY.ASM

DETAILS

The SCPY macro copies a substring from a larger string starting at the given index and continuing until the given length. The substring is passed back in RETURN with its length in RETLEN. Additionally, the length is passed back via the .A register.

LISTING 5.33: The SCPY Macro Source

*
*``````````````````````````````*
* SCPY          (NATHAN RIGGS) *
*                              *
* COPY SUBSTRING FROM STRING   *
*                              *
* PARAMETERS                   *
*                              *
*  ]1 = SOURCE STRING          *
*  ]2 = SUBSTRING INDEX        *
*  ]3 = SUBSTRING LENGTH       *
*                              *
* CYCLES: 91+                  *
* SIZE: 71 BYTES               *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
SCPY     MAC
         _MSTR ]1;WPAR1   ; {14C13B}
         LDA   ]2         ; {4C3B}
         STA   BPAR2      ; {3C2B}
         LDA   ]3         ; {4C3B}
         STA   BPAR1      ; {3C2B}
         JSR   SUBCOPY    ; {63C48B}
         <<<
*


THE SUBCOPY SUBROUTINE

SUMMARY

Condition Value
Name SUBCOPY
Type Subroutine
File SUB.SUBCOPY.ASM
Author Nathan Riggs
Last Revision 12-APR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose copy a substring
Input WPAR1 = Source string address
BPAR1 = Substring length
BPAR2 = Substring index
Output none
Dependencies none
Flags Destroyed NZCV
Cycles 47+
Bytes 25
Notes none
See Also SCPY

DETAILS

The SUBCOPY subroutine accepts a string address, and index and a length and then copies the part of the string that starts at the index through the length provided. The string itself is then stored in RETURN with its length in both RETLEN and the .A register.

LISTING 5.34: The SUBCOPY Subroutine Source

*``````````````````````````````*
* SUBCOPY       (NATHAN RIGGS) *
*                              *
* INPUT:                       *
*                              *
*  BPAR1 = SUBSTRING LENGTH    *
*  BPAR2 = SUBSTRING INDEX     *
*  WPAR1 = SOURCE STRING ADDR  *
*                              *
* DESTROY: NZCIDV              *
*          ^^^  ^              *
*                              *
* CYCLES: 47+                  *
* SIZE: 25 BYTES               *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]SUBLEN  EQU   BPAR1      ; SUBSTRING LENGTH
]SUBIND  EQU   BPAR2      ; SUBSTRING INDEX
]STR     EQU   WPAR1      ; SOURCE STRING
*
SUBCOPY
         LDY   ]SUBIND    ; {3C2B} STARTING COPY INDEX
         LDA   ]SUBLEN    ; {3C2B} SUBSTRING LENGTH
         STA   RETLEN     ; {4C3B} STORE SUB LENGTH IN RETLEN
         LDX   #0         ; {3C2B}
:COPY
         LDA   (]STR),Y   ; {5C2B} GET SUBSTRING CHARACTER
         STA   RETURN,X   ; {5C2B} STORE CHAR IN RETURN
         CPX   ]SUBLEN    ; {3C2B} IF .X COUNTER = SUBSTRING LENGTH
         BEQ   :EXIT      ; {3C2B} THEN FINISHED WITH LOOP
         INY              ; {2C1B} OTHERWISE, INCREMENT .Y
         INX              ; {2C1B} AND INCREMENT .X
         CLC              ; {2C1B} CLEAR CARRY FOR FORCED BRANCH
         BCC   :COPY      ; {3C2B} LOOP
:EXIT
         LDA   ]SUBLEN    ; {3C2B} RETURN SUBSTRING LENGTH IN .A
         RTS              ; {6C1B}


THE SDEL MACRO

SUMMARY

Condition Value
Name SDEL
Type Macro
File MAC.SUBSTRINGS.ASM
Author Nathan Riggs
Last Revision 12-APR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Delete a substring from a string
Input ]1 = Source string
]2 = Substring index
]3 = Substring length
Output none
Dependencies SUB.SUBDEL.ASM
Flags Destroyed NZCV
Cycles 135+
Bytes 79
Notes None
See Also SUB.SUBDEL.ASM

DETAILS

The SUBDEL macro deletes a substring from a larger string that starts at the given index and continues until the given length has been met. The new string (with no substring) is held in RETURN, with its length in both RETLEN and the .A register.

LISTING 5.35: The SUBDEL Macro Source

*
*``````````````````````````````*
* SDEL          (NATHAN RIGGS) *
*                              *
* DELETE SUBSTRING FROM STRING *
*                              *
* PARAMETERS                   *
*                              *
*  ]1 = SOURCE STRING          *
*  ]2 = SUBSTRING INDEX        *
*  ]3 = SUBSTRING LENGTH       *
*                              *
* CYCLES: 135+                 *
* SIZE: 79 BYTES               *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
SDEL     MAC
         _MSTR ]1;WPAR1   ; {14C13B}
         LDA   ]2         ; {4C3B}
         STA   BPAR2      ; {3C2B}
         LDA   ]3         ; {4C3B}
         STA   BPAR1      ; {3C2B}
         JSR   SUBDEL     ; {107C56B}
         <<<
*


THE SUBDEL SUBROUTINE

SUMMARY

Condition Value
Name SUBDEL
Type Subroutine
File SUB.SUBDEL.ASM
Author Nathan Riggs
Last Revision 12-APR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose delete a substring
Input WPAR1 = Source string address
BPAR1 = Substring length
BPAR2 = Substring index
Output none
Dependencies none
Flags Destroyed NZCV
Cycles 101+
Bytes 53
Notes none
See Also SDEL

DETAILS

The SUBDEL subroutine accepts a string address, an index and a length, then deletes a substring from the source string at the given index through to the given length. The string with the deleted substring is then held in RETURN with its length in RETLEN and the .A register.

LISTING 5.36: The SDEL Macro Source

*``````````````````````````````*
* SUBDEL        (NATHAN RIGGS) *
*                              *
* DELETE A SUBSTRING FROM A    *
* LARGER STRING.               *
*                              *
* INPUT:                       *
*                              *
*  WPAR1 = STRING ADDRESS      *
*  BPAR2 = SUBSTRING INDEX     *
*  BPAR1 = SUBSTRING LENGTH    *
*                              *
* DESTROY: NZCIDV              *
*          ^^^  ^              *
*                              *
* CYCLES: 101+                 *
* SIZE: 53 BYTES               *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]SUBLEN  EQU   BPAR1
]SUBIND  EQU   BPAR2
]STR     EQU   WPAR1
*
SUBDEL
         DEC   ]SUBIND    ; {5C2B}
         INC   ]SUBLEN    ; {5C2B}
         LDY   #0         ; {3C2B} RESET .Y INDEX
         LDA   (]STR),Y   ; {5C2B} GET STRING LENGTH
         SEC              ; {2C1B} SET CARRY
         SBC   ]SUBLEN    ; {4C3B} SUBTRACT SUBSTRING LENGTH
         STA   RETLEN     ; {4C3B} STORE NEW LENGTH IN RETLEN
         INC   RETLEN     ; {5C2B}
:LP1
         INY              ; {2C1B} INCREASE .Y INDEX
         LDA   (]STR),Y   ; {5C2B} LOAD CHARACTER FROM STRING
         STA   RETLEN,Y   ; {5C2B} STORE IN RETURN
         CPY   ]SUBIND    ; {4C3B} IF .Y != SUBSTRING INDEX
         BNE   :LP1       ; {3C2B} THEN CONTINUE LOOPING
         LDX   ]SUBIND    ; {4C3B} OTHERWISE, .X = SUB INDEX
         TYA              ; {2C1B} TRANSFER .Y INDEX TO .A
         CLC              ; {2C1B} CLEAR CARRY
         ADC   ]SUBLEN    ; {4C3B} ADD .Y TO SUBSTRING LENGTH
         TAY              ; {2C1B} FOR NEW POS, THEN BACK TO .Y
         DEX              ; {2C1B}
         DEY              ; {2C1B}
:LP2
         INY              ; {2C1B} INCREMENT .Y INDEX
         INX              ; {2C1B} INCREMEMNT .X INDEX
         LDA   (]STR),Y   ; {5C2B} GET CHAR AFTER SUBSTRING
         STA   RETURN,X   ; {5C2B} STORE IN RETURN AT .X
         CPX   RETLEN     ; {4C3B} IF .X != NEW STRING LENGTH,
         BNE   :LP2       ; {3C2B} CONTINUE LOOPING
:EXIT
         LDA   RETLEN     ; {4C3B} LOAD NEW STRING LENGTH IN .A
         RTS              ; {6C1B}
*CPY #255 ; IF AT LENGTH MAX
*BEQ :EXIT ; THEN QUIT COPYING


THE SINS MACRO

SUMMARY

Condition Value
Name SINS
Type Macro
File MAC.SUBSTRINGS.ASM
Author Nathan Riggs
Last Revision 12-APR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Insert a substring into another string
Input ]1 = Source string
]2 = Substring
]3 = Substring index
Output none
Dependencies SUB.SUBINS.ASM
Flags Destroyed NZCV
Cycles 177+
Bytes 111
Notes None
See Also SUB.SUBINS.ASM

DETAILS

The SUBINS macro inserts a given substring at a specific index in another string. This new string is then held in RETURN with the length-byte stored in RETLEN.

LISTING 5.37: The SUBINS Macro Source

*
*``````````````````````````````*
* SINS          (NATHAN RIGGS) *
*                              *
* INSERT SUBSTRING INTO STRING *
*                              *
* PARAMETERS                   *
*                              *
*  ]1 = STRING ADDRESS         *
*  ]2 = SUBSTRING ADDRESS      *
*  ]3 = SUBSTRING INDEX        *
*                              *
* CYCLES: 177+                 *
* SIZE: 111 BYTES              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
SINS     MAC
         _MSTR ]1;WPAR2   ; {14C13B}
         _MSTR ]2;WPAR1   ; {14C13B}
         LDA   ]3         ; {4C3B}
         STA   BPAR1      ; {3C2B}
         JSR   SUBINS     ; {142C82B}
         <<<
*


THE SUBINS SUBROUTINE

SUMMARY

Condition Value
Name SUBINS
Type Subroutine
File SUB.SUBINS.ASM
Author Nathan Riggs
Last Revision 12-APR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose insert a substring
Input WPAR1 = Substring address
BPAR1 = String length
BPAR2 = Insertion index
Output none
Dependencies none
Flags Destroyed NZCV
Cycles 136+
Bytes 79
Notes none
See Also SINS

DETAILS

The SUBINS subroutine accepts a substring address, a source string address and an index, then inserts the substring into a copy of the source string at the specified index. This new string copy is then stored in RETURN, with the length stored in the .A register and RETLEN.

LISTING 5.38: The SUBINS Subroutine Source

*``````````````````````````````*
* SUBINS        (NATHAN RIGGS) *
*                              *
* INSERT A SUBSTRING INTO A    *
* STRING AT A GIVEN POSITION.  *
*                              *
* INPUT:                       *
*                              *
*  WPAR1 = SUBSTRING ADDRESS   *
*  WPAR2 = STRING ADDRESS      *
*  BPAR1 = INSERTION INDEX     *
*                              *
* DESTROY: NZCIDV              *
*          ^^^  ^              *
*                              *
* CYCLES: 136+                 *
* SIZE: 79 BYTES               *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]SUB     EQU   WPAR1      ; SUBSTRING ADDRESS
]STR     EQU   WPAR2      ; STRING ADDRESS
]INDEX   EQU   BPAR1      ; STRING INDEX
]OLDIND  EQU   VARTAB     ; OLD INDEX
]TMP     EQU   VARTAB+2   ; TEMPORARY VARIABLE
]SUBLEN  EQU   VARTAB+4   ; SUBSTRING LENGTH
*
SUBINS
         DEC   ]INDEX     ; {5C2B}
         LDY   #0         ; {3C2B} SET .Y INDEX TO 0
         LDA   (]STR),Y   ; {5C2B} GET STRING LENGTH
         STA   ]TMP       ; {4C3B} TEMPORARILY STORE
         LDA   (]SUB),Y   ; {5C2B} GET SUBSTRING LENGTH
         STA   ]SUBLEN    ; {4C3B}
         CLC              ; {2C1B} CLEAR CARRY
         ADC   ]TMP       ; {4C3B} ADD SOURCE STRING LENGTH
         STA   RETLEN     ; {4C3B} STORE NEW STRING LENGTH
         BCC   :CONT      ; {3C2B} IF NO OVERFLOW, CONTINUE
         LDA   #255       ; {3C2B} ELSE, NEW STRING LENGTH IS 255
         STA   RETLEN     ; {4C3B} STORE IN RETLEN
:CONT
         LDA   ]INDEX     ; {4C3B} IF INDEX IS 0, GO STRAIGHT
         BEQ   :SUBCOPY   ; {3C2B} TO COPYING SUBSTRING FIRST
:LP1
         INY              ; {2C1B} INCREASE INDEX
         LDA   (]STR),Y   ; {5C2B} GET SOURCE STRING CHARACTER
         STA   RETLEN,Y   ; {4C3B} STORE IN RETURN
         CPY   ]INDEX     ; {3C2B} IF WE DON'T HIT SUBSTRING INDEX
         BNE   :LP1       ; {3C2B} KEEP ON COPYING
:SUBCOPY
         STY   ]OLDIND    ; {4C3B} STORE CURRENT STRING INDEX
         TYA              ; {2C1B} TRANSFER .Y COUNTER TO
         TAX              ; {2C1B} .X COUNTER TEMPORARILY
         LDY   #0         ; {3C2B} RESET .Y COUNTER
:SUBLP
         INY              ; {2C1B} INCREASE .Y SUBSTRING INDEX
         INX              ; {2C1B} CONTINUE INCREASING .X INDEX
         LDA   (]SUB),Y   ; {5C2B} LOAD INDEXED CHAR FROM SUBSTRING
         STA   RETLEN,X   ; {5C2B} STORE INTO RETURN AT INDEX
         CPY   ]SUBLEN    ; {4C3B} IF .Y != SUBSTRING LENGTH
         BNE   :SUBLP     ; {3C2B} THEN CONTINUE COPYING
         LDY   ]OLDIND    ; {4C3B} RESTORE OLD INDEX
:FINLP
         INY              ; {2C1B} INCREASE ORIGINAL INDEX
         INX              ; {2C1B} INCREASE NEW INDEX
         LDA   (]STR),Y   ; {5C2B} LOAD NEXT CHAR FROM STRING
         STA   RETLEN,X   ; {5C2B} AND STORE AFTER SUBSTRING
         CPY   ]TMP       ; {4C3B} IF ORIGINAL STRING LENGTH
         BNE   :FINLP     ; {3C2B} IS NOT YET HIT, KEEP LOOPING
:EXIT
         LDA   RETLEN     ; {4C3B} RETURN NEW LENGTH IN .A
         RTS              ; {6C1B}


THE STOK MACRO

SUMMARY

Condition Value
Name STOK
Type Macro
File MAC.SUBSTRINGS.ASM
Author Nathan Riggs
Last Revision 12-APR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Find a substring by token
Input ]1 = String
]2 = Token
]3 = Token Number
Output none
Dependencies SUB.SUBTOK.ASM
Flags Destroyed NZCV
Cycles 182+
Bytes 118
Notes None
See Also SUB.SUBTOK.ASM

DETAILS

The STOK macro searches a string for a given token and passes through the specified number of tokens before copying a substring from that token to the next token. If the token number is #0, then the substring pulled will be whatever text comes become the first token. The substring is held in RETURN, with its length in RETLEN.

Note that this macro has no protection against overflow, and thus extra care should be taken not to request a token number that extends beyond the number of tokens+1 (the extra token is due to having an implied token at the beginning of the string). The number of tokens available can be found by using the SCNT macro, which counts the number of occurrences of a character in a string.

LISTING 5.39: The STOK Macro Source

*
*``````````````````````````````*
* STOK          (NATHAN RIGGS) *
*                              *
* THIS MACRO FINDS AN ARGUMENT *
* WITHIN A STRING THAT IS      *
* SEPARATED BY TOKENS AT A     *
* GIVEN INDEX.                 *
*                              *
* PARAMETERS                   *
*                              *
*  ]1 = STRING ADDRESS         *
*  ]2 = DELIMITER / TOKEN      *
*  ]3 = ARGUMENT NUMBER        *
*                              *
* CYCLES: 182+                 *
* SIZE: 118 BYTES              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
STOK     MAC
         _MLIT ]1;WPAR1   ; {14C13B}
         LDA   ]2         ; {4C3B}
         STA   WPAR2      ; {3C2B}
         LDA   ]3         ; {4C3B}
         STA   BPAR2      ; {3C2B}
         JSR   SUBTOK     ; {154C95B}
         <<<
*


THE SUBTOK SUBROUTINE

SUMMARY

Condition Value
Name SUBTOK
Type Subroutine
File SUB.SUBTOK.ASM
Author Nathan Riggs
Last Revision 12-APR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose retrieve substring by token
Input WPAR1 = String address
WPAR2 = Token
BPAR2 = Token number
Output none
Dependencies none
Flags Destroyed NZCV
Cycles 148+
Bytes 92
Notes none
See Also STOK

DETAILS

The SUBTOK subroutine accepts a string address, a token character and a token number as parameters, then steps through the string as it counts the number of tokens. When the specified token number is found, the substring following it is copied to RETURN up until either another token is encountered or the end of the string is reached. The length of this substring is held in both RETLEN and the .A register.

Importantly, this subroutine does not protect against overflow. As such, the SCNT macro should be used to determine the number of tokens in a string if that number is not known beforehand.

LISTING 5.40: The SUBTOK Subroutine Source

*``````````````````````````````*
* SUBTOK        (NATHAN RIGGS) *
*                              *
* THIS SUBROUTINE PULLS FROM A *
* STRING OF ARGUMENTS THAT ARE *
* SEPARATED BY A DELIMITER AND *
* RETURNS THE ARGUMENT NUMBER  *
* THAT IS REQUESTED.           *
*                              *
* INPUT:                       *
*                              *
*  WPAR1 = STRING ADDRESS      *
*  WPAR2 = DELIMITER           *
*  BPAR2 = ARGUMENT INDEX      *
*                              *
* DESTROY: NZCIDV              *
*          ^^^  ^              *
*                              *
* CYCLES: 148+                 *
* SIZE: 92 BYTES               *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]STR     EQU   WPAR1      ; STRING ADDRESS
]DELIM   EQU   WPAR2      ; DELIMITER
]LEN     EQU   BPAR1      ; STRING LENGTH
]ARG     EQU   BPAR2      ; ARGUMENT NUMBER
]START   EQU   VARTAB     ; START POS OF ARGUMENT
]END     EQU   VARTAB+2   ; END POS OF ARGUMENT
]COUNT   EQU   VARTAB+4   ; ARGUMENT COUNTER
]XFLAG   EQU   VARTAB+6   ; LOOP EXIT FLAG
]FOUND   EQU   VARTAB+8   ; ARGUMENT FOUND FLAG
*
SUBTOK
         LDY   #0         ; {3C2B} CLEAR .Y REGISTER
         STY   ]COUNT     ; {4C3B} CLEAR COUNTER
         STY   ]XFLAG     ; {4C3B} CLEAR LOOP FLAG
         LDA   (]STR),Y   ; {5C2B} LOAD STRING LENGTH
         STA   ]LEN       ; {3C2B} AND STORE IN LENGTH VARIABLE
         STY   ]START     ; {4C3B} CLEAR START POS
         STY   ]END       ; {4C3B} CLEAR ENDING POS
         STY   ]FOUND     ; {4C3B} CLEAR FOUND FLAG
         TAX              ; {2C1B} CLEAR .X REGISTER
:_LP2
         INY              ; {2C1B} INCREASE INDEX
         LDA   (]STR),Y   ; {5C2B} LOAD CHARACTER FROM STRING
         CMP   ]DELIM     ; {3C2B} COMPARE TO DELIMITER
         BEQ   :CHECK     ; {3C2B} IF EQUAL, GOTO :CHECK
:RELOOP  CPY   ]LEN       ; {3C2B} ELSE, COMPARE .Y TO LENGTH
         BNE   :_LP2      ; {3C2B} IF NOT EQUAL, LOOP AGAIN
         LDX   #1         ; {3C2B} LOAD #1 IN .X
         STX   ]XFLAG     ; {4C3B} SET LOOP EXIT FLAG
:CHECK
         STY   ]END       ; {4C3B} STORE CUR INDEX TO END POS
         LDX   ]ARG       ; {3C2B} LOAD ARGUMENT NUMBER
         CPX   ]COUNT     ; {4C3B} COMPARE TO CURRENT COUNT
         BEQ   :COPY      ; {3C2B} IF EQUAL, PROCEED TO :COPY
         INC   ]COUNT     ; {5C2B} OTHERWISE, INCREASE COUNT
         STY   ]START     ; {4C3B} STORE CURRENT INDEX AS START
         JMP   :RELOOP    ; {3C3B} GOTO BEGINNING LOOP
:COPY
         LDY   ]START     ; {4C3B} LOAD STARTING POSITION IN .Y
         LDX   #255       ; {3C2B} LOAD -1 IN .X REGISTER
         LDA   ]XFLAG     ; {4C3B} LOAD LOOP EXIT FLAG
         CMP   #1         ; {3C2B} AND COMPARE TO #1
         BEQ   :CPLP      ; {3C2B} IF SET, GOTO COPY LOOP
         DEC   ]END       ; {5C2B} OTHERWISE, DECREASE END POS
:CPLP
         INY              ; {2C1B} INCREASE .Y INDEX
         INX              ; {2C1B} INCREASE .X INDEX
         LDA   (]STR),Y   ; {5C2B} LOAD CHARACTER FROM STRING
         STA   RETURN,X   ; {5C2B} STORE IN RETURN
         CPY   ]END       ; {4C3B} IF .Y INDEX != END POS
         BNE   :CPLP      ; {3C2B} THEN CONTINUE COPY LOOP
:SETLEN
         LDA   ]END       ; {4C3B} LOAD ENDING POS
         SEC              ; {2C1B} SET CARRY
         SBC   ]START     ; {4C3B} SUBTRACT STARTING POS
         STA   RETLEN     ; {4C3B} AND STORE IN RETLEN
:EXIT
         RTS              ; {6C1B}


THE SCNT MACRO

SUMMARY

Condition Value
Name SCNT
Type Macro
File MAC.SUBSTRINGS.ASM
Author Nathan Riggs
Last Revision 12-APR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Count occurrence of characters in a string
Input ]1 = String
]2 = Character
Output none
Dependencies SUB.SUBCHARCNT.ASM
Flags Destroyed NZCV
Cycles 88+
Bytes 57
Notes None
See Also SUB.SUBCHARCNT.ASM

DETAILS

The SCNT macro counts the number of times a character appears in a string. This is especially useful for tokenized strings that need to be parsed into other variables, such as the classic comma-delimited string used prevalently for arrays and spreadsheets. This macro is doubly important due to the fact that the STOK macro and the SUBTOK subroutine do not check for value overflows, meaning that the SCNT macro must be relied on for strings with an unknown number of tokens.

LISTING 5.41: The SCNT Macro Source

*
*``````````````````````````````*
* SCNT          (NATHAN RIGGS) *
*                              *
*                              *
* PARAMETERS                   *
*                              *
*  ]1 = STRING ADDRESS         *
*  ]2 = DELIMITER / TOKEN      *
*                              *
* CYCLES: 88+                  *
* SIZE: 57 BYTES               *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
SCNT     MAC
         _MSTR ]1;WPAR1   ; {14C13B}
         LDA   ]2         ; {4C3B}
         STA   WPAR2      ; {3C2B}
         JSR   SUBCHARCNT ; {67C39B}
         <<<


THE SUBCHARCNT SUBROUTINE

SUMMARY

Condition Value
Name SUBCHARCNT
Type Subroutine
File SUB.SUBCHARCNT.ASM
Author Nathan Riggs
Last Revision 12-APR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose count character occurrences in string
Input WPAR1 = String address
WPAR2 = Character to find
Output none
Dependencies none
Flags Destroyed NZCV
Cycles 61+
Bytes 36
Notes none
See Also SCNT

DETAILS

The SUBCHARCNT subroutine counts the number of occurrences of a character within a string, storing that number (byte) in both RETLEN and the .A register. While potentially useful on its own, this subroutine is immediately available to work in tandem with the STOK macro (or the SUBTOK subroutine), since it does not protect against overflow. SUBCHARCNT can be used to count the number of tokens in a string before STOK is used to retrieve a substring by token number.

LISTING 5.41: The SUBCHARCNT Subroutine Source

*``````````````````````````````*
* SUBCHARCNT    (NATHAN RIGGS) *
*                              *
* COUNT THE NUMBER OF TOKENS,  *
* OR OCCURRENCES OF A GIVEN    *
* CHARACTER, IN A STRING.      *
*                              *
* INPUT:                       *
*                              *
*  WPAR1 = STRING ADDRESS      *
*  WPAR2 = CHARACTER TO FIND   *
*                              *
* DESTROYS: NZCIDV             *
*           ^^^  ^             *
*                              *
* CYCLES: 61+                  *
* SIZE: 36 BYTES               *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]STR     EQU   WPAR1      ; STRING ADDRESS
]CHAR    EQU   WPAR2      ; TOKEN TO FIND
]LEN     EQU   BPAR2      ; LENGTH OF STRING
*
SUBCHARCNT
         LDA   #0         ; {3C2B} CLEAR OUT .A REGISTER
         TAY              ; {2C1B} CLEAR OUT .Y
         TAX              ; {2C1B} CLEAR OUT .X
         LDA   (]STR),Y   ; {5C2B} GET LENGTH FROM STRING
         STA   ]LEN       ; {3C2B} AND HOLD TEMPORARILY
:LP1
         INY              ; {2C1B} INCREASE .Y INDEX
         LDA   (]STR),Y   ; {5C2B} LOAD CHAR AT INDEX
         CMP   ]CHAR      ; {3C2B} COMPARE TO TOKEN
         BEQ   :FOUND     ; {3C2B} IF EQUAL, THEN GOTO :FOUND
:RELOOP  CPY   ]LEN       ; {3C2B} COMPARE .Y TO STRING LENGTH
         BNE   :LP1       ; {3C2B} RELOOP UNTIL EQUAL
         JMP   :EXIT      ; {3C3B} JUMP OVER REST
:FOUND
         INX              ; {2C1B} INCREASE .X COUNTER
         JMP   :RELOOP    ; {3C3B} JUMP BACK TO LOOP
:EXIT
         STX   RETURN     ; {4C3B} STORE .X COUNT IN RETURN
         LDA   #1         ; {3C2B} LOAD #1 AND
         STA   RETLEN     ; {4C3B} STORE IN RETLEN
         TXA              ; {2C1B} ALSO RETURN NUMBER IN .A
         RTS              ; {6C1B}


PART II: STRING AND SUBSTRING DEMONSTRATIONS

The following demo files illustrate how each macro in the collection is used. This includes two demos, one for whole strings and one for substrings.

LISTING 5.50: Whole Strings Demonstration Source

*
*``````````````````````````````*
* DEMO.STRINGS.ASM             *
*                              *
* A DEMO OF THE VARIOUS MACROS *
* FOR STRING HANDLING.         *
*                              *
* AUTHOR:    NATHAN RIGGS      *
* CONTACT:   NATHAN.RIGGS@     *
*            OUTLOOK.COM       *
*                              *
* DATE:      12-APR-2021       *
* ASSEMBLER: MERLIN 8 PRO      *
* OS:        DOS 3.3           *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
** ASSEMBLER DIRECTIVES
*
         CYC   AVE
         EXP   OFF
         TR    ON
         DSK   DEMO.STRINGS
         OBJ   $BFE0
         ORG   $6000
*
*``````````````````````````````*
*  TOP INCLUDES (PUTS, MACROS) *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
         PUT   MIN.HEAD.REQUIRED.ASM
         USE   MIN.MAC.REQUIRED.ASM
         USE   MIN.MAC.STRINGS.ASM
         PUT   MIN.HEAD.STRINGS.ASM
]HOME    EQU   $FC58
*
*``````````````````````````````*
*      PROGRAM MAIN BODY       *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
*``````````````````````````````*
* STRING MACROS                *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
** THIS FILE CONTAINS MACRO DEMONSTRATIONS FOR THE
** STRING COLLECTION OF THE APPLEIIASM LIBRARY. NOTE
** THAT ANOTHER DEMO FILE EXISTS THAT IS DEDICATED TO
** MACROS THAT SPECIFICALLY DEAL WITH SUBSTRINGS,
** WHERAS THIS FILE DEMONSTRATES MACROS THAT WORK ON
** THE WHOLE STRING.
*
*``````````````````````````````*
* STRING REVERSE               *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
** THE SREV MACRO SIMPLY ACCEPTS A STRING
** OR ITS ADDRESS AND REVERSES IT, HANDING
** BACK THE REVERSED STRING IN RETURN WITH
** ITS LENGTH IN RETLEN.
*
         JSR   ]HOME
         _PRN  "STRING REVERSAL",8D
         _PRN  "===============",8D8D
         LDA   #"-"
         JSR   $FDF0
         SREV  #S2
         _AXLIT #RETLEN
         JSR   _PS
         LDA   #"-"
         JSR   $FDF0
         _WAIT
*
*``````````````````````````````*
* STRING TRIMMING              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
** THE STRIM MACRO ACCEPTS A GIVEN TOKEN OR
** DELIMITER TO CHECK FOR AT THE BEGINNING OR
** END OF A STRING, THEN DELETES THEM FROM THE
** STRING IF THEY EXIST AT EITHER END.
*
         JSR   ]HOME
         _PRN  "STRING TRIMMING",8D
         _PRN  "===============",8D8D
         STRIM #S2;#" "
         _AXLIT #RETLEN
         JSR   _PS
         _WAIT
*
*``````````````````````````````*
* STRING UPPERCASE             *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
** THE STRUP MACRO ACCEPTS A STRING AND
** RETURNS ITS UPPERCASE EQUIVALENT.
*
         JSR   ]HOME
         _PRN  "STRING UPPERCASING",8D
         _PRN  "==================",8D8D
         STRUP #S6
         _AXLIT #RETLEN
         JSR   _PS
         _WAIT
*
*``````````````````````````````*
* STRING LOWERCASE             *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
** THE SLO MACRO TAKES A STRING AND MAKES
** EVERY CHARACTER LOWERCASE, PASSING THE NEW
** STRING BACK IN RETURN WITH ITS LENGTH IN
** RETLEN. NOTE THAT *ALL* CHARACTERS ARE TURNED
** TO LOWERCASE THAT ARE ALREADY UPPERCASE, SO EVEN
** WORDS THAT SHOULD BE CAPITALIZED WILL BE
** IN LOWERCASE. YOU CAN REMEDY THIS, IF NEEDED, WITH
** THE SCAP MACRO.
*
         JSR   ]HOME
         _PRN  "STRING LOWERCASING",8D
         _PRN  "==================",8D8D
         STRLO #S1
         _AXLIT #RETLEN
         JSR   _PS
         _WAIT
*
*``````````````````````````````*
* STRING CAPITALIZATION        *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
** THE SCAP MACRO ACCEPTS A STRING AND SCANS
** IT FOR WORDS THAT SHOULD BE CAPITALIZED,
** INCLUDING THE FIRST LETTER OF THE STRING.
** WHILE NOT ALL NEEDS ARE MET WITH THIS, AS
** IT ONLY CAPITALIZES BASED ON WHETHER A NEW
** SENTENCE IS STARTED, IT SHOULD SERVE MOST
** PURPOSES.
*
         JSR   ]HOME
         _PRN  "STRING CAPITALIZATION",8D
         _PRN  "=====================",8D8D
         SCAP  #S5
         _AXLIT #RETLEN
         JSR   _PS
         _WAIT
*
*``````````````````````````````*
* STRING CONCATENATION         *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
** THE SCAT MACRO CONCATENATES (OR JOINS)
** ONE STRING TO ANOTHER AND PASSES THE NEW
** STRING BACK IN RETURN, WITH THE NEW LENGTH
** HELD IN RETLEN.
*
         JSR   ]HOME
         _PRN  "STRING CONCATENATION",8D
         _PRN  "====================",8D8D
         SCAT #S3;#S4
         _AXLIT #RETLEN
         JSR   _PS
         _WAIT
*
*``````````````````````````````*
* STRING COMPARISON            *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
** THE SCMP MACRO COMPARES ONE STRING TO ANOTHER,
** RETURNING THE COMPARISON RESULTS VIA THE STATUS
** REGISTER AS FOLLOWS:
*
** Z = 1 IF STRINGS MATCH
** Z = 0 IF STRINGS DON'T MATCH
** C = 0 IF 1ST STRING < 2ND STRING LENGTH
** C = 1 IF IST STRING >= 2ND STRING LENGTH
*
** NOTE THAT THIS MIRRORS, IN WAYS, HOW THE CMP
** INSTRUCTION WORKS.
*
         JSR   ]HOME
         _PRN  "STRING COMPARISON",8D
         _PRN  "=================",8D8D
         SCMP  #S1;#S2
         BCC   :ALTB
         BCS   :AGTEB
:ALTB
         _PRN  "STRING 1 IS LESS THAN STRING 2.",8D8D
         JMP   :_END
:AGTEB
         _PRN  "STRING 1 IS GREATER THAN OR EQUAL TO",8D
         _PRN  "STRING 2.",8D8D
:_END
         _WAIT
*
         JSR   ]HOME
         _PRN  "FIN!",8D8D
*
*
*
         JMP   $3D0
*
** THE FOLLOWING SUBROUTINE IS A COPY OF THE STDIO
** LIBRARY'S PRNSTR SUBROUTINE, WHICH IS USED TO
** PRINT STRINGS WITH A PRECEDING LENGTH-BYTE. THIS
** IS USED FOR SHOWING THE OUTPUT OF THE VARIOUS
** STRING MACROS.
*
]STRLEN  HEX   0000
]COUT1   EQU   $FDF0
_PS
         STA   ADDR1
         STX   ADDR1+1
         LDY   #0
         LDA   (ADDR1),Y
         STA   ]STRLEN
:_LP
         INY
         LDA   (ADDR1),Y
         JSR   ]COUT1
         CPY   ]STRLEN
         BNE   :_LP
         LDA   ]STRLEN
         RTS
*
S1       STR   "ONE RING TO RULE THEM ALL"
S2       STR   " ONE RING TO FIND THEM "
S3       STR   " ONE RING TO BRING THEM ALL"
S4       STR   "AND IN THE DARKNESS BIND THEM "
S5       STR   "this is a test? a test. a test! a test."
S6       STR   "this is a test."
S7       STR   "ZERO ONE TWO THREE FOUR FIVE SIX"
*
*``````````````````````````````*
*        BOTTOM INCLUDES       *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
** BOTTOM INCLUDES
*
         PUT   MIN.LIB.REQUIRED.ASM
*
** INDIVIDUAL SUBROUTINE INCLUDES
*
** STRING SUBROUTINES
*
         PUT   MIN.SUB.STRCAT.ASM
         PUT   MIN.SUB.STRCOMP.ASM
         PUT   MIN.SUB.STRTRIM.ASM
         PUT   MIN.SUB.STRUPPER.ASM
         PUT   MIN.SUB.STRCAP.ASM
         PUT   MIN.SUB.STRLOWER.ASM
         PUT   MIN.SUB.STRREV.ASM

LISTING 5.51: Substrings Demonstration Source

*
*``````````````````````````````*
* DEMO.SUBSTRINGS.ASM          *
*                              *
* A DEMO OF THE VARIOUS MACROS *
* FOR HANDLING SUBSTRINGS.     *
*                              *
* AUTHOR:    NATHAN RIGGS      *
* CONTACT:   NATHAN.RIGGS@     *
*            OUTLOOK.COM       *
*                              *
* DATE:      11-APR-2021       *
* ASSEMBLER: MERLIN 8 PRO      *
* OS:        DOS 3.3           *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
** ASSEMBLER DIRECTIVES
*
         CYC   AVE
         EXP   OFF
         TR    ON
         DSK   DEMO.SUBSTRINGS
         OBJ   $BFE0
         ORG   $6000
*
*``````````````````````````````*
*  TOP INCLUDES (PUTS, MACROS) *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
         PUT   MIN.HEAD.REQUIRED.ASM
         USE   MIN.MAC.REQUIRED.ASM
         USE   MAC.SUBSTRINGS.ASM
         PUT   MIN.HEAD.STRINGS.ASM
]HOME    EQU   $FC58
*
*``````````````````````````````*
*      PROGRAM MAIN BODY       *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
** THIS FILE CONTAINS DEMONSTRATIONS OF THE MACROS
** DEDICATED TO SUBSTRINGS IN THE STRINGS COLLECTION
** OF THE APPLEIIASM LIBRARY. NOTE THAT ANOTHER
** DEMO FILE EXISTS FOR OPERATIONS ON FULL STRINGS.
*
*``````````````````````````````*
* TOKENIZED SUBSTRING RETRIEVE *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
** THE STOK MACRO RETRIEVES A SUBSTRING FROM A
** LARGER STRING THAT IS FOUND BY A PRECEDING
** TOKEN. THE ENTIRE STRING CONSISTS OF SUBSTRINGS
** SEPARATED BY TOKENS (A SPACE, A COMMA, ETC.), AND
** THE SUBSTRING IS RETRIEVED BY SPECIFYING THE TOKEN
** NUMBER PRECEDING IT.
*
** NOTE THAT THERE IS CURRENTLY NO SAFETY MEASURE TO
** PREVENT OVERFLOW HERE. TO BE SPECIFIC, IT IS
** POSSIBLE TO ATTEMPT TO RETRIEVE A TOKEN SUBSTRING
** THAT DOES NOT EXIST, SUCH AS A SEVENTH SUBSTRING
** WHERE ONLY SIX EXIST. TO GUARD AGAINST THIS, THE
** SCNT MACRO SHOULD BE USED TO ASCERTAIN THE NUMBER OF
** TOKENS IN THE STRING (SEE BELOW).
*
** ADDITIONALLY, IT IS IMPORTANT TO KNOW THAT TOKEN
** NUMBER ZERO IS COUNTED, MEANING THE TEXT BEFORE
** THE FIRST OCCURENCE OF A TOKEN. IF A STRING HAS
** SIX TOKENS IN IT, THEN, IT WILL TECHNICALLY HAVE
** SEVEN SUBSTRINGS.
*
         JSR   ]HOME
         _PRN  "TOKENIZED SUBSTRINGS",8D
         _PRN  "====================",8D8D
         STOK  #S7;#" ";#6
         _AXLIT #RETLEN
         JSR   _PS
         _WAIT
*
*``````````````````````````````*
* STRING TOKEN COUNTING        *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
** THE SCNT MACRO COUNTS THE NUMBER OF TOKENS,
** OR THE NUMBER OF TIMES A GIVEN CHARACTER IS
** PRESENTED IN A STRING. THIS IS MOSTLY USEFUL
** IN CONJUNCTION WITH THE STOK MACRO, AS IT
** PROVIDES THE NUMBER OF SUBSTRINGS AVAILABLE
** TO BE RETRIEVED.
*
** NOTE THAT WHEN USED WITH THE STOK MACRO,
** ONE SHOULD BE ADDED TO THE RESULT OF SCNT
** TO FIND THE TOTAL NUMBER OF SUBSTRINGS THAT
** ARE AVAILABLE. THIS IS DUE TO THE FACT THAT
** THE STOK MACRO STARTS WITH A ZERO INDEX,
** COUNTING THE TEXT BEFORE THE FIRST TOKEN AS
** A TOKENIZED SUBSTRING AS WELL.
*
         JSR   ]HOME
         _PRN  "STRING TOKEN COUNTING",8D
         _PRN  "=====================",8D8D
         SCNT  #S7;#" "
         DUMP  #RETURN;#1
         _WAIT
*
*``````````````````````````````*
* SUBSTRING POSITION           *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
** THE SPOS MACRO FINDS THE POSITION OF A SUBSTRING
** IN A GIVEN STRING AND RETURNS IT. NOTE THAT THIS
** MACRO ONLY RETURNS THE POSITION OF THE FIRST
** INSTANCE OF THE SUBSTRING, AND DOES NOT ACCOUNT
** FOR REPETITIONS.
*
** NOTE THAT THIS MACRO USES A STARTING INDEX OF ONE
** FOR A MORE INTUITIVE FEEL. WHEN USED IN CONJUNCTION
** WITH ZERO-INDEXED MACROS, CARE SHOULD BE TAKEN TO
** ADD OR SUBTRACT TO THIS VALUE ACCORDINGLY.
*
         JSR   ]HOME
         _PRN  "SUBSTRING POSITION",8D
         _PRN  "==================",8D8D
         SPOS  #S1;"RING"
         DUMP  #RETURN;#1
         _WAIT
*
*``````````````````````````````*
* SUBSTRING INSERTION          *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
** THE SINS MACRO INSERTS A GIVEN SUBSTRING INTO
** ANOTHER GIVEN STRING AT THE PROVIDED INDEX. THE
** NEW STRING IS PASSED BACK VIA RETURN, WITH ITS
** LENGTH BYTE IN RETLEN.
*
         JSR   ]HOME
         _PRN  "SUBSTRING INSERTION",8D
         _PRN  "===================",8D8D
         SINS  #S1;#S7;#10
         _AXLIT #RETLEN
         JSR   _PS
         _WAIT
*
*``````````````````````````````*
* SUBSTRING COPY               *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
** THE SCPY MACRO COPIES A SUBSTRING FROM A GIVEN
** STRING AT AN INDEX AND LENGTH. THE RESULTING
** SUBSTRING IS STORED IN RETURN, WITH ITS LENGTH
** HELD IN RETLEN.
*
         JSR   ]HOME
         _PRN  "SUBSTRING COPY",8D
         _PRN  "==============",8D8D
         SCPY  #S1;#5;#10
         _AXLIT #RETLEN
         JSR   _PS
         _WAIT
*
*``````````````````````````````*
* SUBSTRING DELETION           *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
** THE SDEL MACRO TAKES A STRING, AN INDEX AND A
** SUBSTRING LENGTH AND DELETES THAT SUBSTRING
** FROM THE LARGER STRING, PASSING BACK THE RESULTING
** STRING IN RETURN WITH ITS LENGTH IN RETLEN.
*
         JSR   ]HOME
         _PRN  "SUBSTRING DELETION",8D
         _PRN  "==================",8D8D
         SDEL  #S1;#5;#10
         _AXLIT #RETLEN
         JSR   _PS
         _WAIT
*
         JSR   ]HOME
         _PRN  " ",8D8D
         _PRN  "FIN!",8D8D
*
         JMP   $3D0
*
** THE FOLLOWING SUBROUTINE IS A COPY OF THE STDIO
** LIBRARY'S PRNSTR SUBROUTINE, WHICH IS USED TO
** PRINT STRINGS WITH A PRECEDING LENGTH-BYTE. THIS
** IS USED FOR SHOWING THE OUTPUT OF THE VARIOUS
** STRING MACROS.
*
]STRLEN  HEX   0000
]COUT1   EQU   $FDF0
_PS
         STA   ADDR1
         STX   ADDR1+1
         LDY   #0
         LDA   (ADDR1),Y
         STA   ]STRLEN
:_LP
         INY
         LDA   (ADDR1),Y
         JSR   ]COUT1
         CPY   ]STRLEN
         BNE   :_LP
         LDA   ]STRLEN
         RTS
*
S1       STR   "ONE RING TO RULE THEM ALL"
S2       STR   " ONE RING TO FIND THEM "
S3       STR   " ONE RING TO BRING THEM ALL"
S4       STR   "AND IN THE DARKNESS BIND THEM "
S5       STR   "this is a test? a test. a test! a test."
S6       STR   "this is a test."
S7       STR   "ZERO ONE TWO THREE FOUR FIVE SIX"
*
*``````````````````````````````*
*        BOTTOM INCLUDES       *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
** BOTTOM INCLUDES
*
         PUT   MIN.LIB.REQUIRED.ASM
*
** INDIVIDUAL SUBROUTINE INCLUDES
*
** STRING SUBROUTINES
*
         PUT   SUB.SUBTOK.ASM
         PUT   SUB.SUBCOPY.ASM
         PUT   SUB.SUBDEL.ASM
         PUT   SUB.SUBINS.ASM
         PUT   SUB.SUBPOS.ASM
         PUT   SUB.SUBCHARCNT.ASM