mirror of
https://github.com/nathanriggs/AppleIIAsm-Collection.git
synced 2024-06-15 15:29:47 +00:00
3045 lines
107 KiB
Markdown
3045 lines
107 KiB
Markdown
|
# Disk 5: Strings and Substrings
|
||
|
|
||
|
|
||
|
|
||
|
- [Part I: The Strings Collection](#part-i-the-strings-collection)
|
||
|
|
||
|
- [Strings Components](#string-components)
|
||
|
|
||
|
- [Strings Header File](#header-file)
|
||
|
|
||
|
- [Strings Macros and Subroutines](#macros-and-subroutines)
|
||
|
- [Whole Strings](#whole-string-macros)
|
||
|
- [The SCMP Macro](#the-scmp-macro)
|
||
|
- [The STRCOMP Subroutine](#the-strcomp-subroutine)
|
||
|
- [The SCAT Macro](#the-scat-macro)
|
||
|
- [The STRCAT Subroutine](#the-strcat-subroutine)
|
||
|
- [The STRIM Macro](#the-strim-macro)
|
||
|
- [The STRTRIM Subroutine](#the-strtrim-subroutine)
|
||
|
- [The STRUP Macro](#the-strup-macro)
|
||
|
- [The STRUPPER Subroutine](#the-strupper-subroutine)
|
||
|
|
||
|
- [The SLO Macro](#the-slo-macro)
|
||
|
- [The STRLOWER Subroutine](#the-strlower-subroutine)
|
||
|
- [The SREV Macro](#the-srev-macro)
|
||
|
- [The STRREV Subroutine](#the-strrev-subroutine)
|
||
|
- [The SCAP Macro](#the-scap-macro)
|
||
|
- [The STRCAP Subroutine](#the-strcap-subroutine)
|
||
|
|
||
|
- [Substrings](#substring-macros)
|
||
|
- [The SPOS Macro](#the-spos-macro)
|
||
|
- [The SUBPOS Subroutine](#the-subpos-subroutine)
|
||
|
- [The SCPY Macro](#the-scpy-macro)
|
||
|
- [The SUBCOPY Subroutine](#the-subcopy-subroutine)
|
||
|
- [The SDEL Macro](#the-sdel-macro)
|
||
|
- [The SUBDEL Subroutine](#the-subdel-subroutine)
|
||
|
- [The SINS Macro](#the-sins-macro)
|
||
|
- [The SUBINS Subroutine](#the-subins-subroutine)
|
||
|
- [The STOK Macro](#the-stok-macro)
|
||
|
- [The SUBTOK Subroutine](#the-subtok-subroutine)
|
||
|
- [The SCNT Macro](#the-scnt-macro)
|
||
|
- [The SUBCHARCNT Subroutine](#the-subcharcnt-subroutine)
|
||
|
|
||
|
- [Part II: String Collection Demos](#part-ii-string-and-substring-demonstrations)
|
||
|
- Whole Strings Demo
|
||
|
- Substrings Demo
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
---
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
## 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`
|
||
|
|
||
|
```assembly
|
||
|
*
|
||
|
*``````````````````````````````*
|
||
|
* 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`
|
||
|
|
||
|
```assembly
|
||
|
*
|
||
|
*``````````````````````````````*
|
||
|
* 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<br />]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`
|
||
|
|
||
|
```assembly
|
||
|
*
|
||
|
*``````````````````````````````*
|
||
|
* 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<br />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`
|
||
|
|
||
|
```assembly
|
||
|
*``````````````````````````````*
|
||
|
* 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 <br />]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`
|
||
|
|
||
|
```assembly
|
||
|
*
|
||
|
*``````````````````````````````*
|
||
|
* 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<br />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`
|
||
|
|
||
|
```assembly
|
||
|
*``````````````````````````````*
|
||
|
* 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<br />end of a string |
|
||
|
| Input | ]1 = String <br />]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`
|
||
|
|
||
|
```assembly
|
||
|
*
|
||
|
*``````````````````````````````*
|
||
|
* 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<br />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`
|
||
|
|
||
|
```assembly
|
||
|
*``````````````````````````````*
|
||
|
* 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<br />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`
|
||
|
|
||
|
```assembly
|
||
|
*
|
||
|
*``````````````````````````````*
|
||
|
* 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`
|
||
|
|
||
|
```assembly
|
||
|
*``````````````````````````````*
|
||
|
* 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<br />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`
|
||
|
|
||
|
```assembly
|
||
|
*
|
||
|
*``````````````````````````````*
|
||
|
* 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`
|
||
|
|
||
|
```assembly
|
||
|
*``````````````````````````````*
|
||
|
* 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`
|
||
|
|
||
|
```assembly
|
||
|
*
|
||
|
*``````````````````````````````*
|
||
|
* 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`
|
||
|
|
||
|
```assembly
|
||
|
*``````````````````````````````*
|
||
|
* 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<br />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`
|
||
|
|
||
|
```assembly
|
||
|
*
|
||
|
*``````````````````````````````*
|
||
|
* 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<br />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`
|
||
|
|
||
|
```assembly
|
||
|
*``````````````````````````````*
|
||
|
* 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`
|
||
|
|
||
|
```assembly
|
||
|
*``````````````````````````````*
|
||
|
* 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<br />in another string. |
|
||
|
| Input | ]1 = Source string<br />]2 = Substring |
|
||
|
| Output | none |
|
||
|
| Dependencies | `SUB.SUBPOS.ASM` |
|
||
|
| Flags Destroyed | NZCV |
|
||
|
| Cycles | 205+ |
|
||
|
| Bytes | 136 |
|
||
|
| Notes | None |
|
||
|
| See Also | `SUB.SUBPOS.ASM` |
|
||
|
|
||
|
---
|
||
|
|
||
|
*DETAILS*
|
||
|
|
||
|
The `SPOS`macro 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`
|
||
|
|
||
|
```assembly
|
||
|
*
|
||
|
*``````````````````````````````*
|
||
|
* 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<br />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`
|
||
|
|
||
|
```assembly
|
||
|
*``````````````````````````````*
|
||
|
* 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<br />]2 = Substring index<br />]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`
|
||
|
|
||
|
```assembly
|
||
|
*
|
||
|
*``````````````````````````````*
|
||
|
* 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<br />BPAR1 = Substring length<br />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`
|
||
|
|
||
|
```assembly
|
||
|
*``````````````````````````````*
|
||
|
* 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<br />]2 = Substring index<br />]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`
|
||
|
|
||
|
```assembly
|
||
|
*
|
||
|
*``````````````````````````````*
|
||
|
* 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<br />BPAR1 = Substring length<br />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`
|
||
|
|
||
|
```assembly
|
||
|
*``````````````````````````````*
|
||
|
* 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<br />]2 = Substring<br />]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`
|
||
|
|
||
|
```assembly
|
||
|
*
|
||
|
*``````````````````````````````*
|
||
|
* 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<br />BPAR1 = String length<br />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`
|
||
|
|
||
|
```assembly
|
||
|
*``````````````````````````````*
|
||
|
* 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 <br />]2 = Token<br />]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`
|
||
|
|
||
|
```assembly
|
||
|
*
|
||
|
*``````````````````````````````*
|
||
|
* 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<br />WPAR2 = Token<br />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`
|
||
|
|
||
|
```assembly
|
||
|
*``````````````````````````````*
|
||
|
* 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 <br />]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<br />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`
|
||
|
|
||
|
```assembly
|
||
|
*``````````````````````````````*
|
||
|
* 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`
|
||
|
|
||
|
```assembly
|
||
|
*
|
||
|
*``````````````````````````````*
|
||
|
* 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`
|
||
|
|
||
|
```assembly
|
||
|
*
|
||
|
*``````````````````````````````*
|
||
|
* 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
|
||
|
|
||
|
```
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|