diff --git a/bin/IIGS/x16_iigs_gui.po b/bin/IIGS/x16_iigs_gui.po new file mode 100644 index 0000000..0dbd3ac Binary files /dev/null and b/bin/IIGS/x16_iigs_gui.po differ diff --git a/bin/IIGS/x17_iigs_graphics.po b/bin/IIGS/x17_iigs_graphics.po new file mode 100644 index 0000000..0dbd3ac Binary files /dev/null and b/bin/IIGS/x17_iigs_graphics.po differ diff --git a/bin/IIGS/x18_iigs_sound.po b/bin/IIGS/x18_iigs_sound.po new file mode 100644 index 0000000..0dbd3ac Binary files /dev/null and b/bin/IIGS/x18_iigs_sound.po differ diff --git a/bin/README.md b/bin/README.md index 616d4de..45c3296 100644 --- a/bin/README.md +++ b/bin/README.md @@ -1,3 +1,3 @@ You will find the current disks being worked on here. Commits to these files, unfortunately, may spam the documentation. -Note that the core part of the collection is numbered, while there rest has yet to be decided on or oncorporated. +Note that the core part of the collection is numbered, while there rest has yet to be decided on or incorporated. diff --git a/bin/__damaged_d02_stdio.dsk b/bin/__damaged_d02_stdio.dsk deleted file mode 100644 index 6636582..0000000 Binary files a/bin/__damaged_d02_stdio.dsk and /dev/null differ diff --git a/bin/d01_required.dsk b/bin/d01_required.dsk index a501f29..61042ca 100644 Binary files a/bin/d01_required.dsk and b/bin/d01_required.dsk differ diff --git a/bin/d02_stdio.dsk b/bin/d02_stdio.dsk index 4be4def..8104abb 100644 Binary files a/bin/d02_stdio.dsk and b/bin/d02_stdio.dsk differ diff --git a/bin/d03_arrays.dsk b/bin/d03_arrays.dsk index c565a2e..b9cd72b 100644 Binary files a/bin/d03_arrays.dsk and b/bin/d03_arrays.dsk differ diff --git a/bin/d04_math.dsk b/bin/d04_math.dsk index 94b29f3..dfa8cd3 100644 Binary files a/bin/d04_math.dsk and b/bin/d04_math.dsk differ diff --git a/bin/d05_strings.dsk b/bin/d05_strings.dsk index cd1d73d..bdab07a 100644 Binary files a/bin/d05_strings.dsk and b/bin/d05_strings.dsk differ diff --git a/bin/d06_DOS.dsk b/bin/d06_DOS.dsk new file mode 100644 index 0000000..f655b66 Binary files /dev/null and b/bin/d06_DOS.dsk differ diff --git a/bin/d06_filesDOS.dsk b/bin/d06_filesDOS.dsk deleted file mode 100644 index 8255486..0000000 Binary files a/bin/d06_filesDOS.dsk and /dev/null differ diff --git a/bin/d07_convert.dsk b/bin/d07_convert.dsk deleted file mode 100644 index cd57a03..0000000 Binary files a/bin/d07_convert.dsk and /dev/null differ diff --git a/bin/d08_lores.dsk b/bin/d08_lores.dsk index 279aae1..290a51e 100644 Binary files a/bin/d08_lores.dsk and b/bin/d08_lores.dsk differ diff --git a/bin/d09_hires.dsk b/bin/d09_hires.dsk index a31102c..9ecbce8 100644 Binary files a/bin/d09_hires.dsk and b/bin/d09_hires.dsk differ diff --git a/bin/d10_speaker.dsk b/bin/d10_speaker.dsk index e534ca4..815d89e 100644 Binary files a/bin/d10_speaker.dsk and b/bin/d10_speaker.dsk differ diff --git a/bin/d12_applechop.dsk b/bin/d11_applechop.dsk similarity index 97% rename from bin/d12_applechop.dsk rename to bin/d11_applechop.dsk index 9d865a7..5347081 100644 Binary files a/bin/d12_applechop.dsk and b/bin/d11_applechop.dsk differ diff --git a/bin/d11_detection.dsk b/bin/d13_col80.dsk similarity index 100% rename from bin/d11_detection.dsk rename to bin/d13_col80.dsk diff --git a/bin/xd12_serial_prn.dsk b/bin/d14_dbl_lores.dsk similarity index 100% rename from bin/xd12_serial_prn.dsk rename to bin/d14_dbl_lores.dsk diff --git a/bin/xd_80col.dsk b/bin/d15_dbl_hires.dsk similarity index 100% rename from bin/xd_80col.dsk rename to bin/d15_dbl_hires.dsk diff --git a/bin/xd_dbl_hires.dsk b/bin/d16_mockingboard.dsk similarity index 100% rename from bin/xd_dbl_hires.dsk rename to bin/d16_mockingboard.dsk diff --git a/bin/d20_copydisk_A.dsk b/bin/d20_copydisk_A.dsk new file mode 100644 index 0000000..4bfca87 Binary files /dev/null and b/bin/d20_copydisk_A.dsk differ diff --git a/bin/xd_minidisk_A.dsk b/bin/d21_copydisk_B.dsk similarity index 100% rename from bin/xd_minidisk_A.dsk rename to bin/d21_copydisk_B.dsk diff --git a/bin/z__demo_builds_1.dsk b/bin/d22_demo_builds_1.dsk similarity index 100% rename from bin/z__demo_builds_1.dsk rename to bin/d22_demo_builds_1.dsk diff --git a/bin/z__demo_builds_2.dsk b/bin/d23_demo_builds_2.dsk similarity index 100% rename from bin/z__demo_builds_2.dsk rename to bin/d23_demo_builds_2.dsk diff --git a/bin/z__utilities_1.dsk b/bin/d24_utilities_1.dsk similarity index 100% rename from bin/z__utilities_1.dsk rename to bin/d24_utilities_1.dsk diff --git a/bin/d7_convert.dsk b/bin/d7_convert.dsk new file mode 100644 index 0000000..be234df Binary files /dev/null and b/bin/d7_convert.dsk differ diff --git a/bin/external_tools/Apple DOS 3.3 August 1980.dsk b/bin/external_tools/Apple DOS 3.3 August 1980.dsk new file mode 100644 index 0000000..220fdc5 Binary files /dev/null and b/bin/external_tools/Apple DOS 3.3 August 1980.dsk differ diff --git a/bin/xd_dbl_lores.dsk b/bin/external_tools/Apple DOS 3.3 January 1983.dsk similarity index 51% rename from bin/xd_dbl_lores.dsk rename to bin/external_tools/Apple DOS 3.3 January 1983.dsk index 9ab4308..ef257aa 100644 Binary files a/bin/xd_dbl_lores.dsk and b/bin/external_tools/Apple DOS 3.3 January 1983.dsk differ diff --git a/bin/external_tools/Apple's ProDOS System Utilities - Filer v1.1.dsk b/bin/external_tools/Apple's ProDOS System Utilities - Filer v1.1.dsk new file mode 100644 index 0000000..65a757b Binary files /dev/null and b/bin/external_tools/Apple's ProDOS System Utilities - Filer v1.1.dsk differ diff --git a/bin/external_tools/Copy ][ Plus 5.5 (clean rip from original disk).dsk b/bin/external_tools/Copy ][ Plus 5.5 (clean rip from original disk).dsk new file mode 100644 index 0000000..028a393 Binary files /dev/null and b/bin/external_tools/Copy ][ Plus 5.5 (clean rip from original disk).dsk differ diff --git a/bin/xd_iigs_graphics2.dsk b/bin/external_tools/DOS 3.3 System Master - 680-0210-A (1982).dsk similarity index 57% rename from bin/xd_iigs_graphics2.dsk rename to bin/external_tools/DOS 3.3 System Master - 680-0210-A (1982).dsk index 9ab4308..7bdd466 100644 Binary files a/bin/xd_iigs_graphics2.dsk and b/bin/external_tools/DOS 3.3 System Master - 680-0210-A (1982).dsk differ diff --git a/bin/xd_iigs_gui.dsk b/bin/external_tools/DOS33_blank_with_integer_basic.DSK similarity index 85% rename from bin/xd_iigs_gui.dsk rename to bin/external_tools/DOS33_blank_with_integer_basic.DSK index 9ab4308..273c81b 100644 Binary files a/bin/xd_iigs_gui.dsk and b/bin/external_tools/DOS33_blank_with_integer_basic.DSK differ diff --git a/bin/external_tools/MR_FIXIT_DISK_ARRANGER.dsk b/bin/external_tools/MR_FIXIT_DISK_ARRANGER.dsk new file mode 100644 index 0000000..947f251 Binary files /dev/null and b/bin/external_tools/MR_FIXIT_DISK_ARRANGER.dsk differ diff --git a/bin/external_tools/PRODOS-8 v4.0.2 System.dsk b/bin/external_tools/PRODOS-8 v4.0.2 System.dsk new file mode 100644 index 0000000..4160cae Binary files /dev/null and b/bin/external_tools/PRODOS-8 v4.0.2 System.dsk differ diff --git a/bin/external_tools/Tristan's Utilities II.DSK b/bin/external_tools/Tristan's Utilities II.DSK new file mode 100644 index 0000000..a7fcf32 Binary files /dev/null and b/bin/external_tools/Tristan's Utilities II.DSK differ diff --git a/bin/xd_iigs_graphics1.dsk b/bin/hires/d09_hires.dsk similarity index 85% rename from bin/xd_iigs_graphics1.dsk rename to bin/hires/d09_hires.dsk index 9ab4308..ea3d17a 100644 Binary files a/bin/xd_iigs_graphics1.dsk and b/bin/hires/d09_hires.dsk differ diff --git a/bin/hires/test1.dsk b/bin/hires/test1.dsk new file mode 100644 index 0000000..8d88f46 Binary files /dev/null and b/bin/hires/test1.dsk differ diff --git a/bin/hires/test1_000000000.bmp b/bin/hires/test1_000000000.bmp new file mode 100644 index 0000000..ab6fd65 Binary files /dev/null and b/bin/hires/test1_000000000.bmp differ diff --git a/bin/Merlin Pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3).dsk b/bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3).dsk similarity index 100% rename from bin/Merlin Pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3).dsk rename to bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3).dsk diff --git a/bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3)_000000000.bmp b/bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3)_000000000.bmp new file mode 100644 index 0000000..5f7733e Binary files /dev/null and b/bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3)_000000000.bmp differ diff --git a/bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3)_000000001.bmp b/bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3)_000000001.bmp new file mode 100644 index 0000000..5f7733e Binary files /dev/null and b/bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3)_000000001.bmp differ diff --git a/bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3)_000000002.bmp b/bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3)_000000002.bmp new file mode 100644 index 0000000..58774f5 Binary files /dev/null and b/bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3)_000000002.bmp differ diff --git a/bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3)_000000003.bmp b/bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3)_000000003.bmp new file mode 100644 index 0000000..740b94d Binary files /dev/null and b/bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3)_000000003.bmp differ diff --git a/bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3)_000000004.bmp b/bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3)_000000004.bmp new file mode 100644 index 0000000..740b94d Binary files /dev/null and b/bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3)_000000004.bmp differ diff --git a/bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3)_000000005.bmp b/bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3)_000000005.bmp new file mode 100644 index 0000000..740b94d Binary files /dev/null and b/bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3)_000000005.bmp differ diff --git a/bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3)_000000006.bmp b/bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3)_000000006.bmp new file mode 100644 index 0000000..40d4395 Binary files /dev/null and b/bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3)_000000006.bmp differ diff --git a/bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3)_000000007.bmp b/bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3)_000000007.bmp new file mode 100644 index 0000000..40d4395 Binary files /dev/null and b/bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3)_000000007.bmp differ diff --git a/bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3)_000000008.bmp b/bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3)_000000008.bmp new file mode 100644 index 0000000..40d4395 Binary files /dev/null and b/bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3)_000000008.bmp differ diff --git a/bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3)_000000009.bmp b/bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3)_000000009.bmp new file mode 100644 index 0000000..40d4395 Binary files /dev/null and b/bin/merlin_pro/Merlin-Pro v2.43 Disk 1 (DOS 3.3)_000000009.bmp differ diff --git a/bin/Merlin Pro/Merlin-Pro v2.43 Disk 2.dsk b/bin/merlin_pro/Merlin-Pro v2.43 Disk 2.dsk similarity index 100% rename from bin/Merlin Pro/Merlin-Pro v2.43 Disk 2.dsk rename to bin/merlin_pro/Merlin-Pro v2.43 Disk 2.dsk diff --git a/bin/Merlin Pro/Merlin-Pro v2.43 disk 3.dsk b/bin/merlin_pro/Merlin-Pro v2.43 disk 3.dsk similarity index 100% rename from bin/Merlin Pro/Merlin-Pro v2.43 disk 3.dsk rename to bin/merlin_pro/Merlin-Pro v2.43 disk 3.dsk diff --git a/bin/Merlin Pro/Merlin-Pro v2.4_000000000.bmp b/bin/merlin_pro/Merlin-Pro v2.4_000000000.bmp similarity index 100% rename from bin/Merlin Pro/Merlin-Pro v2.4_000000000.bmp rename to bin/merlin_pro/Merlin-Pro v2.4_000000000.bmp diff --git a/bin/Merlin Pro/Merlin-Pro v2.4_000000001.bmp b/bin/merlin_pro/Merlin-Pro v2.4_000000001.bmp similarity index 100% rename from bin/Merlin Pro/Merlin-Pro v2.4_000000001.bmp rename to bin/merlin_pro/Merlin-Pro v2.4_000000001.bmp diff --git a/bin/Merlin Pro/Merlin-Pro v2.4_000000002.bmp b/bin/merlin_pro/Merlin-Pro v2.4_000000002.bmp similarity index 100% rename from bin/Merlin Pro/Merlin-Pro v2.4_000000002.bmp rename to bin/merlin_pro/Merlin-Pro v2.4_000000002.bmp diff --git a/bin/Merlin Pro/Merlin-Pro v2.4_000000003.bmp b/bin/merlin_pro/Merlin-Pro v2.4_000000003.bmp similarity index 100% rename from bin/Merlin Pro/Merlin-Pro v2.4_000000003.bmp rename to bin/merlin_pro/Merlin-Pro v2.4_000000003.bmp diff --git a/bin/Merlin Pro/blankdos33.dsk b/bin/merlin_pro/blankdos33.dsk similarity index 100% rename from bin/Merlin Pro/blankdos33.dsk rename to bin/merlin_pro/blankdos33.dsk diff --git a/bin/xd_iigs_sound1.dsk b/bin/xd_iigs_sound1.dsk deleted file mode 100644 index 9ab4308..0000000 Binary files a/bin/xd_iigs_sound1.dsk and /dev/null differ diff --git a/bin/xd_iigs_sound2.dsk b/bin/xd_iigs_sound2.dsk deleted file mode 100644 index 9ab4308..0000000 Binary files a/bin/xd_iigs_sound2.dsk and /dev/null differ diff --git a/bin/xd_minidisk_B.dsk b/bin/xd_minidisk_B.dsk deleted file mode 100644 index 2000438..0000000 Binary files a/bin/xd_minidisk_B.dsk and /dev/null differ diff --git a/bin/xd_mischw.dsk b/bin/xd_mischw.dsk deleted file mode 100644 index 9ab4308..0000000 Binary files a/bin/xd_mischw.dsk and /dev/null differ diff --git a/bin/xd_mockingboard.dsk b/bin/xd_mockingboard.dsk deleted file mode 100644 index 9ab4308..0000000 Binary files a/bin/xd_mockingboard.dsk and /dev/null differ diff --git a/bin/z__sortsearch.dsk b/bin/z__sortsearch.dsk deleted file mode 100644 index 9ab4308..0000000 Binary files a/bin/z__sortsearch.dsk and /dev/null differ diff --git a/bin/z__tmenus_twindows.dsk b/bin/z__tmenus_twindows.dsk deleted file mode 100644 index 9fc8f8d..0000000 Binary files a/bin/z__tmenus_twindows.dsk and /dev/null differ diff --git a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/0.0 Title to TOC.md b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/0.0 Title_to_TOC.md similarity index 69% rename from documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/0.0 Title to TOC.md rename to documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/0.0 Title_to_TOC.md index d7e910a..fc23e61 100644 --- a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/0.0 Title to TOC.md +++ b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/0.0 Title_to_TOC.md @@ -68,13 +68,18 @@ limitations under the License. 5. [Software Architecture](4.0%20Software_Architecture.md) 6. [Macro/Subroutine Quick References](5.0%20Quick_Reference_TOC.md) 7. - [Quick Reference Introduction](5.0%20Quick_Reference_TOC.md) - - [Disk 1 -- REQUIRED Macros and Subroutines](6.0%20Quick_Reference_D1_MACREQUIRED.md) - - [Disk 2 -- STDIO Macros and Subroutines](7.0%20Quick_Reference_D2_MACSTDIO.md) + - [Disk 1 -- REQUIRED Macros](6.0%20Quick_Reference_D1_MAC.REQUIRED.md) + - [Disk 2 -- STDIO Macros](7.0%20Quick_Reference_D2_MAC.STDIO.md) + - [Disk 3 -- ARRAYS Macros](8.0%20Quick_Reference_D3_MAC.ARRAYS.md) + - [Disk 4 -- MATH Macros](9.0%20quick_reference_d4_mac.math.md) 7. Detailed References and Source Code Listings - [Disk 1: Required Library and Aliases](30.0%20Detailed_Reference_D1_REQUIRED.md) + - [Disk 2: STDIO Macros and Subroutines](31.0%20Detailed_Reference_D2_STDIO.md) + - [Disk 3: Array Macros and Subroutines](32.0%20Detailed_Reference_D3_Arrays.md) + - [Disk 4: Basic Math Macros and Subroutines](33.0%20Detailed_Reference_D4_MATH.md) 8. Appendices - Supplemental Texts - Further Reference - Optimizations - - Typical Assembly Opimizations + - Typical Assembly Optimizations - Architecture Clobbering Optimizations diff --git a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/1.0 Preface.md b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/1.0 Preface.md index 353561b..259a716 100644 --- a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/1.0 Preface.md +++ b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/1.0 Preface.md @@ -1,10 +1,10 @@ ## Preface -This is the first complete reference manual for the _AppleIIAsm_ macro and subroutine library collection. Currently, this collection is in the alpha stages of development: not all disks are complete, there may be some bugs here and there, and major workflow decisions might still be in flux. However, this version, 0.7.0, represents a major step forward in functionality, optimization and standardization, and at least for what is complete---the first eleven disks, the technical manual, AppleChop introduction as well as some demos---the collection can be reasonably considered to be stable. That does not, of course, mean that there are any guarantees. +This is the first complete reference manual for the _AppleIIAsm_ macro and subroutine library. Currently, this library is in the alpha stages of development: not all disks (collections) are complete, there may be some bugs here and there, and major workflow decisions might still be in flux. However, this version, 0.7.0, represents a major step forward in functionality, optimization and standardization, and at least for what is complete--the first eleven disks, the technical manual, AppleChop introduction as well as some demos--the collection can be reasonably considered to be stable. That does not, of course, mean that there are any guarantees. I started this project as research into how the Apple II works as a platform for another book I am writing, and eventually became interested in the cohesive technical documentation (or sometimes lack thereof) that was available to beginning coders in the heyday of the Apple II as well as those looking to learn Apple II (6502) Assembly today. Having no prior experience with Assembly language, I began coding these libraries as part of my own learning process while trying to write subroutines that provided much of the functionality afforded by Applesoft BASIC. Eventually, this became a beast of its own, and what you’re reading here is (part) of the result. -As the libraries grow and morph, so will this document. If nothing else, I hope that the collection and its accompanying documentation helps hobbyists, researchers, and otherwise self-hating hopeless nerds learn and accomplish what they want or need---at least as much as it has helped, and harmed, me. +As the library collections grow and morph, so will this document. If nothing else, I hope that the collection and its accompanying documentation helps hobbyists, researchers, and otherwise self-hating hopeless nerds learn and accomplish what they want or need---at least as much as it has helped, and harmed, me. @@ -12,7 +12,12 @@ Nathan Riggs - +--- + + + +[Return to Table of Contents](0.0%20Title_to_TOC) +[Next: Introduction](2.0%20Introduction.md) diff --git a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/10.0 Quick_Reference_D5_MAC.STRINGS.md b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/10.0 Quick_Reference_D5_MAC.STRINGS.md index e69de29..8e98cbe 100644 --- a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/10.0 Quick_Reference_D5_MAC.STRINGS.md +++ b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/10.0 Quick_Reference_D5_MAC.STRINGS.md @@ -0,0 +1,40 @@ +# Disk 5: Strings and Substrings + + + +The Strings Collection of the AppleIIAsm Library is dedicated to macros used for handling strings and substrings. In particular, this refers to the default type of string: those with a preceding length-byte. If these macros need to be used on another type of string, such as a null-terminated string, then the `STRPC` macro should be used on the string first to convert it to a preceding length-byte type. + +The strings collection can be divided further into two categories: those that deal with whole strings and those that deal with substrings exclusively. This is currently reflected in the fact that the macros are held in separate files and use different demos. + + + +--- + + + +### MAC.STRINGS.ASM + +| MACRO | DEPENDENCY | PARAMETERS | ACTION | DESTROYS | CYCLES | BYTES | +| ------- | ------------------ | ----------------------------------------- | -------------------- | -------- | ------ | ----- | +| `SCMP` | `SUB.STRCOMP.ASM` | ]1 = First string
]2 = Second string | Compare strings | NZCV | 110+ | 67 | +| `SCAT` | `SUB.STRCAT.ASM` | ]1 = First string
]2 = Second string | Concatenate strings | NZCV | 170+ | 113 | +| `STRIM` | `SUB.STRIM.ASM` | ]1 = String
]2 = Token | Trim string | NZCV | 123+ | 80 | +| `STRUP` | `SUB.STRUPPER.ASM` | ]1 = String | Convert to uppercase | NZCV | 114+ | 64 | +| `SLO` | `SUB.STRLOWER.ASM` | ]1 = String | Convert to lowercase | NZCV | 113+ | 64 | +| `SREV` | `SUB.STRREV.ASM` | ]1 = String | Reverse a string | NZCV | 70+ | 41 | +| `SCAP` | `SUB.STRCAP.ASM` | ]1 = String | Capitalize sentences | NZCV | 192+ | 117 | + + + + + +### MAC.SUBSTRINGS.ASM + +| MACRO | DEPENDENCY | PARAMETERS | ACTION | DESTROYS | CYCLES | BYTES | +| ------ | -------------------- | ------------------------------------------------------ | --------------------------- | -------- | ------ | ----- | +| `SPOS` | `SUB.SUBPOS.ASM` | ]1 = String
]2 = Substring | Get substring position | NZCV | 205+ | 136 | +| `SCPY` | `SUB.SUBCOPY.ASM` | ]1 = Source string
]2 = Index
]3 = Length | Copy substring | NZCV | 95+ | 71 | +| `SDEL` | `SUB.SUBDEL.ASM` | ]1 = Source string
]2 = Index
]3 = Length | Delete substring | NZCV | 135+ | 79 | +| `SINS` | `SUB.SUBINS.ASM` | ]1 = Source string
]2 = Substring
]3 = Index | Insert substring | NZCV | 177+ | 111 | +| `STOK` | `SUB.SUBTOK.ASM` | ]1 = String
]2 = Token
]3 = Token number | Find tokenized substring | NZCV | 182+ | 118 | +| `SCNT` | `SUB.SUBCHARCNT.ASM` | ]1 = String
]2 = Character | Count character occurrences | NZCV | 88+ | 57 | \ No newline at end of file diff --git a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/11.0 Quick_Reference_D6_MAC.DOS.md b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/11.0 Quick_Reference_D6_MAC.DOS.md new file mode 100644 index 0000000..99f0d1d --- /dev/null +++ b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/11.0 Quick_Reference_D6_MAC.DOS.md @@ -0,0 +1,59 @@ +# Disk 6: Apple DOS + + + +The DOS Collection of the AppleIIAsm Library consists of macros and subroutines dedicated to interfacing with Apple DOS, providing an abstraction layer for the programmer on top of the lower level routines used by DOS such as the file manager and RWTS. All of the basic functions of DOS are available for use, save for specific macros dedicated solely to reading and writing text files. The reasoning for this is that 1) unless you are interfacing with a BASIC program or another system, text files are largely unnecessary (binary files work better), and 2) text file manipulation often requires creating a system of your own since there is no simple default way to read the end of a file. The macros and routines that do exist can be used to create your own custom system for reading and writing text files. + +Of all the collections in the library, the DOS collection is perhaps the most convoluted and has the largest overhead due to the more complex nature of its functionality. There are many subroutines and macros that are meant to be used only by the collection itself, though the enterprising programmer may of course utilize them. These are described in the detailed listings. + + + +--- + + + +### MAC.DOSREQ.ASM + +| MACRO | DEPENDENCY | PARAMETERS | ACTION | DESTROYS | CYCLES | BYTES | +| ------- | ------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | -------- | ------ | ----- | +| `FMFIL` | HEAD.DOS.ASM | ]1 = Command
]2 = Slot
]3 = Drive
]4 = Volume
]5 = Record | Fill most common parameters of the File Manager. | NZCV | 603 | 303 | +| `FMNAM` | HEAD.DOS.ASM | ]1 = String Address | Copy a string to the file name parameter in the File Parameter List. | NZCV | 117 | 70 | +| `FRWB` | HEAD.DOS.ASM | ]1 = Read/Write flag
]2 = Command
]3 = Slot
]4 = Drive
]5 = Volume
]6 = Record
]7 = Byte Offset
]8 = Write Byte | Read or write a byte from or to a file. Use `FRWRTB` and `FRDB` instead of this, both of which call this macro. | NZCV | 779 | 404 | +| `FWRTB` | HEAD.DOS.ASM | ]1 = Slot
]2 = Drive
]3 = Volume
]4 = Record
]5 = Byte Offset
]6 = Write Byte | Write a byte to a file. | NZCV | 665 | 337 | +| `FRDB` | HEAD.DOS.ASM | ]1 = Slot
]2 = Drive
]3 = Volume
]4 = Record
]5 = Byte Offset
]6 = Write Byte (always 0) | Read a byte from a file. | NZCV | 700 | 357 | +| `FRWR` | HEAD.DOS.ASM | ]1 = Read/Write flag
]2 = Command
]3 = Slot
]4 = Drive
]5 = Volume
]6 = Record
]7 = Byte Offset
]8 = Range Address
]TEMP = Range Length | Read or write a range of bytes in a file. Use `FRDR` or `FWRTR` instead of this macro directly. | NZCV | 846 | 451 | +| `FRDR` | HEAD.DOS.ASM | ]1 = Slot
]2 = Drive
]3 = Volume
]4 = Record
]5 = Byte Offset
]6 = Range Address
]7 = Range Length | Read a range of bytes from a file. | NZCV | 697 | 361 | +| `FWRTR` | HEAD.DOS.ASM | ]1 = Slot
]2 = Drive
]3 = Volume
]4 = Record
]5 = Byte Offset
]6 = Range Address
]7 = Range Length | Write a range of bytes to a file. | NZCV | 940 | 509 | +| `sRWTS` | HEAD.DOS.ASM | ]1 = Slot
]2 = Drive
]3 = Volume
]4 = Track
]5 = Sector
]6 = Buffer
]7 = Command | Set the most common RWTS parameters. | NZCV | 228 | 141 | +| `GRWTS` | HEAD.DOS.ASM | none | Execute the RWTS routine. | NZCV | 19 | 11 | + + + + + +### MAC.DOSFM.ASM + +| MACRO | DEPENDENCY | PARAMETERS | ACTION | DESTROYS | CYCLES | BYTES | +| ------- | -------------- | ------------------------------------------------------------ | ----------------- | -------- | ------ | ----- | +| `FCAT` | HEAD.DOS.ASM | ]1 = Slot
]2 = Drive | Catalog. | NZCV | 657 | 348 | +| `FULCK` | HEAD.DOS.ASM | ]1 = Filename address
]2 = Slot
]3 = Drive | File unlock. | NZCV | 629 | 353 | +| `FLOCK` | HEAD.DOS.ASM | ]1 = Filename address
]2 = Slot
]3 = Drive | File lock. | NZCV | 172 | 116 | +| `FDEL` | HEAD.DOS.ASM | ]1 = Filename address
]2 = Slot
]3 = Drive | File delete. | NZCV | 791 | 412 | +| `FVFY` | HEAD.DOS.ASM | ]1 = Filename address
]2 = Slot
]3 = Drive | File verify. | NZCV | 791 | 411 | +| `FCLOS` | HEAD.DOS.ASM | ]1 = Filename address
]2 = Slot
]3 = Drive | File close. | NZCV | 797 | 415 | +| `FRENM` | HEAD.DOS.ASM | ]1 = Filename address
]2 = New filename address
]3 = Slot
]4 = Drive | File rename. | NZCV | 927 | 578 | +| `FOPEN` | HEAD.DOS.ASM | ]1 = Filename address
]2 = Slot
]3 = Drive
]4 = Volume
]5 = Record | File open. | NZCV | 828 | 433 | +| `BLOAD` | SUB.FBLOAD.ASM | ]1 = Filename address
]2 = Load address
]3 = Slot
]4 = Drive
]5 = Volume | Load binary file. | NZCV | 3768 | 2735 | +| `BSAVE` | SUB.FBSAVE.ASM | ]1 = Filename address
]2 = Load address
]3 = Range length
]4 = Slot
]5 = Drive
]6 = Volume | Save binary file. | NZCV | 5300 | 2758 | + + + +### MAC.DOSMORE.ASM + +| MACRO | DEPENDENCY | PARAMETERS | ACTION | DESTROYS | CYCLES | BYTES | +| ------- | ---------- | ---------- | -------------------------------------------- | -------- | ------ | ----- | +| `DVER` | none | none | Get DOS version. | NZCV | 28 | 18 | +| `DOSIN` | none | none | Check if DOS is loaded. | NZCV | 17 | 12 | +| `ABAS` | none | none | Check if Applesoft is loaded. | NZCV | 40 | 29 | +| `IBEX` | none | none | Check if Integer Basic program is running. | NZCV | 17 | 12 | +| `ABEX` | none | none | Check if Applesoft Basic program is running. | NZCV | 26 | 18 | \ No newline at end of file diff --git a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/11.0 Quick_Reference_D6_MAC.DOSFILES.md b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/11.0 Quick_Reference_D6_MAC.DOSFILES.md deleted file mode 100644 index e69de29..0000000 diff --git a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/12.0 Quick_Reference_D7_MAC.CONVERT.md b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/12.0 Quick_Reference_D7_MAC.CONVERT.md index e69de29..1598fcf 100644 --- a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/12.0 Quick_Reference_D7_MAC.CONVERT.md +++ b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/12.0 Quick_Reference_D7_MAC.CONVERT.md @@ -0,0 +1,36 @@ +# Disk #7: Conversion + + + +The Conversion Collection contains macros and subroutines for converting between different data types. This currently includes macros for converting a: + +- numeric value to an integer string +- integer string to a numeric value +- numeric value to a hexadecimal string +- hexadecimal string to a numeric value +- numeric value to a binary string +- binary string to a numeric value + +In future revisions, other conversions will include: + +- string type conversions (preceding length byte, null-terminated, etc.) +- string to array +- array to string + +Note that some of these subroutines are redundant due to the fact that some conversions are already available in the ROM. However, for the sake of understanding how these conversions work, they are provided here if for nothing more than illustration. + + + +--- + + + +| MACRO | DEPENDENCY | PARAMETERS | ACTION | DESTROYS | CYCLES | BYTES | +| ------- | ------------------ | -------------------------------- | ------------------------------------------- | -------- | ------ | ----- | +| `I2STR` | SUB.HEX2INTASC.ASM | ]1 = Value to convert or address | Convert numeric value to integer string | NZCV | 290 | 172 | +| `STR2I` | SUB.INTASC2HEX.ASM | ]1 = String address | Convert integer string to numeric value | NZCV | 415 | 196 | +| `H2STR` | SUB.HEX2HEXASC.ASM | ]1 = Value to convert or address | Convert numeric value to hexadecimal string | NZCV | 97 | 59 | +| `STR2H` | SUB.HEXASC2HEX.ASM | ]1 = String address | Convert hexadecimal string to numeric value | NZCV | 116 | 71 | +| `B2STR` | SUB.HEX2BINASC.ASM | ]1 = Value to convert or address | Convert numeric value to binary string | NZCV | 262 | 167 | +| `STR2B` | SUB.BINASC2HEX.ASM | ]1 = String address | Convert binary string to numeric value | NZCV | 501 | 331 | + diff --git a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/2.0 Introduction.md b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/2.0 Introduction.md index e1045e6..4af5483 100644 --- a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/2.0 Introduction.md +++ b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/2.0 Introduction.md @@ -2,9 +2,9 @@ ## Introduction -The AppleIIAsm Library Collection is an array of subroutines and macros for the Apple II line of computers, aimed at providing a stable set of assembly routines for most common tasks. Additionally, this library is meant to ease the transition between programming in Applesoft BASIC and 6502 Assembly by not only providing the basic data structures and functions found in higher-level languages but also by providing a set a macros—currently dubbed AppleChop—that simulates the design and workflow of BASIC. A companion booklet to this library, *From Applesoft to AppleChop to Assembly,* provides a framework for making that transition. +The AppleIIAsm Library is an array of subroutines and macros for the Apple II line of computers, aimed at providing a stable set of assembly routines for most common tasks. Additionally, this library is meant to ease the transition between programming in Applesoft BASIC and 6502 Assembly by not only providing the basic data structures and functions found in higher-level languages but also by providing a set a macros--currently dubbed AppleChop--that simulates the design and workflow of BASIC. A companion booklet to this library, *From Applesoft to AppleChop to Assembly,* provides a framework for making that transition. -These subroutines and macros are written for the Merlin 8 Pro assembler, which should run on any Apple II with 64k of memory (programs assembled with Merlin 8 Pro will run on machines with less than 64k, however). Since we are using 6502 Assembly here, however, it should not be too difficult to port the subroutines to other assemblers and even other systems like the Commodore 64, Nintendo Entertainment System, BBC Micro, and more. For a guide on using the Merlin Pro 8 Assembler, see the other companion booklet, *The New Merlin Pro 8 Quick Start User Guide*. +These subroutines and macros are written for the Merlin 8 Pro assembler, which should run on any Apple II with 64k of memory (programs assembled with Merlin 8 Pro will run on machines with less than 64k, however). Since we are using 6502 Assembly here, it should not be too difficult to port the subroutines to other assemblers and even other systems like the Commodore 64, Nintendo Entertainment System, BBC Micro, and more. For a guide on using the Merlin Pro 8 Assembler, see the other companion booklet, *The New Merlin Pro 8 Quick Start User Guide*. It should be noted that the core libraries in this collection---those on disks one to eleven---should be compatible with every iteration of the Apple II family, from the original Apple II in 1977 to the Apple IIGS in 1986. Disks beyond that core collection are considered bonus disks, and are dedicated to either creating libraries for specialized hardware (such as much of the IIGS, or the Mockingboard) or are there to serve as more complicated and integrated demos for the user to peruse. @@ -14,17 +14,17 @@ The primary audience for this manual is someone who is already familiar with 650 **Who is this manual NOT for?** -This manual is definitely not for someone completely unexposed to Assembly language,, but nor is it really aimed at 6502 experts. The collection itself can be used by beginner and expert alike, but whereas this manual would likely confuse the absolute beginner, an expert interested in optimizing their work (and these subroutines) will not find much help here. For those well-versed in 6502 Assembly, this collection might better serve as a prototyping system that then gets torn apart and optimized; for those barely versed in 6502 Assembly, this collection, and especially the AppleChop library, this collection will best serve to ease the transition between a standard knowedge of BASIC and the quirks of Assembly language (commenting is excessive in the libraries for this very reason). Both extremes may find this collection useful, but for wildly different reasons. +This manual is definitely not for someone completely unexposed to Assembly language, but nor is it really aimed at 6502 experts. The collection itself can be used by beginner and expert alike, but whereas this manual would likely confuse the absolute beginner, an expert interested in optimizing their work (and these subroutines) will not find much help here. For those well-versed in 6502 Assembly, this collection might better serve as a prototyping system that then gets torn apart and optimized; for those barely versed in 6502 Assembly, this library, and especially the AppleChop collection, will best serve to ease the transition between a standard knowledge of BASIC and the quirks of Assembly language (commenting is excessive in the libraries for this very reason). Both extremes may find this collection useful, but for wildly different reasons. As someone who spends a *lot* of time thinking about, writing about, and teaching different facets of technical writing (in its broadest sense), I can confirm the following: there are thousands of books written about the 6502 architecture and Assembly programming. I can also confirm that these books---as well as most websites---tend to approach the subject from a "writerly" position rather than a reader-centered one; that is, written for engineers and computer scientists who have already spent a lot of time and money understanding the theory, learning the jargon, and training themselves to be able to do things by muscle memory. That's great for established engineers, mathematicians, computer scientists and the like, as well as those who can afford to dedicate years of their lives (and again, gobs of $$$) to obtain a degree that qualifies them as entry level in the field. It is not so great, however, for beginners, hobbyists, or those trying to study it from a non-engineering theoretical perspective. That is, at least, part of the gap I am hoping to fill; after all, this book is part of my research for writing another book aimed at a non-technical audience. -That said, I myself would have failed quite readily without at least a few key texts and websites, and it would be remiss to not list them here. And if you're committed to learning this, know that there is no good replacement to sitting down, typing out a listing from a book, assembling it and then trying to figure out what the hell you just did---or what you did wrong! There is no doing without learning, and there is no learning without doing. At risk of pontificting, I believe many of us have forgotten this mantra, and the internet has not helped matters. +That said, I myself would have failed quite readily without at least a few key texts and websites, and it would be remiss to not list them here. And if you're committed to learning this, know that there is no good replacement to sitting down, typing out a listing from a book, assembling it and then trying to figure out what the hell you just did---or what you did wrong! There is no doing without learning, and there is no learning without doing. At risk of pontificating, I believe many of us have forgotten this mantra, and the internet has not helped matters. **Why Merlin Pro 8? Why not something...modern?** Understanding how coding for a specific platform and a specific era works is not merely a matter of knowledge, but a matter of practice. Much of the way development happens, in computer software or not, is predicated on the apparatus in place that allows for it. Changing that apparatus, whether it be adding modern components like multiple open files, faster assembly, easier access and legibility and so on changes your understanding of how everything worked (and works). Especially with an ancient (and largely obsolete) language like 6502 assembly, few people are learning it to accomplish a practical task. Instead, we are approaching the topic more like an archaeologist or historical reenactor: going through the same motions to understand the topic cohesively. This is explained more thoroughly in the Preface. -That said, there is nothing inherently wrong with using modern tools—it just does not fit the goals for writing this library. Brutal Deluxe software has rewritten a more modern version of Merlin 16, and the CC65 compiler/assembler makes contemporary 6502 development far more efficient and less frustrating overall. If Merlin 8 Pro feels too dated—and to many, it will feel hopelessly so—by all means use these modern software packages. Just be aware that some substantial effort may be involved in rewriting the code here for different assemblers. If you must, I would suggest using an Apple II emulator like _AppleWin_ with a high speed setting for compile time---though be careful if so, as it is easy to miss error interruptions. +That said, there is nothing inherently wrong with using modern tools--it just does not fit the goals for writing this library. Brutal Deluxe software has rewritten a more modern version of Merlin 16, and the CC65 compiler/assembler makes contemporary 6502 development far more efficient and less frustrating overall. If Merlin 8 Pro feels too dated--and to many, it will feel hopelessly so--by all means use these modern software packages. Just be aware that some substantial effort may be involved in rewriting the code here for different assemblers. If you must, I would suggest using an Apple II emulator like _AppleWin_ with a high speed setting for compile time---though be careful if so, as it is easy to miss error interruptions. **Further Resources** @@ -79,3 +79,11 @@ A number of folk are doing work on 6502 or the Apple II on GitHub. While I canno - [CC65](https://github.com/cc65), a modern cross-compiling C compiler and assembler for 6502 systems. - [PLASMA: The Proto-Language Assembler for All](https://github.com/dschmenk/PLASMA) --- this was originally written for the Apple II alone, but has recently expanded to other systems. + + +--- + + + +[Return to Table of Contents](0.0%20Title_to_TOC) +[Next: Package Overiew](3.0%20Package_Overview.md) \ No newline at end of file diff --git a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/3.0 Package_Overview.md b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/3.0 Package_Overview.md index 6d6d5ff..d3e5f46 100644 --- a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/3.0 Package_Overview.md +++ b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/3.0 Package_Overview.md @@ -1,10 +1,10 @@ ## Package Overview -The **AppleIIAsm** package consists of 25 disks that contain thematically related subroutines, demos and utilities, as well as extra disks that hold minified versions of every subroutine for convenience. The contents of each disk and library are covered in Part II: Detailed Descriptions and Listings. The disks are ordered as follows: +The **AppleIIAsm** package library consists of 25 disks that contain thematically related subroutines, demos and utilities, as well as extra disks that hold minified versions of every subroutine for convenience. The contents of each disk and collection are covered in Part II: Detailed Descriptions and Listings. The disks are ordered as follows: - **Disk 0** BOOT (for setting up own environment) -### CORE LIBRARIES +### CORE COLLECTIONS - **Disk 1** REQUIRED (Required and Common Libraries) - **Disk 2** STDIO (Standard Input and Output Library) @@ -38,3 +38,13 @@ The **AppleIIAsm** package consists of 25 disks that contain thematically relate - **Disk 24** IIGSAUDIO (IIGS AUDIO library) (3.5" diskette) - **Disk 25** IIGSGRAPHICS (IIGS Additional Graphics Mode Libraries) (3.5" diskette) + + +--- + + + + + +[Return to Table of Contents](0.0%20Title_to_TOC) +[Software Architecture](4.0%20Software_Architecture.md) \ No newline at end of file diff --git a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/30.0 Detailed_Reference_D1_REQUIRED.md b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/30.0 Detailed_Reference_D1_REQUIRED.md index a04e087..ef976c6 100644 --- a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/30.0 Detailed_Reference_D1_REQUIRED.md +++ b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/30.0 Detailed_Reference_D1_REQUIRED.md @@ -1,4 +1,4 @@ -# Disk 1 : Required Library and Aliases +# Disk 1 : Required Collection and Aliases - [Part I: The Required Library](#part-i-required-library) - [Required Components](#required-components) @@ -13,7 +13,7 @@ - [The `_MSTR` Macro](#the-mstr-macro) - [Subroutine Shortcuts](#subroutine-shortcuts) - [The `_PRN` Macro](#the-prn-macro) - - [The `__P` Subroutine](#the-p-subroutine) + - [The `__P` Subroutine](#the-p-subroutine) - [The `_WAIT` Macro](#the-wait-macro) - [The `BEEP` Macro](#the-beep-macro) - [Long Branches](#long-branches) @@ -36,22 +36,22 @@ - [Miscellaneous Useful Macros](#miscellaneous-useful-macros) - [The `CLRHI` Macro](#the-clrhi-macro) - [The `DELAY` Macro](#the-delay-macro) - - [The `DELAYMS` Subroutine](#the-delayms-subroutine) + - [The `DELAYMS` Subroutine](#the-delayms-subroutine) - [The `DUMP` Macro](#the-dump-macro) - - [The `__DUMP` Subroutine](#the-dump-subroutine) + - [The `__DUMP` Subroutine](#the-dump-subroutine) - [The `ERRH` Macro](#the-errh-macro) - - [The `__ERRH` Subroutine](#the-errh-subroutine) + - [The `__ERRH` Subroutine](#the-errh-subroutine) - [The `GBIT` Macro](#the-gbit-macro) - [The `GRET` Macro](#the-gret-macro) - [Memory Macros and Subroutines](#memory-macros-and-subroutines) - [The `PEEK` Macro](#the-peek-macro) - [The `POKE` Macro](#the-poke-macro) - [The `MFILL` Macro](#the-mfill-macro) - - [The `MEMFILL` Subroutine](#the-memfill-subroutine) + - [The `MEMFILL` Subroutine](#the-memfill-subroutine) - [The `MOVB` Macro](#the-movb-macro) - - [The `MEMMOVE` Subroutine](#the-memmove-subroutine) + - [The `MEMMOVE` Subroutine](#the-memmove-subroutine) - [The `MSWAP` Macro](#the-mswap-macro) - - [The `MEMSWAP` Subroutine](#the-memswap-subroutine) + - [The `MEMSWAP` Subroutine](#the-memswap-subroutine) - [Part II: Aliases](#part-ii-aliases) - [8080 Instruction Set Macro Substitutions](#8080-instruction-set-macro-substitutions) - [Z80 Family Instruction Set Macro Substitutions](#z80-family-instruction-set-macro-substitutions) @@ -82,9 +82,9 @@ ## PART I: REQUIRED LIBRARY -The first disk in the AppleIIAsm Collection includes all of the macros, subroutines, vectors and reserved memory locations required for the rest of the library to function properly. Note that "functioning properly" only applies to unmodified codebases; highly optimized code that makes the library barely recognizable may not use the same standard procedures. +The first disk in the AppleIIAsm Library includes all of the macros, subroutines, vectors and reserved memory locations required for the rest of the library to function properly. Note that "functioning properly" only applies to unmodified codebases; highly optimized code that makes the library barely recognizable may not use the same standard procedures. -You may consider the files on this disk to provide the overall software architecture for the rest of the library (as outline in the Software Architecture section). However, one of the goals is to be able to remove or alter major components of the architecture--the girders, you might say--in order to lighten the building as a whole while still keeping its basic structure intact. That is, a good deal of effort is done to make this package of libraries as easy for beginners to use as possible while also allowing for substantial changes to the architecture to allow for further optimization after the fact. +You may consider the files on this disk to provide the overall software architecture for the rest of the library (as outline in the Software Architecture section). However, one of the goals is to be able to remove or alter major components of the architecture--the girders, you might say--in order to lighten the building as a whole while still keeping its basic structure intact. That is, a good deal of effort is done to make this package of collections as easy for beginners to use as possible while also allowing for substantial changes to the architecture that allows for further optimization after the fact. @@ -97,11 +97,11 @@ Basically, the following components will be first covered in this chapter, via l - Most common kinds of parameter passing - Checking for a string or an address being passed as a parameter -Since this will encompass the same group of macros, the information will be contained in a section of its own However, many more macros have yet to be covered, and will be so largely in alphabetical order. There are times, however, when it makes sense to group together subroutines or macros non-alphabetically; this is done on occasion. +Since this will encompass the same group of macros, the information will be contained in a section of its own However, many more macros have yet to be covered, and will be so largely in alphabetical order. There are times, additionally, when it makes sense to group together subroutines or macros non-alphabetically; this is done on occasion. -The next components are the `_PRN` and `_WAIT` macros, alternatives of which are technically found on other disks but are useful enough to also include in the required library; the subroutines for these will be covered as well..\ However, following that will be a long series of macros that being with "B," which are versions of 6502 branch instructions that allow for longer calls. These are useful enough to include in the required library, and take up little enough space in terms of bytes and cycles that their inclusion here is negligible. +The next components are the `_PRN` and `_WAIT` macros, alternatives of which are technically found on other disks but are useful enough to also include in the required collection; the subroutines for these will be covered as well. However, following that will be a long series of macros that being with "B," which are versions of 6502 branch instructions that allow for longer calls. These are useful enough to include in the required collection, and take up little enough space in terms of bytes and cycles that their inclusion here is negligible. -Following the branching macros, two macros dedicated to clear the highest and lowest nibbles of the value in .A are included, following then a series of macros beginning with "C" that emulate some of the functionality found in the 65C02 processor. Additionally, a group of mostly unrelated macros are listed, along with their corresponding subroutines, that are most often used for debugging. Then, finally, 8080 and 780 instruction set macro emulations will be covered briefly, for use by those who have yet to fully internalize the 6502 instruction set. +Following the branching macros, two macros dedicated to clear the highest and lowest nibbles of the value in **.A** are included, following then a series of macros beginning with "C" that emulate some of the functionality found in the 65C02 processor. Additionally, a group of mostly unrelated macros are listed, along with their corresponding subroutines, that are most often used for debugging. Then, finally, 8080 and Z80 instruction set macro emulations will be covered briefly, for use by those who have yet to fully internalize the 6502 instruction set. --- @@ -111,7 +111,7 @@ Following the branching macros, two macros dedicated to clear the highest and lo _SUMMARY_ -Without the required header file, almost nothing in the rest of the collection will function properly. This is because certain areas of memory defined in the header file are used by their libraries for holding variable data, referring to zero-page locations, and so on. The listing is as follows, with explanations afterwards (beyond the inline comments): +Without the required header file, almost nothing in the rest of the collection will function properly. This is because certain areas of memory defined in the header files are used by their collections for holding variable data, referring to zero-page locations, and so on. The LISTING is as follows, with explanations afterwards (beyond the inline comments): | Condition | Value | | --- | --- | @@ -121,10 +121,13 @@ Without the required header file, almost nothing in the rest of the collection w | Last Revision | 29-NOV-2019 | | Assembler | Merlin 8 Pro | | OS | Apple DOS 3.3 | -| Purpose | Initialize system for use by rest of libraries | +| Purpose | Initialize system for use by rest of library | | Dependencies | none | | Bytes | 311 | | Notes | Required for nearly every macro or subroutine | +| See Also | none | + + --- @@ -132,7 +135,7 @@ Without the required header file, almost nothing in the rest of the collection w First we have the file header, which should be the first section of every file regardless of its type. This includes a short description of what the file contains, last update, contact information, and so on. This file header additionally contains the number of bytes the file uses, and this one in particular uses an unusually substantial number of bytes for a header file (in the optimizations appendix, you'll find ways to reduce the number of bytes here). -`Listing 1.0: HEAD.REQUIRED File Heading` +`LISTING 1.0: HEAD.REQUIRED File Heading` ```asm * @@ -165,11 +168,11 @@ What then follows is 256 bytes dedicated to values returned by subroutines. The The rest of the header defines certain hooks and variables that the rest of the libraries depend on. For instance, `ADDR1` through `ADDR4` are often used for indirect addressing modes, and are helpful in the sense that they are all sequential: a high nibble precedes a low nibble, for instance, in **$06** and **$07**. Following the addressing indices, scratchpad locations on the zero page are identified as `SCRATCH` and `SCRATCH2`, though `SCRATCH` is most often used for backing up **.Y** values before and after a macro and thus should rarely be used beyond that capacity. The last variable, `RETVAL`, is used on the rare occasion when usage of the stack requires a return location to be backed up elsewhere. -The hooks `RESULT` and `RESULT2` are rarely used, though this may change in the future. The following word and byte parameter passing hooks, however, are likely the most used parts of the zero page in this collection: `WPAR1`, `WPAR2`,`WPAR3` and `WPAR4` are all used primarily for passing word-length values (16-bits) to subroutines, and have sequential memory locations, whereas `BPAR1`, `BPAR2`, `BPAR3` and `BPAR4` are used for passing single bytes (these do not have sequential free memory locations, and are thus unsuitable for anything by bytes) . +The hooks `RESULT` and `RESULT2` are rarely used, though this may change in the future. The following word and byte parameter passing hooks, however, are likely the most used parts of the zero page in this collection: `WPAR1`, `WPAR2`,`WPAR3` and `WPAR4` are all used primarily for passing word-length values (16-bits) to subroutines, and have sequential memory locations, whereas `BPAR1`, `BPAR2`, `BPAR3` and `BPAR4` are used for passing single bytes (these do not have sequential free memory locations, and are thus unsuitable for anything but bytes) . -Finally, the warm DOS `REENTRY` hook is defined, which is required to be jumped to at the end of each and every program (for all intents and purposes, anyhow). A few hooks are defined for memory operations, and then we have the `MAIN START` label, which signals the beginning of the actual code of the program that follow. +Finally, the warm DOS `REENTRY` hook is defined, which is required to be jumped to at the end of each and every program (for all intents and purposes, anyhow). A few hooks are defined for memory operations, and then we have the `MAIN START` label, which signals the beginning of the actual code of the program that follows. -`Listing 1.1: HEAD.REQUIRED CODE` +`LISTING 1.1: HEAD.REQUIRED Source` ```asm * @@ -279,11 +282,16 @@ MAIN_START +--- + + + ## Next Up: Required Macros -As stated in earlier sections of this text, each file contains its own heading with information about the content therein; the required macros file is no different. As will all libraries in AppleIIAsm, we will begin with macros because they are the most used (and most useful) way to interface with the collection of libraries. If a macro uses a particular subroutine, that subroutine will be discussed in relationship to the macro rather than be introduced as a separate entity. First, however, we'll list the macro file Header to stress again how such headers will be presented in the future. +As stated in earlier sections of this text, each file contains its own heading with information about the content therein; the required macros file is no different. As will all collections in the AppleIIAsm library, we will begin with macros because they are the most used (and most useful) way to interface with the collection of libraries. If a macro uses a particular subroutine, that subroutine will be discussed in relationship to the macro rather than be introduced as a separate entity. First, however, we'll list the macro file header to stress again how such headers will be presented in the future. + +`LISTING 1.2: MAC.REQUIRED File Heading` -`Listing 1.2: MAC.REQUIRED File Heading` ```ASM * *``````````````````````````````* @@ -374,12 +382,14 @@ As stated in earlier sections of this text, each file contains its own heading w -A typical macro file header includes the name of the file at the top, followed by a short paragraph that describes what these macros are for. Then, the (main) author and his or her contact information is listed, along with the last date of revision, the Operating system targeted, and the Assembler used. Unless this project grow far beyond my original intentions, these will all likely remain the same, save for revision dates. +A typical macro file header includes the name of the file at the top, followed by a short paragraph that describes what these macros are for. Then, the (main) author and his or her contact information is listed, along with the last date of revision, the operating system targeted, and the assembler used. Unless this project grow far beyond my original intentions, these will all likely remain the same, save for revision dates. -What follows then is a listing of all subroutine files that are used by the macros in the file; luckily, there is only one subroutine file in use for the required library. After that, a listing of macros available in the file are listed, with extremely brief descriptions of what they are for; more detailed descriptions can be found in the headers of the macros themselves. A bit strangely, this macro file header contains fewer subroutine listings that most others and more macro listing than most other file headers, as you will see. +What follows then is a LISTING of all subroutine files that are used by the macros in the file; luckily, there is only one subroutine file in use for the required library. After that, a LISTING of macros available in the file are listed, with extremely brief descriptions of what they are for; more detailed descriptions can be found in the headers of the macros themselves. A bit strangely, this macro file header contains fewer subroutine LISTINGs that most others and more macro LISTING than most other file headers, as you will see. Also note that this header carries with it some key variables that are used in the rest of the library. This is usually relegated to the HEAD file of a library, but assembly variables, rather than vectors, are often defined within the file they are used. + + --- @@ -392,8 +402,12 @@ Yet what the macros _really_ do is still up for grabs. Before describing each on While these all behave much the same, there are some important differences worth noting, thus good reasoning for tearing apart each macro line-by-line. Note that a subroutine always expects a particular macro to pass parameters; if the wrong macro is used, the subroutine will either freeze or return an error. Instead of complete consistency in terms of passing data to subroutines, we have instead opted for efficiency. + + --- + + ### THE \_AXLIT MACRO _SUMMARY_ @@ -412,16 +426,18 @@ _SUMMARY_ | Dependencies | none | | Flags Destroyed | NZ | | Cycles | 8 | -| Bytes | 6 (every use) | +| Bytes | 6 | | Notes | note that literal values are *never* actually used as literal values, in any macro or subroutine. | +| See Also | `_AXSTR` `_ISLIT` `_ISSTR` `_MLIT` `_MSTR` | --- *DETAILS* -Let us first look at the `_AXLIT` macro, as it serves as the first macro alphabetically as well as succinctly illustrates how the other macros work as well. But first, just like with everything else, we should start with the heading. +Let us first look at the `_AXLIT` macro, as it serves as the first macro alphabetically as well as succinctly illustrates how the other macros work as well. First, just like with everything else, we should start with the heading. + +`LISTING 1.3: _AXLIT Macro Heading` -`Listing 1.3: _AXLIT Macro Heading` ```asm * *``````````````````````````````* @@ -443,13 +459,13 @@ Let us first look at the `_AXLIT` macro, as it serves as the first macro alphabe -At first, you should notice that each and every macro (and subroutine, for that matter) also includes its own heading, like each file. These headings differ in focus, however: while the name and short description remain the same, there is also a listing of parameters that are passed to the macro as well as the number of cycles and bytes a macro will use every time it is called. Note that this is slightly different than a subroutine; since a subroutine is listed only once in memory, it only uses the number of bytes listed once, and does not add to the byte count with any further usage. This is primarily why subroutines and macros exist separately: sometimes it costs more to `JSR` to a subroutine and then `RTS` back to the main listing than it would take to simply write out the entire subroutine and add more bytes. There is far more to it than this, of course, but this is a good foundation for understanding a key difference, especially in terms of optimization. +At first, you should notice that each and every macro (and subroutine, for that matter) also includes its own heading, like each file. These headings differ in focus, however: while the name and short description remain the same, there is also a LISTING of parameters that are passed to the macro as well as the number of cycles and bytes a macro will use. In parentheses, the number of bytes and cycles used for every time the macro is called are listed; that this is slightly different than a subroutine. Since a subroutine is listed only once in memory, it only uses the number of bytes listed once, and does not add to the byte count with any further usage. This is primarily why subroutines and macros exist separately: sometimes it costs more to `JSR` to a subroutine and then `RTS` back to the main LISTING than it would take to simply write out the entire subroutine and add more bytes. There is far more to it than this, of course, but this is a good foundation for understanding a key difference, especially in terms of optimization. Note that `_AXLIT` expects a single parameter, denoted by **]1**, and will *at least* use up 8 cycles and 6 bytes per usage. Note that I tend to take the pessimists side, as you'll soon see, and would rather add possible cycles or bytes rather than assume fewer of each. One runs into the problem of execution being too slow far more often than one does too fast. -Now let's look at the `_AXLIT` listing itself, explaining both what each line accomplishes as well as some of the more peculiar notations used in the inline comments. +Now let's look at the `_AXLIT` LISTING itself, explaining both what each line accomplishes as well as some of the more peculiar notations used in the inline comments. -`Listing 1.4: _AXLIT Macro Code` +`LISTING 1.4: _AXLIT Macro Code` ```asm @@ -466,9 +482,9 @@ _AXLIT MAC ; CHECK IF LITERAL -Warning: If you are unfamiliar with how Assembly language is formatted, this is not the best place to learn so; please see an introductory guide before attempting to read this document any further. Your best bet would be to start with the first few chapters of the first book in this technical documentation series, _A New Users' Guide to Merlin 8 Pro_, to familiarize yourself with the interface, then move to the next book,_From AppleSoft to AppleChop to ASM_ to move from a BASIC mindset (or Pascal, etc.) to an Assembly mindset. Of course, you are more than welcome to use other resources instead. +Warning: If you are unfamiliar with how Assembly language is formatted, this is not the best place to learn so; please see an introductory guide before attempting to read this document any further. Your best bet would be to start with the first few chapters of the first book in this technical documentation series, _A New Users' Guide to Merlin 8 Pro_, to familiarize yourself with the interface, then move to the next book, _From AppleSoft to AppleChop to ASM_ to move from a BASIC mindset (or Pascal, etc.) to an Assembly mindset. Of course, you are more than welcome to use other resources instead. -This macro, of course, begins by declaring `_AXLIT` as a macro on the first line \(the lines are not numbered, so be prepared for some confusion with longer macro and subroutine listings\). The second line uses what is called a "Pseudo Opcode" provided by the Merlin Pro 8 Assembler to test whether the first character of the first \(and only\) parameter is a pound sign, which denotes whether the value being sent is a literal value or an address. If this happens to be a literal, then the next line divides the byte's high nibble of the parameter by $100 and stores it in **.X**, then loads the low nibble in **.A**. +This macro, of course, begins by declaring `_AXLIT` as a macro on the first line \(the lines are not numbered, so be prepared for some confusion with longer macro and subroutine LISTINGs\). The second line uses what is called a "Pseudo Opcode" provided by the Merlin Pro 8 Assembler to test whether the first character of the first \(and only\) parameter is a pound sign, which denotes whether the value being sent is a literal value or an address. If this happens to be a literal, then the next line divides the byte's high nibble of the parameter by $100 and stores it in **.X**, then loads the low nibble in **.A**. But why? The reasoning is simple enough: when a literal value is passed as a parameter, it is still treated as an address, but one that indirectly points to another address. This provides all memory locations with the capability of zero-page addresses indirectly pointing to an address. This does not mean that these memory locations can be used as indirect addresses in the source code, but it does mean that user-inputted data (or file-inputted data) can be addressed indirectly without overburdening the zero page. Thus, when "#" is detected as the first character, it is assumed that the actual address being passed is stored in the literal value converted into an address. @@ -478,10 +494,14 @@ If the first character is not a pound sign, however, then the parameter is assum Rather quickly, you may notice some strange notation at the beginning of some lines the inline comments, like the third line's _{4C3B}_. This notation documents how many cycles and bytes that the line has added to the total (four cycles, three bytes), and the notation allows for the code minifier utility to skip or include this information in the minified version of the macro or subroutine. This notation is also used at the end of a comment to let the user know which status flags are affected. Again, note that in terms of bytes, this means something different depending on whether it is found in a macro or a subroutine: in a macro, the bytes are always added at each call, whereas in a subroutine the bytes are only added once. -There are other inline comment notations that will be encountered later on---most notably, the double-minus, which says to keep the entire line during minification---but we will describe those as they are encountered. A full list of these meta-notations can be found in Appendix XXX. +There are other inline comment notations that will be encountered later on---most notably, the double-minus, which says to keep the entire line during minification---but we will describe those as they are encountered. A full list of these meta-notations can be found in [Appendix XXX](). + + --- + + ### THE \_AXSTR MACRO _SUMMARY_ @@ -502,6 +522,7 @@ _SUMMARY_ | Cycles | 11 | | Bytes | 9+ string bytes (every use) | | Notes | If the parameter is found to be an address, it is further sent to
the _AXLIT macro for more processing. | +| See Also | `_AXLIT` `_ISLIT` `_ISSTR` `_MLIT` `_MSTR` | --- @@ -511,7 +532,7 @@ While the `_AXSTR` macro works much the same as `_AXLIT`, there are some importa One problem with using the `_AXSTR` macro, however, is that bytes will accumulate rather quickly, turning what should be 1K program into 10K-15K in no time. This is because of the way that the `_AXSTR` macro works, as well as with `_ISSTR` and `_MSTR`. But to understand how this works, let's first look again at the macro header: -`Listing 1.5: _AXSTR Macro Heading` +`LISTING 1.5: _AXSTR Macro Heading` ```ASM * @@ -540,7 +561,7 @@ One problem with using the `_AXSTR` macro, however, is that bytes will accumulat For the most part, there is nothing to worry about here: this is a standard macro header. However, note that in the BYTES section, you'll see "9 + STRING BYTES." This means that for each time `_AXSTR` is called with a literal string as a parameter, that string is declared somewhere to become part of the byte-length of the total! A 9 byte macro, then, can easily become a 100 byte macro, and when used over and over again, those bytes can add up. Let's see how this works via the source in question: -`Listing 1.6: _AXSTR Macro Code` +`LISTING 1.6: _AXSTR Macro Code` ```asm @@ -565,6 +586,12 @@ An alternative way to do this, which might be implemented in the near future, is Beyond that, if the first character is not a quotation mark, the parameter is then tested by `_AXLIT` to determine whether it is literal or indirect address being passed to the macro. The `IF-ELSE` opcode is then ended with `FIN` and the macro is ended with `<<<`, returning control to main execution and likely running the appropriate subroutine. + + +--- + + + ### THE \_ISLIT MACRO _SUMMARY_ @@ -577,14 +604,15 @@ _SUMMARY_ | Last Revision | 29-NOV-2019 | | Assembler | Merlin 8 Pro | | OS | Apple DOS 3.3 | -| Purpose | | +| Purpose | Check if parameter sent is a literal address or not | | Input | ]1 = memory address byte | -| Output | | +| Output | none | | Dependencies | none | | Flags Destroyed | NZ | | Cycles | 14 | | Bytes | 8 | -| Notes | | +| Notes | none | +| See Also | `_AXLIT` `_AXSTR` `_ISSTR` `_MLIT` `_MSTR` | --- @@ -592,7 +620,7 @@ _DETAILS_ `_ISLIT`, like `_AXLIT` before it, checks if a parameter is a literal or indirect address pass, then parses the address appropriately for an awaiting subroutine. At this point, it would be fairly pointless to separate the header from the rest of the parameter-parsing routines, as the macros here all behave alike, so we will now list them together before dissecting the code. -`Listing 1.7: _ISLIT Macro Heading and Code` +`LISTING 1.7: _ISLIT Macro Heading and Code` ```asm * @@ -633,6 +661,12 @@ _ISLIT MAC ; CHECK IF LITERAL Like before, `FIN` acts as an `END-IF` while `<<<` indicates the end of the macro. Such instructions will not be explained again for the rest of this section on parameter-passing macros. + + +--- + + + ### The \_ISSTR MACRO _SUMMARY_ @@ -645,14 +679,15 @@ _SUMMARY_ | Last Revision | 29-NOV-2019 | | Assembler | Merlin 8 Pro | | OS | Apple DOS 3.3 | -| Purpose | | +| Purpose | Checks if a literal string is passed
as a parameter | | Input | ]1 = memory address byte | -| Output | | +| Output | none | | Dependencies | `_ISLIT` | | Flags Destroyed | NZ | | Cycles | 13 or 8 | | Bytes | 10+ or 6 | -| Notes | | +| Notes | none | +| See Also | `_AXLIT` `_AXSTR` `_ISLIT` `_MLIT` `_MSTR` | --- @@ -660,7 +695,7 @@ _DETAILS_ The `_ISSTR` macro once again works like the `_AXSTR` macro, except that again the parameter pointers are passed via the stack rather than the registers. The first parameter is checked whether it is a literal string; if so, the string is then created on-the-fly and a pointer to that address is pushed to the stack, high byte first then low byte. Otherwise, the parameter is assumed to be an address and is again checked to be a literal; if so, the address is sent to `_ISLIT` for further parsing. -`Listing 1.8: _ISSTR Macro Heading and Code` +`LISTING 1.8: _ISSTR Macro Heading and Code` ```asm * @@ -698,6 +733,11 @@ __STRCONT ``` + +--- + + + ### THE \_MLIT MACRO _SUMMARY_ @@ -710,20 +750,21 @@ _SUMMARY_ | Last Revision | 29-NOV-2019 | | Assembler | Merlin 8 Pro | | OS | Apple DOS 3.3 | -| Purpose | | +| Purpose | Checks if parameter is a literal address
then puts appropriate value in zero memory location | | Input | ]1 = memory address byte | -| Output | | +| Output | none | | Dependencies | none | | Flags Destroyed | NZ | | Cycles | 16 | | Bytes | 12 | -| Notes | | +| Notes | none | +| See Also | `_AXLIT` `_AXSTR` `_ISLIT` `_ISSTR` `_MSTR` | --- _DETAILS_ -The `_MLIT` macro passes address pointer parameters to a subroutine via predetermined locations on the zero page---those defined by `WPAR1` through `WPAR4` for 16-bit values and those between `BPAR1` through `BPAR4` for single-byte values. Unlike previous macros, `_MLIT` now accepts and introduces the concept of two parameters, defined by the symbols **]1** for the first parameter and **]2** for the second. The first parameter should already be familiar: it's the pointer to the address being passed, and may be either a literal value or otherwise. The second parameter, however, is a bit more complicated due to the fact that the address passed there **must** match the address expected by the subroutine that follows the macro. This is why the hooks `WPAR1` or `BPAR` are provided in the first place: to provide an easy-to-remember method of passing a variable via zero-page addresses. +The `_MLIT` macro passes address pointer parameters to a subroutine via predetermined locations on the zero page---those defined by `WPAR1` through `WPAR4` for 16-bit values and those between `BPAR1` through `BPAR4` for single-byte values. Unlike previous macros, `_MLIT` now accepts and introduces the concept of two parameters, defined by the symbols **]1** for the first parameter and **]2** for the second. The first parameter should already be familiar: it's the pointer to the address being passed, and may be either a literal value or otherwise. The second parameter, however, is a bit more complicated due to the fact that the address passed there **must** match the address expected by the subroutine that follows the macro. This is why the locations `WPAR1` or `BPAR` are provided in the first place: to provide an easy-to-remember method of passing a variable via zero-page addresses. Like with the order of passing parameters on the stack, it is extremely important that the same memory locations be used in sending the parameters that the subroutine will be expecting--otherwise, it will be working with the incorrect data, leading to errors or a frozen computer. To pass more than a single parameter, the a semicolon is used to separate the values, as such: @@ -733,7 +774,7 @@ This passes the contents found at the address held in **$300** via `WPAR1` on t `_MLIT` mostly works like the parameter-passing macros before it. The first parameter is checked as to whether it is a literal or not; if it is, then the address is divided by **$100** to retrieve the high byte of the address and this is stored in the provided variable location's high byte (say, `_MLIT1`). The low byte is then copied to the low byte are of `_MLIT1.` If the parameter is not a literal value, then the first parameter is copied to the second parameter, both low byte and high byte, in a straight-forward manner. -`Listing 1.9: _MLIT Macro Heading and Code` +`LISTING 1.9: _MLIT Macro Heading and Code` ```asm * @@ -769,8 +810,12 @@ _MLIT MAC ; CHECK IF LITERAL <<< ``` + + --- + + ### THE \_MSTR MACRO _SUMMARY_ @@ -783,14 +828,15 @@ _SUMMARY_ | Last Revision | 29-NOV-2019 | | Assembler | Merlin 8 Pro | | OS | Apple DOS 3.3 | -| Purpose | | +| Purpose | Check if parameter is a string, then
passes pointer via the zero page | | Input | ]1 = memory address byte | -| Output | | +| Output | none | | Dependencies | `_ISLIT` | | Flags Destroyed | NZ | | Cycles | 15 or 8 | | Bytes | 14+ or 6 | -| Notes | | +| Notes | none | +| See Also | `_AXLIT` `_AXSTR` `_ISLIT` `_ISSTR` `_MLIT` | --- @@ -800,7 +846,7 @@ Lastly, the `_MSTR` macro is used to pass a string or string address to a subrou The first parameter is checked firsthand for a preceding quotation mark, signaling that it is a literal string; if so, the string is stored in memory and the pointer to that particular address is then stored in the second parameter address, byte-for-byte. If the first parameter is not a string, this the parameter is then checked by `_MLIT` and treated appropriately before being finally prepared for passage to a subroutine. -`Listing 1.10: _MSTR Macro Heading and Code` +`LISTING 1.10: _MSTR Macro Heading and Code` ```asm * @@ -836,15 +882,18 @@ __STRCONT ; CONTINUE _ISLIT ]1 ; {8C6B} CHECK IF LITERAL FIN ; END IF <<< ; END MACRO + ``` + + --- ## SUBROUTINE SHORTCUTS -There are times when you will either want to quickly print some text to the screen or wait for the user to press a key before continuing without importing the macros from the STDIO library. This is especially useful when debugging and writing demo programs for libraries separate from STDIO, as you will see. To accommodate this need, the `_PRN` and the `_WAIT` macros have been included in the required library. Note, however, that these are severely limited macros and corresponding subroutines; when anything remotely complicated or versatile is necessary, it is advisable to use the STDIO library. +There are times when you will either want to quickly print some text to the screen or wait for the user to press a key before continuing without importing the macros from the STDIO collection. This is especially useful when debugging and writing demo programs for collections separate from STDIO, as you will see. To accommodate this need, the `_PRN` and the `_WAIT` macros have been included in the required library. Note, however, that these are severely limited macros and corresponding subroutines; when anything remotely complicated or versatile is necessary, it is advisable to use the STDIO library. ### THE \_PRN MACRO @@ -858,22 +907,23 @@ _SUMMARY_ | Last Revision | 29-NOV-2019 | | Assembler | Merlin 8 Pro | | OS | Apple DOS 3.3 | -| Purpose | | +| Purpose | print a literal string to COUT | | Input | ]1 = memory address byte | -| Output | | -| Dependencies | none | +| Output | string is displayed | +| Dependencies | `__P` | | Flags Destroyed | NZCV | | Cycles | 161+ | | Bytes | 9+ | -| Notes | | +| Notes | none | +| See Also | `__P` | --- _DETAILS_ -The `_PRN` macro is a quick and dirty method of printing to the screen via COUT. Primarily, this macro is used when the STDIO library is not being used but some output needs to be put onto the screen, especially in terms of debugging. It accepts a single parameter that is either a literal string---nothing more, nothing less. Note that this macro, as you'll see in the source, can quickly add bytes to your program's total byte size. Let's skip the `_PRN` macro's header to be closer to the source we are interpreting. +The `_PRN` macro is a quick and dirty method of printing to the screen via **COUT**. Primarily, this macro is used when the **STDIO** collection is not being used but some output needs to be put onto the screen, especially in terms of debugging. It accepts a single parameter that is either a literal string---nothing more, nothing less. Note that this macro, as you'll see in the source, can quickly add bytes to your program's total byte size. Let's skip the `_PRN` macro's header to be closer to the source we are interpreting. -`Listing 1.11: _PRN Macro Heading` +`LISTING 1.11: _PRN Macro Heading` ```asm * @@ -892,11 +942,11 @@ The `_PRN` macro is a quick and dirty method of printing to the screen via COUT. *,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* * ``` -As usual, the first line of the macro defines its name; in this case, `_PRN`. The second line backs up the value in .Y as a courtesy to the programmer of the calling routine, since .Y is often used as a looping counter \(most all subroutines follow this same example, though there are a few exceptions\). The third line, however, may come as a surprise: the subroutine `__P` is called, despite apparently no preparations for passing it a string parameter. While this will become more apparent once the `__P` subroutine is discussed, for now it suffices to say that what is passed to the `__P` subroutine is the current program counter itself, as it is pushed to the stack by the JSR opcode. +As usual, the first line of the macro defines its name; in this case, `_PRN`. The second line backs up the value in **.Y** as a courtesy to the programmer of the calling routine, since **.Y** is often used as a looping counter \(most all subroutines follow this same example, though there are a few exceptions\). The third line, however, may come as a surprise: the subroutine `__P` is called, despite apparently no preparations for passing it a string parameter. While this will become more apparent once the `__P` subroutine is discussed, for now it suffices to say that what is passed to the `__P` subroutine is the current program counter itself, as it is pushed to the stack by the `JSR` opcode. -After the subroutine is called, somewhat confusingly at first, an ASC is declared with the string passed as a parameter to the `_PRN` macro, followed by a HEX byte of $00. **.Y** is then loaded back with its previously backed up value, and control is returned to the main routine. +After the subroutine is called, somewhat confusingly at first, an `ASC` is declared with the string passed as a parameter to the `_PRN` macro, followed by a HEX byte of $00. **.Y** is then loaded back with its previously backed up value, and control is returned to the main routine. -`Listing 1.12: _PRN Macro Code` +`LISTING 1.12: _PRN Macro Code` ```asm _PRN MAC ; PRINT STRING @@ -907,10 +957,16 @@ _PRN MAC ; PRINT STRING LDY SCRATCH ; {3C2B} RESTORE .Y {NZ} <<< ; END MACRO ``` -Of course, the big question is: how does `__P` know what to print? Those of you fairly well-versed will immediately recognize the trick---and a dirty trick it is. Let's look at the source code for the `__P` subroutine to see exactly how this works. In the meantime, this will also be the first subroutine examined of all subroutines in the collection, so we may have to explain a bit more than the subroutine would usually merit. +Of course, the big question is: how does `__P` know what to print? Those of you fairly well-versed will immediately recognize the trick--and a dirty trick it is. Let's look at the source code for the `__P` subroutine to see exactly how this works. In the meantime, this will also be the first subroutine examined of all subroutines in the collection, so we may have to explain a bit more than the subroutine would usually merit. The source is as follows. First and foremost, of course, we have the subroutine header. This differs slightly from the header of a macro in that it specifies methods of input and output rather than feign any parameters, and the header details any of the status registers it alters \(or, in the lingo of the header, "destroys"\). Again also note that since a subroutine is placed into memory only once, unlike a macro, the number of bytes it uses does not increase or multiply on each use. + + +--- + + + ### THE \_\_P SUBROUTINE _SUMMARY_ @@ -923,20 +979,21 @@ _SUMMARY_ | Last Revision | 29-NOV-2019 | | Assembler | Merlin 8 Pro | | OS | Apple DOS 3.3 | -| Purpose | | +| Purpose | print text to the screen | | Input | `ASC` string trailing call to subroutine | -| Output | | +| Output | Literal string on the display | | Dependencies | none | | Flags Destroyed | NZCV | | Cycles | 155+ | | Bytes | 35 | -| Notes | | +| Notes | none | +| See Also | `_PRN` | --- _DETAILS_ -`Listing 1.13: __P Subroutine Heading` +`LISTING 1.13: __P Subroutine Heading` ```asm * @@ -964,13 +1021,13 @@ As the INPUT section of the header reveals, this subroutine reads the lines _aft The first line, `__P`, declares the start of the subroutine. Afterwards, the first thing that happens is a `PLA` opcode, which pulls the top value off of the stack and stores it in **.A**. This is then stored in the low byte of `ADDR1`, which is, if you don't recall, a 16-bit group on the zero page meant to hold addresses that will be accessed indirectly. Next another byte is pulled from the stack and stored in **.A**, then transferred to `ADDR1` + 1; that is, it is the high byte of `ADDR1`. Essentially, what has just been pulled from the stack is the address of the instruction that called this subroutine from the macro. -Next, a **.Y** offset of 1 is established for indirectly accessing the contents off the calling address+1 byte---that is, the ASC data following the call to `__P` in the macro! This line just so happens to begin with the `:LP` label so that (As per Merlin's rules, the preceding colon means that it is a local label) .**.Y** can be increased and then the next byte in the string printed. This is repeated until the `$00 HEX` byte is encountered, at which point the entire string has been printed to the screen. +Next, a **.Y** offset of 1 is established for indirectly accessing the contents off the calling address+1 byte--that is, the ASC data following the call to `__P` in the macro! This line just so happens to begin with the `:LP` label so that (As per Merlin's rules, the preceding colon means that it is a local label) .The **.Y** register can be increased and then the next byte in the string printed. This is repeated until the `$00 HEX` byte is encountered, at which point the entire string has been printed to the screen. The loop is then ended, as `BNE :LP` indicates that as long as .A is not holding $00, the loop should continue. We have hit the $00, so we now continue to the next line, labeled `:DONE`, where we clear the carry bit. We now transfer the offset value stored in **.Y** to **.A**, and add that to the original address we pulled from the stack, effectively skipping the string and `#HEX $00` to the next address pointer. This address is then pushed to the stack and JSR is called, an opcode that jumps to the address at the top of the stack. We have now officially left the subroutine, and returned to the macro---except now the program counter points at the line after `HEX $00`, the `LDY SCRATCH` line, which restores the original **.Y** value before the macro was called. The macro is then ended with `<<<`, and our first subroutine adventure is complete! -`Listing 1.14: __P Subroutine Code` +`LISTING 1.14: __P Subroutine Source` ```asm __P @@ -997,26 +1054,32 @@ __P ``` + +--- + + + ### THE \_WAIT MACRO _SUMMARY_ -| Condition | Value | -| --------------- | ------------------------ | -| Name | `_WAIT` | -| Type | Macro | -| Author | Nathan Riggs | -| Last Revision | 29-NOV-2019 | -| Assembler | Merlin 8 Pro | -| OS | Apple DOS 3.3 | -| Purpose | | -| Input | ]1 = memory address byte | -| Output | | -| Dependencies | none | -| Flags Destroyed | NZ | -| Cycles | 14+ | -| Bytes | 10 | -| Notes | | +| Condition | Value | +| --------------- | ------------------------------------- | +| Name | `_WAIT` | +| Type | Macro | +| Author | Nathan Riggs | +| Last Revision | 29-NOV-2019 | +| Assembler | Merlin 8 Pro | +| OS | Apple DOS 3.3 | +| Purpose | halt execution until a key is pressed | +| Input | ]1 = memory address byte | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZ | +| Cycles | 14+ | +| Bytes | 10 | +| Notes | none | +| See Also | none | --- @@ -1024,7 +1087,7 @@ _DETAILS_ Thankfully, the next subroutine we'll be exploring is much simpler, as well as the macro that calls it. The `_WAIT` macro simply calls the `__W` subroutine, which then loops until a key is pressed on the keyboard or gamepad. -`Listing 1.15: _WAIT Macro Heading and Code` +`LISTING 1.15: _WAIT Macro Heading and Source` ```asm * @@ -1048,6 +1111,11 @@ _WAIT MAC ; WAIT FOR KEYPRESS ``` + +--- + + + ### THE BEEP MACRO _SUMMARY_ @@ -1062,12 +1130,13 @@ _SUMMARY_ | OS | Apple DOS 3.3 | | Purpose | Beep Alert sound | | Input | ]1 = length of beep | -| Output | | +| Output | send a standard beep to the speaker | | Dependencies | none | | Flags Destroyed | NZC | | Cycles | 108+ | | Bytes | 15 | -| Notes | | +| Notes | none | +| See Also | none | --- @@ -1075,7 +1144,7 @@ _DETAILS_ The last of the quick and dirty macros for error testing is the `BEEP` macro, which turns out to be a rather self-explanatory title. This macro sends a click to the internal speaker for the specified number of cycles; the longer this lasts, the more annoying it gets. The macro stands alone and does not rely on any subroutines, due to its inherent simplicity. -`Listing 1.16: BEEP Macro and Heading Subroutine` +`LISTING 1.16: BEEP Macro and Heading Subroutine` ```asm * @@ -1102,7 +1171,10 @@ BEEP MAC ; ROUTINE BEEP BNE ]LP1 ; {4C2B} IF !=, LOOP LDY SCRATCH ; {3C2B} OTHERWISE, RESTORE .Y {NZ} <<< ; END MACRO + ``` + + --- @@ -1131,6 +1203,7 @@ _SUMMARY_ | Cycles | 7 | | Bytes | 8 | | Notes | Obviously, use this only if BCC won't work | +| See Also | `BCSL` `BEQL` `BMIL` `BNEL` `BPLL` `BVCL` `BVSL` | --- @@ -1140,7 +1213,7 @@ The BCCL macro simply performs a BCC instruction and, if the condition is true, For those of you who need a reminder: the `BCC` instruction stands for _Branch Carry Clear_, and branches execution of a program to a specified address when the carry flag is set to 0. `BCC` is also used to test whether a `CMP` comparison yields a result in which the **.A** register holds a value less than the comparison value. Thus, `BCCL` simply does the same, except is capable of branching to an address that is further away. -`Listing 1.17: BCCL Macro and Heading` +`LISTING 1.17: BCCL Macro and Heading` ```asm * @@ -1165,6 +1238,12 @@ BCCL MAC ; LONG BCC <<< ; END MACRO ``` + + +--- + + + ### THE BCSL MACRO _SUMMARY_ @@ -1185,6 +1264,7 @@ _SUMMARY_ | Cycles | 7 | | Bytes | 8 | | Notes | Obviously, use this only if BCC won't work | +| See Also | `BCCL` `BEQL` `BMIL` `BNEL` `BPLL` `BVCL` `BVSL` | --- @@ -1194,7 +1274,7 @@ The BCSL macro simply performs a BCS instruction and, if the condition is true, For those of you who need a reminder: the `BCS` instruction stands for _Branch Carry SET_, and branches execution of a program to a specified address when the carry flag is set to 1. `BCS` is also used to test whether a `CMP` comparison yields a result in which the **.A** register holds a value greater than or equal to the comparison value. Thus, `BCSL` simply does the same, except is capable of branching to an address that is further away. -`Listing 1.18: BCSL Macro and Heading` +`LISTING 1.18: BCSL Macro and Heading` ```asm * @@ -1214,6 +1294,12 @@ BCSL MAC ; LONG BCS ]EXIT ; EXIT LABEL <<< ; END MACRO ``` + + +--- + + + ### THE BEQL MACRO _SUMMARY_ @@ -1234,6 +1320,7 @@ _SUMMARY_ | Cycles | 7 | | Bytes | 8 | | Notes | Obviously, use this only if BEQ won't work | +| See Also | `BCCL` `BCSL` `BMIL` `BNEL` `BPLL` `BVCL` `BVSL` | --- @@ -1243,7 +1330,7 @@ The BEQL macro simply performs a BEQ instruction and, if the condition is true, For those of you who need a reminder: the `BEQ` instruction stands for _Branch Equal_, and branches execution of a program to a specified address when the zero flag is set. `BEQ` is also used to test whether a `CMP` comparison yields a result in which the **.A** register holds a value equal to the comparison value. `BEQ` can also be used to test whether many other operations yield a 0 result, and is often used to test whether a loop should end. Regardless, `BEQL` simply does the same as `BEQ`, except is capable of branching to an address that is further away. -`Listing 1.19: BEQL Macro and Heading` +`LISTING 1.19: BEQL Macro and Heading` ```asm * @@ -1263,6 +1350,12 @@ BEQL MAC ; LONG BEQ ]EXIT ; EXIT LABEL <<< ; END MACRO ``` + + +--- + + + ### THE BMIL MACRO _SUMMARY_ @@ -1283,6 +1376,7 @@ _SUMMARY_ | Cycles | 7 | | Bytes | 8 | | Notes | Obviously, use this only if BMI won't work | +| See Also | `BCCL` `BCSL` `BEQL` `BNEL` `BPLL` `BVCL` `BVSL` | --- @@ -1292,7 +1386,7 @@ The `BMIL` macro simply performs a `BMI` instruction and, if the condition is tr For those of you who need a reminder: the `BMI` instruction stands for _Branch MINUS_, and branches execution of a program to a specified address when the negative flag is set. `BMI` is obviously used to test if a value is negative, but also doubles as an easy way to test whether the high bit of a value is set. `BMIL` simply does the same as `BMI`, except is capable of branching to an address that is further away. -`Listing 1.20: BMIL Macro and Heading` +`LISTING 1.20: BMIL Macro and Heading` ```asm * @@ -1313,6 +1407,12 @@ BMIL MAC ; LONG BMI <<< ; END MACRO ``` + + +--- + + + ### THE BNEL MACRO _SUMMARY_ @@ -1333,6 +1433,7 @@ _SUMMARY_ | Cycles | 7 | | Bytes | 8 | | Notes | Obviously, use this only if BNE won't work | +| See Also | `BCCL` `BCSL` `BEQL` `BMIL` `BPLL` `BVCL` `BVSL` | --- @@ -1342,7 +1443,7 @@ The `BNEL` macro simply performs a` BNE` instruction and, if the condition is tr For those of you who need a reminder: the `BNE` instruction stands for _Branch Not Equal_, and branches execution of a program to a specified address when the zero flag is not set. `BNE` is often used at the end of a loop to determine if the decreasing index has reached zero yet, in which case the loop would be exited. `BNEL` simply does the same, except is capable of branching to an address that is further away. -`Listing 1.21: BNEL Macro and Heading` +`LISTING 1.21: BNEL Macro and Heading` ```asm * @@ -1362,6 +1463,12 @@ BNEL MAC ; LONG BNE ]EXIT ; QUITTING LABEL <<< ; END MACRO ``` + + +--- + + + ### THE BPLL MACRO _SUMMARY_ @@ -1382,6 +1489,7 @@ _SUMMARY_ | Cycles | 7 | | Bytes | 8 | | Notes | Obviously, use this only if BPL won't work | +| See Also | `BCCL` `BCSL` `BEQL` `BMIL` `BNEL` `BVCL` `BVSL` | --- @@ -1391,7 +1499,7 @@ The `BPLL` macro simply performs a `BPL` instruction and, if the condition is tr For those of you who need a reminder: the `BPL` instruction stands for _Branch Plus_, and branches execution of a program to a specified address when the negative flag is not set. `BEQ` is also used to test whether a value has its high bit set; if it is not set, the `BPL` returns nothing. `BPLL` simply does the same as `BPL`, except is capable of branching to an address that is further away. -`Listing 1.22: BPLL Macro and Heading` +`LISTING 1.22: BPLL Macro and Heading` ```asm * @@ -1411,6 +1519,12 @@ BPLL MAC ; LONG BPL ]EXIT ; QUIT <<< ; END MACRO ``` + + +--- + + + ### THE BVCL MACRO _SUMMARY_ @@ -1431,6 +1545,7 @@ _SUMMARY_ | Cycles | 7 | | Bytes | 8 | | Notes | Obviously, use this only if BVC won't work | +| See Also | `BCCL` `BCSL` `BEQL` `BMIL` `BNEL` `BPLL` `BVSL` | --- @@ -1440,7 +1555,7 @@ The `BVCL` macro simply performs a `BVC` instruction and, if the condition is tr For those of you who need a reminder: the `BVC` instruction stands for _Branch oVerflow Clear, and branches execution of a program to a specified address when the overflow flag is clear. `BVC` is often used in signed number calculations, but can also be used as a fake `BRA` in relocatable code. Regardless, `BVCL` simply does the same as `BVC`, except is capable of branching to an address that is further away. -`Listing 1.23: BVCL Macro and Heading` +`LISTING 1.23: BVCL Macro and Heading` ```asm * @@ -1460,6 +1575,12 @@ BVCL MAC ; LONG BVC ]EXIT ; QUIT <<< ; END MACRO ``` + + +--- + + + ### THE BVSL MACRO _SUMMARY_ @@ -1480,6 +1601,7 @@ _SUMMARY_ | Cycles | 7 | | Bytes | 8 | | Notes | Obviously, use this only if BVS won't work | +| See Also | `BCCL` `BCSL` `BEQL` `BMIL` `BNEL` `BPLL` `BVCL` | --- @@ -1489,7 +1611,7 @@ The `BVSL` macro simply performs a `BVS` instruction and, if the condition is tr For those of you who need a reminder: the `BVS` instruction stands for _Branch oVerflow Set_, and branches execution of a program to a specified address when the overflow flag is set. `BVS` is often used in signed arithmetic. `BVSL` simply does the same as `BEQ`, except is capable of branching to an address that is further away. -`Listing 1.24: BVSL Macro and Heading` +`LISTING 1.24: BVSL Macro and Heading` ```asm * @@ -1509,6 +1631,8 @@ BVSL MAC ; LONG BVS ]EXIT ; QUIT <<< ; END MACRO ``` + + --- @@ -1536,7 +1660,8 @@ _SUMMARY_ | Flags Destroyed | none | | Cycles | 4 | | Bytes | 4 | -| Notes | | +| Notes | none | +| See Also | none | --- @@ -1544,7 +1669,7 @@ _DETAILS_ `CBRA` stands for "65**_C_**02 Branch Always, a function that is not available in the original 6502 instruction set. All we do here is replace that macro with a JMP opcode.. -`Listing 1.25: CPHX Macro and Heading` +`LISTING 1.25: CPHX Macro and Heading` ```asm * @@ -1563,6 +1688,12 @@ CBRA MAC ; BRANCH ALWAYS ``` + + +--- + + + ### THE CPHX MACRO _SUMMARY_ @@ -1582,7 +1713,8 @@ _SUMMARY_ | Flags Destroyed | NZ | | Cycles | 11 | | Bytes | 6 | -| Notes | | +| Notes | none | +| See Also | `CPHY` `CPLX` `CPLY` | --- @@ -1590,7 +1722,7 @@ _DETAILS_ `CPHX` stands for "65**_C_**02 PusH **.X**, functionality that is not available in the original 6502 instruction set. Due to swapping values in the registers, this does take more cycles than desirable, so when possible just use the `PHA` instruction like every other normal human being. -`Listing 1.26: CPHX Macro and Heading` +`LISTING 1.26: CPHX Macro and Heading` ```asm * @@ -1611,6 +1743,12 @@ CPHX MAC ; PUSH .X TO STACK <<< ; END MACRO ``` + + +--- + + + ### THE CPHY MACRO _SUMMARY_ @@ -1630,7 +1768,8 @@ _SUMMARY_ | Flags Destroyed | NZ | | Cycles | 1 2 | | Bytes | 6 | -| Notes | | +| Notes | none | +| See Also | `CPHX` `CPLX` `CPLY` | --- @@ -1638,7 +1777,7 @@ _DETAILS_ `CPHY` stands for "65**_C_**02 PusH **.Y**," functionality that is not available in the original 6502 instruction set. Due to swapping values in the registers, this does take more cycles than desirable, so when possible just use the `PHA` instruction like every other normal human being. -`Listing 1.27: CPHY Macro and Heading` +`LISTING 1.27: CPHY Macro and Heading` ```asm * @@ -1658,6 +1797,12 @@ CPHY MAC ; PUSH .Y TO STACK LDA SCRATCH ; {3C2B} RESTORE .A CONTENTS {NZ} <<< ; END MACRO ``` + + +--- + + + ### THE CPLX MACRO _SUMMARY_ @@ -1677,7 +1822,8 @@ _SUMMARY_ | Flags Destroyed | NZ | | Cycles | 11 | | Bytes | 6 | -| Notes | | +| Notes | none | +| See Also | `CPHX` `CPHY` `CPLY` | --- @@ -1685,7 +1831,7 @@ _DETAILS_ `CPLX` stands for "65**_C_**02 Pul**L .X**, functionality that is not available in the original 6502 instruction set. Due to swapping values in the registers, this does take more cycles than desirable, so when possible just use the `PLA` instruction like every other normal human being. Otherwise, this macro pulls the top value from the stack and stores it in **.X**. -`Listing 1.28: CPLX Macro and Heading` +`LISTING 1.28: CPLX Macro and Heading` ```asm * @@ -1705,6 +1851,12 @@ CPLX MAC ; PULL FROM STACT TO .X LDA SCRATCH ; {3C2B} RESTORE .A CONTENTS {NZ} <<< ; END MACRO ``` + + +--- + + + ### THE CPLY MACRO _SUMMARY_ @@ -1724,7 +1876,8 @@ _SUMMARY_ | Flags Destroyed | NZ | | Cycles | 11 | | Bytes | 7 | -| Notes | | +| Notes | none | +| See Also | `CPHX` `CPHY` `CPLX` | --- @@ -1732,7 +1885,7 @@ _DETAILS_ `CPLY` stands for "65**_C_**02 PulL **.Y**, functionality that is not available in the original 6502 instruction set. Due to swapping values in the registers, this does take more cycles than desirable, so when possible just use the `PLA` instruction like every other normal human being. Otherwise, this macro pulls the top value from the 6502 stack and stores it in **.Y**. -`Listing 1.29: CPLY Macro and Heading` +`LISTING 1.29: CPLY Macro and Heading` ```asm * *``````````````````````````````* @@ -1751,6 +1904,12 @@ CPLY MAC ; PULL FROM STACK INTO .Y LDA SCRATCH ; {3C2B} RESTORE .A {NZ} <<< ; END MACRO ``` + + +--- + + + ### THE CTXY MACRO _SUMMARY_ @@ -1770,7 +1929,8 @@ _SUMMARY_ | Flags Destroyed | NZ | | Cycles | 10 | | Bytes | 6 | -| Notes | | +| Notes | none | +| See Also | `CTYX` | --- @@ -1778,7 +1938,7 @@ _DETAILS_ Sadly, in regular 6502 there is no instruction for transferring the contents of **.X** to **.Y**, like there is in the 65c02 instruction set. This macro, which stands for "65**_C_**02 Transfer **.X** to **.Y**, fakes that functionality by using **.A** as an intermediary and then restoring its original value. -`Listing 1.30: CTXY Macro and Heading` +`LISTING 1.30: CTXY Macro and Heading` ```asm * *``````````````````````````````* @@ -1797,6 +1957,12 @@ CTXY MAC ; TRANSFER X TO Y LDA SCRATCH ; {3C2B} RESTORE .A {NZ} <<< ; END MACRO ``` + + +--- + + + ### THE CTYX MACRO _SUMMARY_ @@ -1816,7 +1982,8 @@ _SUMMARY_ | Flags Destroyed | NZ | | Cycles | 10 | | Bytes | 6 | -| Notes | | +| Notes | none | +| See Also | `CTXY` | --- @@ -1824,7 +1991,7 @@ _DETAILS_ Likewise, in regular 6502 there is no instruction for transferring the contents of **.Y** to **.X**, like there is in the 65c02 instruction set. This macro, which stands for "65**_C_**02 Transfer **.Y** to **.X**, fakes that functionality by using **.A** as an intermediary and then restoring its original value. -`Listing 1.31: CTXY Macro and Heading` +`LISTING 1.31: CTXY Macro and Heading` ```asm * *``````````````````````````````* @@ -1868,7 +2035,8 @@ _SUMMARY_ | Flags Destroyed | NZC | | Cycles | 10 | | Bytes | 6 | -| Notes | | +| Notes | none | +| See Also | none | --- @@ -1876,7 +2044,7 @@ _DETAILS_ The `CLRHI` macro clears the high nibble of the byte held in **.A**. -`Listing 1.32: DELAY Macro and Heading` +`LISTING 1.32: DELAY Macro and Heading` ```asm * *``````````````````````````````* @@ -1899,6 +2067,12 @@ CLRHI MAC <<< ``` + + +--- + + + ### THE DELAY MACRO _SUMMARY_ @@ -1914,20 +2088,21 @@ _SUMMARY_ | Purpose | Delay a number of milliseconds | | Input | none | | Output | none | -| Dependencies | none | +| Dependencies | `DELAYMS` | | Flags Destroyed | NZC | | Cycles | 58+ | | Bytes | 10 | -| Notes | | +| Notes | none | +| See Also | `DELAYMS` | --- _DETAILS_ -The `DELAY` macro calls the `DELAYMS` subroutine following this listing, which holds the 6502 CPU busy for a specified number of milliseconds by repeating through a specific number of cycles. It first backs up the **.Y** register for restoration after the delay, then calls the subroutine. +The `DELAY` macro calls the `DELAYMS` subroutine following this LISTING, which holds the 6502 CPU busy for a specified number of milliseconds by repeating through a specific number of cycles. It first backs up the **.Y** register for restoration after the delay, then calls the subroutine. -`Listing 1.33: DELAY Macro and Heading` +`LISTING 1.33: DELAY Macro and Heading` ```asm * *``````````````````````````````* @@ -1951,6 +2126,12 @@ DELAY MAC ; DELAY FOR MILLISECONDS <<< ; END MACRO ``` + + +--- + + + ### THE DELAYMS SUBROUTINE _SUMMARY_ @@ -1970,7 +2151,8 @@ _SUMMARY_ | Flags Destroyed | NZC | | Cycles | 39+ | | Bytes | 29 | -| Notes | | +| Notes | none | +| See Also | | --- @@ -1980,7 +2162,7 @@ The `DELAYMS` subroutine uses a precise number cycles to delay for a number of g It should be noted that this routine will only work correctly on a 6502 CPU that runs at 1.23 mhz; any other speed will slightly alter the delay. -`Listing 1.34: DELAYMS Subroutine and Heading` +`LISTING 1.34: DELAYMS Subroutine and Heading` ```asm * @@ -2055,6 +2237,12 @@ DELAYMS RTS ; {6C1B} ``` + + +--- + + + ### THE DUMP MACRO _SUMMARY_ @@ -2070,11 +2258,12 @@ _SUMMARY_ | Purpose | Dump value in mem block to screen | | Input | none | | Output | none | -| Dependencies | none | +| Dependencies | `__DUMP` | | Flags Destroyed | NZCV | | Cycles | 811+ | | Bytes | 16 | -| Notes | | +| Notes | none | +| See Also | `__DUMP` | --- @@ -2082,7 +2271,7 @@ _DETAILS_ It is often useful, and sometimes necessary, to view the contents of a block of memory while trying to debug a given subroutine. The `DUMP` macro does exactly that: it dumps a specified block of memory to the screen for the user to see, in hexadecimal, before continuing execution of the program. Note that this does not ceate a pause for the information to be absorbed; the pause must be explicitly stated with something like a `_WAIT` statement. This macro calls the `__DUMP` subroutine, which handles most of the work. -`Listing 1.35: DUMP Macro and Heading` +`LISTING 1.35: DUMP Macro and Heading` ```asm * *``````````````````````````````* @@ -2108,7 +2297,13 @@ DUMP MAC ; DUMP MEMORY CONTENTS LDY SCRATCH ; {3C2B} RESTORE .Y {NZ} <<< ; END MACRO ``` -### THE DUMP SUBROUTINE + + +--- + + + +### THE __DUMP SUBROUTINE _SUMMARY_ @@ -2127,7 +2322,8 @@ _SUMMARY_ | Flags Destroyed | NZCV | | Cycles | 787+ | | Bytes | 111 | -| Notes | | +| Notes | none | +| See Also | `DUMP` | --- @@ -2135,7 +2331,7 @@ _DETAILS_ The `__DUMP` subroutine dumps a given address range of hexadecimal values to the screen, often used for debugging. The Actual hexadecimal values are converted to strings before being printed to the screen, given a starting addres at each line. -`Listing 1.36: DUMP Subroutine` +`LISTING 1.36: DUMP Subroutine` ```asm * *``````````````````````````````* @@ -2213,6 +2409,12 @@ __DUMP RTS ; {6C1B} ELSE, EXIT ``` + + +--- + + + ### THE ERRH MACRO _SUMMARY_ @@ -2228,11 +2430,12 @@ _SUMMARY_ | Purpose | Change Applesoft Error Handling Routine hook | | Input | none | | Output | none | -| Dependencies | none | +| Dependencies | `__ERRH` | | Flags Destroyed | NZC | | Cycles | 73 | | Bytes | 7 | -| Notes | | +| Notes | none | +| See Also | `__ERRH` | --- @@ -2240,7 +2443,7 @@ _DETAILS_ `ERRH` is a macro that sets the address that would be pointed to in the case of an error-handling issue in Applesoft \(and sometimes in DOS\). This is often used in conjunction with file handline routines, since many of those are to be found Applesoft. First the **.Y** register is backed up for later restoration, then calls the `_AXLIT` macro to pass parameters to the `__ERRH` subroutine. The **.Y** register is then restored to its original value, and control is returned to the main program. -`Listing 1.37: ERRH Macro` +`LISTING 1.37: ERRH Macro` ```asm * *``````````````````````````````* @@ -2264,6 +2467,12 @@ ERRH MAC ; SET ERROR HANDLE <<< ; END MACRO ``` + + +--- + + + ### THE __ERRH SUBROUTINE _SUMMARY_ @@ -2283,7 +2492,8 @@ _SUMMARY_ | Flags Destroyed | NZ | | Cycles | 53 | | Bytes | 32 | -| Notes | | +| Notes | none | +| See Also | `ERRH` | --- @@ -2291,7 +2501,7 @@ _DETAILS_ The `__ERRH` subroutine tricks DOS into thinking it is in immediate mode, turns on error handling, then defines the hook to which an error will point to. Control is then returned to the calling routine. -`Listing 1.38: __ERRH Subroutine` +`LISTING 1.38: __ERRH Subroutine` ```asm * @@ -2335,6 +2545,11 @@ __ERRH ``` + +--- + + + ### THE GBIT MACRO _SUMMARY_ @@ -2354,7 +2569,8 @@ _SUMMARY_ | Flags Destroyed | NZ | | Cycles | 14 | | Bytes | 15 | -| Notes | | +| Notes | none | +| See Also | none | --- @@ -2362,7 +2578,7 @@ _DETAILS_ The `GBIT` macro loads **.A** with the value of a given bit in a given byte. To address which bit to test, the BITON# variables should be used in order to avoid "magic numbers." For instance, `GBIT $300;#BITON6` would test whether bit 6 of the byte found in address $300 is either a 0 or 1, which is store in the Accumulator. Remember that the eight bits of a byte start at bit 0 from the right to bit 7 on the left. Therefore, the preceding code would test the second from last bit in the byte. -`Listing 1.39: GBIT Subroutine` +`LISTING 1.39: GBIT Subroutine` ```asm * @@ -2389,6 +2605,12 @@ GBIT MAC ; GET A SINGLE BIT FROM BYTE ]EXIT <<< ``` + + +--- + + + ### THE GRET MACRO _SUMMARY_ @@ -2408,7 +2630,8 @@ _SUMMARY_ | Flags Destroyed | NZC | | Cycles | 43+ | | Bytes | 27 | -| Notes | | +| Notes | none | +| See Also | none | --- @@ -2416,7 +2639,7 @@ _DETAILS_ The `GRET` macro pulls the data held in `RETURN` and stores it in an alternative address. The length to be copied is to be determined by the `RETLEN` byte, which precedes `RETURN` in memory. -`Listing 1.40: GRET Macro` +`LISTING 1.40: GRET Macro` ```asm * @@ -2449,6 +2672,12 @@ GRET MAC ; GET RETURN VALUE LDY SCRATCH ; {3C2B} RESTORE .Y {NZ} <<< ; END MACRO ``` + + +--- + + + ## MEMORY MACROS AND SUBROUTINES The remaining macros and subroutines are dedicated to memory manipulation, which is such a common need in most routines that their inclusion here is deemed necessary. Note that rior to this version of the collection, memory macros and subroutines were considered a library separate from the required library, though they shared the same disk. @@ -2472,7 +2701,8 @@ _SUMMARY_ | Flags Destroyed | NZC | | Cycles | 28 | | Bytes | 18 | -| Notes | | +| Notes | none | +| See Also | `POKE` | --- @@ -2480,7 +2710,7 @@ _DETAILS_ The `PEEK` macro is simply a macro that loads the **.A** register with the value at a given address. It may serve a greater purpose in the future. -`Listing 1.41: PEEK Macro` +`LISTING 1.41: PEEK Macro` ```asm * @@ -2504,6 +2734,12 @@ PEEK MAC ; PUT VALUE INTO .A <<< ; END MACRO ``` + + +--- + + + ### THE POKE MACRO _SUMMARY_ @@ -2523,7 +2759,8 @@ _SUMMARY_ | Flags Destroyed | NZC | | Cycles | 32 | | Bytes | 20 | -| Notes | | +| Notes | none | +| See Also | `PEEK` | --- @@ -2531,7 +2768,7 @@ _DETAILS_ The `POKE` macro is simply a macro that loads the **.A** register with the value at a given address. It may serve a greater purpose in the future. -`Listing 1.42: POKE Macro` +`LISTING 1.42: POKE Macro` ```asm * @@ -2556,6 +2793,12 @@ POKE MAC ; STORE .A INTO MEM LOC <<< ; END MACRO ``` + + +--- + + + ### THE MFILL MACRO _SUMMARY_ @@ -2571,11 +2814,12 @@ _SUMMARY_ | Purpose | Fill a block of memory | | Input | starting address, length, fill value | | Output | none | -| Dependencies | none | +| Dependencies | `MEMFILL` | | Flags Destroyed | NZ | | Cycles | 110+ | | Bytes | 36 | -| Notes | | +| Notes | none | +| See Also | `MEMFILL` | --- @@ -2583,7 +2827,7 @@ _DETAILS_ The `MFILL` macro takes a starting address a length byte, and a fill value, and fill a block of memory starting at the address given with the specified length and fill value. First, **.Y** is backed up to be restored after calling the `MEMFILL` subroutine, then the parameters are sorted using the `_MLIT` macro for parameters passed via the zero mage. Another parameter is stored in `BPAR1`, then `MEMFILL` is called to complete the task. Once the subroutine returns control back to the macro, **.Y** is restored to its original value. -`Listing 1.43: MFILL Macro` +`LISTING 1.43: MFILL Macro` ```asm * *``````````````````````````````* @@ -2613,6 +2857,11 @@ MFILL MAC ; MEMORY FILL ``` + +--- + + + ### THE MEMFILL SUBROUTINE _SUMMARY_ @@ -2632,7 +2881,8 @@ _SUMMARY_ | Flags Destroyed | NZ | | Cycles | 59+ | | Bytes | 31 | -| Notes | | +| Notes | none | +| See Also | `MFILL` | --- @@ -2640,7 +2890,7 @@ _DETAILS_ The `MEMFILL` subroutine is usualy called by `MFILL` in order to fill block of memory with a given value. Note that this can span more than a single page, and fills whole pages before it continues to fill the pages that are not fully overwritten. Also note that the main algorithm for this was waken from Leventhal & Saville's _6502 Assembly Routines_, as it works very well and there is no need, at this time, to completely reinvent the wheel. -`Listing 1.44: MFILL Macro` +`LISTING 1.44: MFILL Macro` ```asm * *``````````````````````````````* @@ -2701,6 +2951,12 @@ MEMFILL RTS ; {6C1B} ``` + + +--- + + + ### THE MOVB MACRO _SUMMARY_ @@ -2713,14 +2969,15 @@ _SUMMARY_ | Last Revision | 12-DEC-2019 | | Assembler | Merlin 8 Pro | | OS | Apple DOS 3.3 | -| Purpose | | -| Input | | +| Purpose | move block of memory | +| Input | ]1 = Source Address
]2 = Destination Address
]3 = Number of Bytes | | Output | none | | Dependencies | `MEMMOVE` | | Flags Destroyed | NZCV | | Cycles | 267+ | | Bytes | 48 | -| Notes | | +| Notes | none | +| See Also | `MEMMOVE` | --- @@ -2728,7 +2985,7 @@ _DETAILS_ The `MOVB` macro moves an number of blocks of memory from a source address to a destination address with a given length. The macro first backs up the **.Y** register, then runs a series of `_MLIT` macros to prepare for the passing of parameters to the `MEMMOVE` subroutine via the zero page. The subroutine is then called, after which the **.Y** register is restored to its original value. -`Listing 1.46: MOVB Macro` +`LISTING 1.46: MOVB Macro` ```asm * *``````````````````````````````* @@ -2757,6 +3014,12 @@ MOVB MAC ; MEMORY MOVE <<< ; END MACRO ``` + + +--- + + + ### THE MEMMOVE SUBROUTINE _SUMMARY_ @@ -2769,14 +3032,15 @@ _SUMMARY_ | Last Revision | 12-DEC-2019 | | Assembler | Merlin 8 Pro | | OS | Apple DOS 3.3 | -| Purpose | | -| Input | | +| Purpose | move a block of memory | +| Input | WPAR1 = Source Address
WPAR2 = Destination Address
WPAR3 = Number of Bytes | | Output | none | | Dependencies | none | | Flags Destroyed | NZCV | | Cycles | 207+ | | Bytes | 117 | -| Notes | | +| Notes | none | +| See Also | `MOVB` | --- @@ -2786,7 +3050,7 @@ This is another subroutine lifted from Leventhal and Seville's _6502 Assembly La Ultimately, the `MEMMOVE` subroutine moves a block of memory at a certain length to another adfress. This block of memory can span multiple pages, and thanks to the way the subroutine functions, overlaps in memory allocation should not be a concern. -`Listing 1.47: MEMMOVE Macro` +`LISTING 1.47: MEMMOVE Macro` ```asm * *``````````````````````````````* @@ -2926,6 +3190,12 @@ MEMMOVE RTS ; {6C1B} ``` + + +--- + + + ### THE MSWAP MACRO _SUMMARY_ @@ -2939,13 +3209,14 @@ _SUMMARY_ | Assembler | Merlin 8 Pro | | OS | Apple DOS 3.3 | | Purpose | Swap one memory block with another | -| Input | | +| Input | ]1 = First Starting Address
]2 = Second Starting Address
]3 = Number of Bytes | | Output | none | -| Dependencies | none | +| Dependencies | `MEMSWAP` | | Flags Destroyed | NZC | | Cycles | 98+ | | Bytes | 36 | -| Notes | | +| Notes | none | +| See Also | `MEMSWAP` | --- @@ -2955,7 +3226,7 @@ The `MSWAP` macro swaps a given block of memory with another block of memory. No The macro first backs up the contents of the **.Y** register, then makes several calls to the `_MLIT` macro to sort out parameters on the zero page. **.A** is then loaded with the length in bytes of the swap and stored in `BPAR1` as a parameter before the `MEMSWAP` subroutine is called. Afterwards, control is returned to the main program and the original contents of **.Y** are restored. -`Listing 1.48: MSWAP Macro` +`LISTING 1.48: MSWAP Macro` ```asm * *``````````````````````````````* @@ -2985,6 +3256,12 @@ MSWAP MAC ; MEMORY SWAP <<< ; END MAC * ``` + + +--- + + + ### THE MEMSWAP SUBROUTINE _SUMMARY_ @@ -2998,13 +3275,14 @@ _SUMMARY_ | Assembler | Merlin 8 Pro | | OS | Apple DOS 3.3 | | Purpose | Swap one memory block with another | -| Input | | +| Input | WPAR1 = First Starting Address
WPAR2 = Second Starting Address
BPAR1 = Number of Bytes | | Output | none | | Dependencies | none | | Flags Destroyed | NZC | | Cycles | 46+ | | Bytes | 18 | -| Notes | | +| Notes | none | +| See Also | | --- @@ -3012,7 +3290,7 @@ _DETAILS_ The `MEMSWAP` subroutine swaps one block of memory less than a single page to another block of memory. No error checking is applied, especially for cases like memory overlap, so care should be taken with the subroutine as it currently stands. -`Listing 1.49: MEMSWAP SUBROUTINE` +`LISTING 1.49: MEMSWAP SUBROUTINE` ```asm * @@ -3061,6 +3339,10 @@ MEMSWAP +--- + + + ## PART II: ALIASES @@ -3069,12 +3351,14 @@ Disk I also contains alias files that hold macros that emulate parts of the inst - 8080/8086 family (up to 286) - z80 Family of Processors -It is advisable to use only one alias family at a time, as some instruction sets share instruction names. Note that instructions related to mathematics are rarely used here, as these are mainly part of the math library already. Only basic instructions like branching are included in the alias files, and thus the source code for each is rather simple. As such, we won't be listing the source here, but providing tables of the instruction set macros with their cycles, bytes used and purpose. If a particular instruction becomes complicated enough to merit explanation, we will list it separately from the rest of the instruction set table. +It is advisable to use only one alias family at a time, as some instruction sets share instruction names. Note that instructions related to mathematics are rarely used here, as these are mainly part of the math library already. Only basic instructions like branching are included in the alias files, and thus the source code for each is rather simple. As such, we won't be LISTING the source here, but providing tables of the instruction set macros with their cycles, bytes used and purpose. If a particular instruction becomes complicated enough to merit explanation, we will list it separately from the rest of the instruction set table. ### 8080 Instruction Set Macro Substitutions The following instruction set macro replacements for the 8080/8086 line of processors are largely branching instructions, although two others stand out: `ANC` and `SNC`. These stand for _Add No Carry_ and _Subtract No Carry_, respectively, and will be removed from here and added to the math library once revision once again reaches that disk. + + | Instruction / Macro | Purpose | Bytes | Cycles | | ------------------------- | ----------- | ------- | --------- | | `CALL` | MIMICS `JSR` OPERATION | 3 | 6 | @@ -3103,6 +3387,12 @@ The following instruction set macro replacements for the 8080/8086 line of proce | `PULLA` | PULL ALL REGISTERS | 24 | 19 | | `POPA` | PULL ALL REGISTERS | 24 | 19 | + + +--- + + + ### Z80 Family Instruction Set Macro Substitutions Currently, there are few instruction set macro substitutions for the z80 processor because they operate in very different ways. However, they is some overlap, and as much overlap as possible will be provided by these aliases. @@ -3119,15 +3409,19 @@ Currently, there are few instruction set macro substitutions for the z80 process +--- + + + ## Part III: REQUIRED LIBRARY DEMO FILE -At the the end of each library, a demo is provided that shows the macros being used as they should be (or could be). These demos are not meant to be exhaustive, but are meant to merely illustrate how the macro is called, what parameters it might require, and so on. By and large, the demos are all kept fairly simple due to the fact that 1) they cannot interact with the macros and subroutines provided by other libraries, and 2) they are meant for beginners to see how a macro works and not much else. More complicated demos are planned for future disks in the package, and some of them are already finished (though in need of updating to the current version). +At the the end of each collection, a demo is provided that shows the macros being used as they should be (or could be). These demos are not meant to be exhaustive, but are meant to merely illustrate how the macro is called, what parameters it might require, and so on. By and large, the demos are all kept fairly simple due to the fact that 1) they cannot interact with the macros and subroutines provided by other libraries, and 2) they are meant for beginners to see how a macro works and not much else. More complicated demos are planned for future disks in the package, and some of them are already finished (though in need of updating to the current version). The demo file listed here is the same `DEMO.REQUIRED` file on the disk. Note that most of the heavier descriptions of the macros are done in commenting in order to cut down on the demo file size in bytes, but `_PRN` and `_WAIT` are particularly used in demo files to give some context when the demo itself is executed. Note also that demo files, like all other files, have unique headings before the code proper begins. ## DEMO.REQUIRED -`Listing 1.50: DEMO.REQUIRED` +`LISTING 1.50: DEMO.REQUIRED` ```asm * @@ -3849,10 +4143,12 @@ The demo file listed here is the same `DEMO.REQUIRED` file on the disk. Note tha +--- -[Return to Table of Contents](./0.0 Title to TOC) -[Next: Detailed Reference: Disk 2: Standard Input and Output]() + +[Return to Table of Contents](0.0%20Title_to_TOC) +[Next: Detailed Reference: Disk 2: Standard Input and Output](31.0%20Detailed_Reference_D2_STDIO) diff --git a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/31.0 Detailed_Reference_D2_STDIO.md b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/31.0 Detailed_Reference_D2_STDIO.md index 04dbd8f..c853659 100644 --- a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/31.0 Detailed_Reference_D2_STDIO.md +++ b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/31.0 Detailed_Reference_D2_STDIO.md @@ -1,65 +1,58 @@ # Disk 2 : STDIO Library and Aliases -- [Part I: The STDIO Library](#) - - [STDIO Components](#) - - [STDIO Header File](#) - - [Next Up: STDIO Macros](#) - - The `COL40` Macro - - The `COL80` Macro - - The `DIE80` Macro - - The `MTXT0` Macro - - The `MTXT1` Macro - - The `TGBDC` Macro - - The `TGBEC` Macro - - The `CURF` Macro - - The `CURB` Macro - - The `CURU` Macro - - The `CURD` Macro - - The `RCPOS` Macro - - The `SCPOS` Macro - - The `SETCX` Macro - - The `SETCY` Macro - - The `INP` Macro - - The `SINPUT` Subroutine - - The `GKEY` Macro - - The `WAIT` Macro - - The `PBX` Macro - - The `PDL` Macro - - The `PRN` Macro - - The `XPRINT` Subroutine - - The `DPRINT` Subroutine - - The `SPRN` Macro - - The `PRNSTR` Subroutine - - The `PRNC` Macro - - The `COLPRINT` Subroutine - - The `PRNM` Macro - - The `PRNW` Macro - - The `PRNMW` Macro - - The `TLINE` Macro - - The `TBLINE` Subroutine - - The `TCIRC` Macro - - The `TCIRCLE` Subroutine - - The `TVLIN` Macro - - The `TVLINE` Subroutine - - The `THLIN` Macro - - The `THLINE` Subroutine - - The `TRECF` Macro - - The `TRECTF` Subroutine - - The `TPUT` Macro - - The `TXTPUT` Subroutine - - The `TCLR` Macro - - The `TXTCLR` Subroutine - - The `TPNT` Macro - - - - - - - - - - +- [Part I: The STDIO Library](#part-i-stdio-macros-and-subroutine-library) + - [STDIO Components](#stdio-components) + - [STDIO Header File](#stdio-header-file) + - [Next Up: STDIO Macros](#macros-and-subroutines) + - [COUT Text Output](#cout-macros-and-subroutines) + - [The `CURB` Macro](#the-curb-macro) + - [The `CURD` Macro](#the-curd-macro) + - [The `CURF` Macro](#the-curf-macro) + - [The `CURU` Macro](#the-curu-macro) + - [The `PRN` Macro](#the-prn-macro) + - [The `DPRINT` Subroutine](#the-dprint-subroutine) + - [The `XPRINT` Subroutine](#the-xprint-subroutine) + - [The `SCPOS` Macro](#the-scpos-macro) + - [The `SETCX` Macro](#the-setcx-macro) + - [The `SETCY` Macro](#the-setcy-macro) + - [The `SPRN` Macro](#the-sprn-macro) + - [The `PRNSTR` Subroutine](#the-prnstr-subroutine) + - [The `TMORE` Macro](#the-tmore-macro) + - [The `TXTMORE` Subroutine](#the-txtmore-subroutine) + - [Direct Memory Text Input / Output](#screen-memory-macros-and-subroutines) + - [The `CPUT` Macro](#the-cput-macro) + - [The `TXTPUT` Subroutine](#the-txtput-subroutine) + - [The `RCPOS` Macro](#the-rcpos-macro) + - [The `SPUT` Macro](#the-sput-macro) + - [The `STRPUT` Subroutine](#the-strput-subroutine) + - [The `TCIRC` Macro](#the-tcirc-macro) + - [The `TCIRCLE` Subroutine](#the-tcircle-subroutine) + - [The `TCLR` Macro](#the-tclr-macro) + - [The `TXTCLR` Subroutine](#the-txtclr-subroutine) + - [The `THLIN` Macro](#the-thlin-macro) + - [The `THLINE` Subroutine](#the-thline-subroutine) + - [The `TREC` Macro](#the-trec-macro) + - [The `TRECT` Subroutine](#the-trect-subroutine) + - [The `TRECF` Macro](#the-trecf-macro) + - [The `TRECTF` Subroutine](#the-trectf-subroutine) + - [The `TLINE` Macro](#the-tline-macro) + - [The `TBLINE` Subroutine](#the-tbline-subroutine) + - [The `TVLIN` Macro](#the-tvlin-macro) + - [The `TVLINE` Subroutine](#the-tvline-subroutine) + - [Standard Input](#) + - [The `GKEY` Macro](#) + - [The `INP` Macro](#) + - [The `SINPUT` Subroutine](#) + - [The `PBX` Macro](#) + - [The `PDL` Macro](#) + - [The `WAIT` Macro](#) + - [Misc](#miscellaneous-macros-and-subroutines) + - [The `COL40` Macro](#the-col40-macro) + - [The `COL80` Macro](#the-col80-macro) + - [The `DIE80` Macro](#the-die80-macro) + - [The `TCTR` Macro](#the-tctr-macro) + - [The `TXTCENT` Subroutine](#the-txtcent-macro) +- [PART II: STDIO Collection Demo File](#part-ii-stdio-collection-demo-file) @@ -79,7 +72,9 @@ ## PART I: STDIO Macros and Subroutine Library -The second disk inthe AppleIIAsm Collection includes all of the macros, subroutines, vectors and reserved memory locations for standard input and output on the Apple II. Note that by "Standard Output," we are primarily referring to the text screen only, and 40-column mode at that. A separate disk, as part of the "inessential" collection, will deal with 80-column mode as well as devices like printers. As for "Standard Input," here we refer to the keyboard and game paddle, though there are plans to also include optional mouse macros and subroutines (in some cases, the mouse is dealt with in the same fashion as the game paddle). Note that as with all libraries, the STDIO library will not function properly without the REQUIRED library. +The second disk in the AppleIIAsm Library includes all of the macros, subroutines, vectors and reserved memory locations for standard input and output on the Apple II. Note that by "Standard Output," we are primarily referring to the text screen only, and 40-column mode at that. A separate disk, as part of the "inessential" collection, will deal with 80-column mode as well as devices like printers. As for "Standard Input," here we refer to the keyboard and game paddle, though there are plans to also include optional mouse macros and subroutines (in some cases, the mouse is dealt with in the same fashion as the game paddle). Note that as with all libraries, the **STDIO** collection will not function properly without the **REQUIRED** collection. + + --- @@ -88,16 +83,18 @@ The second disk inthe AppleIIAsm Collection includes all of the macros, subrouti ## STDIO Components -The STDIO library contains the following components: +The STDIO collection contains the following components: - A header file with various hooks, vectors and definitions for the rest of the library as well as use by the end programmer. - Macros and subroutines dedicated to standard input and output, which includes keyboard and paddle input routines, 40-column text printing routines, basic text-mode drawing routines, and various macros and subroutines that support other functions provided by the Apple II that don't fit into the previous categories so well. - - Note that some routines use COUT, while others use direct screen memory manipulation (which is why some macros, especially those for drawing, will not translate to 80-column mode). Whether a macro uses COUT or direct memory access is revealed in the macro's or subroutine's given entry. + - COUT versus non-COUT components. Note that some routines use COUT, while others use direct screen memory manipulation (which is why some macros, especially those for drawing, will not translate to 80-column mode). Whether a macro or subroutine uses COUT is listed under the appropriate section. In general, the macros and subroutines are already listed categorically as such. -Macros are largely grouped here by function rather than by a more arbitrary aphabetical scheme, as used in the REQUIRED library documentation. First, macros and subroutines that do not fit in with any of the other categories will be covered. This seems counterintuitive at first, but hopefully the reasoning for it will reveal itself as the documentation is read (long story short: these miscellaneous subroutines can have drastic effects on the rest of the subroutines). Next, COUT cursor macros and subroutines are detailed, followed by standard input routines and screen output routines that rely on COUT for funtionality. +Macros are largely grouped here by function rather than by a more arbitrary alphabetical scheme, as used in the **REQUIRED** collection documentation. First, macros and subroutines that do not fit in with any of the other categories will be covered. This seems counterintuitive at first, but hopefully the reasoning for it will reveal itself as the documentation is read (long story short: these miscellaneous subroutines can have drastic effects on the rest of the subroutines). Next, COUT cursor macros and subroutines are detailed, followed by standard input routines and screen output routines that rely on COUT for functionality. Lastly, macros and subroutines that directly access screen memory to output characters are covered. Usually, these subroutines and macros are dedicated to creating shapes, lines, and ASCII art in general. Note that while this can be used in tandem with the macros and subroutines that use COUT, the standard COUT routine will not recognize that these characters have been plotted, and the cursor position will remain the same as before. + + --- @@ -127,7 +124,7 @@ The STDIO header file is required for all macros and subroutines to work correct As with all files in the collection, this header file includes a short heading that provides contact information, date last changed, number of bytes, a short description, etc. -`Listing 2.0: HEAD.STDIO File Heading` +`LISTING 2.0: HEAD.STDIO File Heading` ```asm * @@ -152,7 +149,7 @@ As with all files in the collection, this header file includes a short heading t Most of the pointers in the STDIO header are rather self-explanatory, and therefore will not be covered here. When used, some of these may be explained more in the macro or subroutine documentation. -`Listing 2.1: HEAD.STDIO CODE` +`LISTING 2.10: HEAD.STDIO Source` ```asm * @@ -227,16 +224,4883 @@ CLAPP EQU $C062 ; CLOSED-APPLE KEY +--- + + + +## Macros and Subroutines + +The **STDIO** collection is separated into three distinct groups of macros and subroutines, with the corresponding macros placed in a file that represents each group: COUT-based components, Direct Screen Memory components, and Miscellaneous components. This is primarily for convenience: very often, you'll only want to use the COUT components without the bells and whistles of the Direct Memory accessing components, or vice versa. However, this also serves a more practical purpose: if all of the macros were held in a single file, memory usage by the assembler could become an issue. Because of this ever-present threat, it is always good practice to copy the macros and subroutines that you need, then delete any unused macros, in the final product. + + + +--- + + + +## COUT Macros and Subroutines + +All macros that use COUT functionality for output are held in the `MAC.COUT.STDOUT.ASM` file. These macros and subroutines play nicely with the standard COUT features of the Apple II, which should be familiar terrain to even Applesoft programmers. Unless plotting text to the screen display quickly is a high concern, the macros and subroutines in this group and file should serve most mundane purposes. + + + +`LISTING 2.20: MAC.COUT.STDOUT.ASM Heading` + +```assembly +* +*``````````````````````````````* +* MAC.COUT.STDOUT.ASM * +* * +* THIS IS A MACRO LIBRARY FOR * +* STANDARD INPUT AND OUTPUT. * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 11-MAR-2021 * +* ASSEMBLER: MERLIN 8 PRO * +* OS: DOS 3.3 * +* * +* SUBROUTINES FILES USED: * +* * +* SUB.XPRINT * +* SUB.DPRINT * +* SUB.PRNSTR * +* SUB.TXTMORE * +* SUB.TXTCENT * +* * +* LIST OF MACROS * +* * +* PRN : FLEXIBLE PRINT * +* SPRN : PRINT STRING * +* SCPOS : SET CURS POS AT X,Y * +* SETCX : SET CURSOR X * +* SETCY : SET CURSOR Y * +* CURF : CURSOR FORWARD * +* CURB : CURSOR BACKWARD * +* CURU : CURSOR UP * +* CURD : CURSOR DOWN * +* TMORE : TEXT MORE WRAPPER * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* + +``` + + + +--- + + + +### THE PRN MACRO + +_SUMMARY_ + +| Condition | Value | +| --- | --- | +| Name | `PRN` | +| Type | Macro | +| File | `MAC.COUT.STDOUT.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Print a literal string or a null-terminated string at a given address | +| Input | ]1 = memory address or literal string | +| Output | Text displayed at current cursor position | +| Dependencies | `SUB.DPRINT.ASM` `SUB.XPRINT.ASM` | +| Flags Destroyed | NZCV | +| Cycles | 159+ | +| Bytes | 34+ | +| Notes | Note that as with all macros that may take an address as its parameter, a literal address (#$0000) indicates that the address is where the string resides, whereas a regular address reference indicates that the address holds a pointer to the correct address of the string. | +| See Also | `XPRINT` `DPRINT` `SPRN` `PRNSTR` | + +--- + +*DETAILS* + +The `PRN` macro does what you would expect it to do: it prints characters to the screen via COUT. This is slower than directly altering screen memory, but in many cases is preferable because it allows for the many affordances provided by COUT. `Listing 2.21` provides the source code for the `PRN` macro, which utilizes the `DPRINT` or `XPRINT` subroutines, depending on the parameter passed to the macro. + + + +`LISTING 2.21: The PRN Macro Source` + +```assembly +* +*``````````````````````````````* +* PRN * +* * +* PRINT A LITERAL STRING OR * +* A NULL-TERMINATED STRING AT * +* A GIVEN ADDRESS. * +* * +* PARAMETERS * +* * +* ]1 = STRING OR ADDRESS * +* * +* CYCLES: 159+ OR 127+ * +* BYTES: 34+ OR 24+ * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +PRN MAC + IF ",]1 ; {0C0B} IF PARAM IS A STRING + JSR XPRINT ; (159C31B} GOSUB TO STACK PRINT ROUTINE + ASC ]1 ; {0C1B} PUT STRING HERE FOR STACK + HEX 00 ; {0C1B} PRINT TO ACCESS; TERMINATE STRING + ELSE ; {0C0B} OTHERWISE, PARAM IS AN ADDRESS + _MLIT ]1;WPAR1 ; {16C12B} SO PARSE ADDRESS FOR DIRECT OR + JSR DPRINT ; {111C12B} INDIRECT PASSING, THEN GOSUB + FIN ; REGULAR PRINT ROUTINE + <<< +* + +``` + + + +If a literal string is passed as a parameter, the `PRN` macro allocates the required number of bytes for its storage in memory and calls the `XPRINT` subroutine to display the characters on the screen. While this is extremely useful, care should be taken not to use this feature too much: the number of bytes allocated by the macro add up quickly, and the memory used is not regained; the bytes are allocated permanently. When possible, a null-terminated string address should be used instead, as the user can then allocate space in any way necessary. For instance, a block of memory could be used and reused to read in string data from a file, significantly cutting down the total memory used by a program. + +If an address is passed, as suggested, then the macro treats the address like any other macro treats a potential address: a literal address, preceded by a **#** sign, indicates that the string is located at the address (direct), whereas an address reference without the pound sign indicates that the address located at the reference points to where the string is located (indirect). This is parsed via the `_MLIT` macro and passed to the `DPRINT` subroutine, which is dedicated to printing null-terminated strings (for regular strings that have a preceding length-byte, see the `SPRN` macro). + + + +--- + + + +### THE DPRINT SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------- | +| Name | `DPRINT` | +| Type | Subroutine | +| File | `SUB.DPRINT.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Print a null-terminated string passed via address | +| Input | ]1 = memory address | +| Output | *variable* | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 111+ | +| Bytes | 12+ | +| Notes | Required by the PRN Macro | +| See Also | `XPRINT` `SPRN` `PRNSTR` | + +--- + +*DETAILS* + +As `Listing 2.22` shows, the `DPRINT` subroutine accepts a single parameter that is passed via the zero page, which points to the address of the null-terminated string to be printed. The subroutine uses a simple loop to send each character of the string to COUT one at a time until a null value ($00) is found or 255 bytes have been read, at which point the loop is exited and the subroutine returns control to the calling routine. + + + +`LISTING 2.22: DPRINT Subroutine Source` + +```assembly +* +*``````````````````````````````* +* DPRINT (NATHAN RIGGS) * +* * +* INPUT: * +* * +* WPAR1 = STRING ADDRESS (2B) * +* * +* OUTPUT: * +* * +* PRINT A ZERO-TERMINATED * +* STRING AT A GIVEN ADDRESS. * +* * +* DESTROYS: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 111+ * +* SIZE: 12 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]ADDR1 EQU WPAR1 ; INDIRECT ADDRESS IS PASSED HERE +* +DPRINT +* + LDY #$00 ; {2C2B} RESET COUNTER {NZ} +:LOOP + LDA (]ADDR1),Y ; {6C2B} GET NEXT CHARACTER IN STRING {NZ} + BEQ :EXIT ; {3C2B} IF CHAR = $00 THEN EXIT + JSR COUT1 ; {89C2B} OTHERWISE, PRINT CHAR {NZCV} + INY ; {2C1B} INCREAS COUNTER {NZ} + BNE :LOOP ; {3C2B} IF COUNTER < 256, LOOP +:EXIT + RTS ; {6C1B} RETURN TO CALLING ROUTINE + +``` + + + +--- + + + +### THE XPRINT SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `XPRINT` | +| Type | Subroutine | +| File | `SUB.XPRINT.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Print a null-terminated string that follows the call to the procedure | +| Input | none (string put after call to routine) | +| Output | *variable* | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 111+ | +| Bytes | 12+ | +| Notes | Required by the PRN Macro | +| See Also | `DPRINT` `SPRN` `PRNSTR` | + +--- + +*DETAILS* + +The `XPRINT` subroutine sends to COUT the null-terminated string that directly follows the calling of the subroutine, which can be helpful when passing literal strings to the `PRN` macro. This is accomplished with a little bit of program stack trickery: the current address of execution is stored until the printing is finished, at which point the execution address is recalculated (by adding the number of bytes in the string and its null terminator) and fed into the `RTS` command. + +While the `DPRINT` subroutine could be used in a functionally equivalent manner, `XPRINT` is used to exclusively indicate that a string is being stored and accessed during runtime. This also presents an easy way for beginners to print text to the screen that somewhat resembles BASIC syntax. + + + +`LISTING 2.23: XPRINT Subroutine Source` + +```assembly +* +*``````````````````````````````* +* XPRINT (NATHAN RIGGS) * +* * +* INPUT: * +* * +* BYTES IMMEDIATELY AFTER THE * +* CALL TO THE SUBROUTINE ARE * +* PRINTED TO THE SCREEN UNTIL * +* A #00 IS ENCOUNTERED OR * +* 256 CHARS HAVE BEEN REACHED * +* * +* OUTPUT * +* * +* STRING TO SCREEN * +* * +* DESTROY: NZCDIV * +* ^^^ ^ * +* * +* CYCLES: 159+ * +* SIZE: 31 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +XPRINT + PLA ; {4C1B} GET CURRENT EXEC ADDRESS {NZ} + STA ADDR1 ; {3C2B} AND STORE IN ZERO PAGE + PLA ; {4C1B} GET HIGH BYTE {NZ} + STA ADDR1+1 ; {3C2B} AND ALSO STORE AT ZP+1 + LDY #$01 ; {2C2B} LOAD .Y WITH #1 TO POINT {NZ} + ; TO INSTRUCTION AFTER ADDRESS +:LOOP + LDA (ADDR1),Y ; {6C2B} GET CHARACTER FROM NEXT BYTE {NZ} + BEQ :EXIT ; {3C2B} IF CHAR = $00 THEN EXIT LOOP + JSR COUT1 ; {89C2B} OTHERWISE, PRINT CHAR {NZCV} + INY ; {2C1B} INCREASE THE BYTE INDEX {NZ} + BNE :LOOP ; {3C2B} IF INDEX < 255, LOOP +:EXIT + CLC ; {2C1B} CLEAR CARRY {C=0} + TYA ; {2C1B} MOVE .Y INDEX TO .A FOR ADDITION {NZ} + ADC ADDR1 ; {3C2B} ADD EXECUTION ADDRESS LOW BYTE {NZCV} + STA ADDR1 ; {3C2B} SAVE AGAIN ON ZERO PAGE + LDA ADDR1+1 ; {3C2B} GET EXECUTION ADDRESS HIGH BYTE {NZ} + ADC #$00 ; {2C2B} ADD CARRY (ADD 1 TO HI IF C=1) {NZCV} + PHA ; {3C1B} PUSH TNEW HIGH BYTE TO STACK + LDA ADDR1 ; {3C2B} LOAD THE LOW BYTE {NZ} + PHA ; {3C1B} PUSH NEW LOW BYTE TO STACK + RTS ; {6C1B} RETURN TO NEW EXECUTION ADDRESS + +``` + + + +--- + + + +### THE SPRN MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | -------------------------------------------- | +| Name | `SPRN` | +| Type | Macro | +| File | `MAC.COUT.STDOUT.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Displays string with a preceding length byte | +| Input | ]1 = memory address or literal string | +| Output | *variable* | +| Dependencies | `SUB.PRNSTR.ASM` | +| Flags Destroyed | NZCV | +| Cycles | 147+ | +| Bytes | 35 | +| Notes | none | +| See Also | `XPRINT` `DPRINT` `PRNSTR` | + +--- + +*DETAILS* + +Strings come in two distinct forms: either they are null-terminated, meaning that a string continues until a value of $00 is found, or they have a preceding length byte that determines the length of the string (note: special cases, like long strings, may have a preceding two-byte word to indicate the string length). The `PRN` macro already handles the former case while the `SPRN` macro addresses the latter case: printing a string to the screen, via COUT, with an established length byte. + +Generally, this kind of string should be used over that of null-terminated strings (although there are special exceptions). Using a preceding length byte makes strings easier to manage, but it does require a separate printing mechanism. The `SPRN` macro, along with its embedded `PRNSTR` subroutine, serve this end. Note that this macro and its subroutine assumes an 8-bit string with no more than 255 characters, thus allowing for a single preceding byte to convey the string's length. + +The `SPRN` macro is about as simple as a macro gets: the parameters are packed into the registers for passing to the `PRNSTR` subroutine, then the subroutine is called. + + + +`LISTING 2.24: SPRN Macro Source` + +```assembly +* +*``````````````````````````````* +* SPRN * +* * +* PRINTS THE STRING LOCATED AT * +* THE SPECIFIED ADDRESS, WHICH * +* HAS A PRECEDING LENGTH BYTE. * +* * +* PARAMETERS: * +* * +* ]1 = STRING ADDRESS * +* * +* CYCLES: 147+ * +* SIZE: 35 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +SPRN MAC + _AXLIT ]1 ; {8C6B} PARSE INTO REGISTERS + JSR PRNSTR ; {139C29B} EXECUTE PRNSTR ROUTINE + <<< +* + +``` + + + +--- + + + +### THE PRNSTR SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ----------------------------------------------------- | +| Name | `PRNSTR` | +| Type | Subroutine | +| File | `SUB.PRNSTR.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Prints a string with a preceding length byte via COUT | +| Input | ]1 = memory address or literal string | +| Output | *variable* | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 133+ | +| Bytes | 26+ | +| Notes | none | +| See Also | `XPRINT` `DPRINT` `SPRN` | + +--- + +*DETAILS* + +The `PRNSTR` subroutine accepts an address that points to a normal string in the .A and .X registers (low byte and high byte, respectively). This address is first stored in the zero page to allow for indirect referencing, and the first byte, which contains the length of the string, is stored in temporary variable space for later access. After this brief setup is accomplished, the string is then displayed via a simple loop: each character is sent to COUT, one after the other, until the length of the string is reached. Control is then returned to the calling routine. + + + +`LISTING 2.25: PRNSTR Subroutine Source` + +```assembly +*``````````````````````````````* +* PRNSTR (NATHAN RIGGS) * +* * +* INPUT: * +* * +* .A = ADDRESS LOBYTE * +* .X = ADDRESS HIBYTE * +* * +* OUTPUT: * +* * +* PRINTS STRING TO SCREEN. * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 133+ * +* SIZE: 26 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]STRLEN EQU VARTAB ; STRING LENGTH +* +PRNSTR +* + STA ADDR1 ; {3C2B} STORE ADDRESS IN ZERO PAGE + STX ADDR1+1 ; {3C2B} STORE ADDRESS IN ZERO PAGE + LDY #0 ; {2C2B} RESET INDEX {NZ} + LDA (ADDR1),Y ; {6C2B} GET STRING LENGTH {NZ} + STA ]STRLEN ; {4C3B} AND STORE IT IN MEM +:LP + INY ; {3C1B} INCREASE INDEX {NZ} + LDA (ADDR1),Y ; {6C2B} GET CHARACTER {NZ} + JSR COUT1 ; {89C3B} PRINT CHARACTER TO SCREEN {NZCV} + CPY ]STRLEN ; {4C3B} IF Y < LENGTH {NZ} + BNE :LP ; {3C2B} THEN LOOP; ELSE + LDA ]STRLEN ; {4C3B} LOAD STRING LENGTH {NZ} + RTS ; {6C1B} RETURN TO CALLING ROUTINE + +``` + + + +--- + + + +### THE SCPOS MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------------------- | +| Name | `SCPOS` | +| Type | Macro | +| File | `MAC.COUT.STDOUT.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Set COUT cursor to given coordinates | +| Input | ]1 = X-coordinate
]2 = Y-coordinate | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 10+ | +| Bytes | 8 | +| Notes | none | +| See Also | `SETCX` `SETCY` | + +--- + +*DETAILS* + +The `SCPOS` macro sets the coordinates of the COUT cursor in a single sweep by calling the `VTAB` monitor routine, which should exist in every Apple II (and related emulators). The X position (or number of columns) is passed first and stored in `CURSH` on the zero page for `VTAB` to access, and the Y position (or row number) is passed second and stored in `CURSV` on the zero page. `VTAB` is then executed before returning control to the parent program. + +Note that the cursor position is permanently changed, and cannot be undone without storing the current coordinates (found in `CURSH` and `CURSV`) before calling the macro. + + + +`LISTING 2.26: SCPOS Macro Source` + +```assembly +* +*``````````````````````````````* +* SCPOS * +* * +* SETS THE CURSOR POSITION. * +* * +* PARAMETERS * +* * +* ]1 = X POSITION * +* ]2 = Y POSITION * +* * +* CYCLES: 10+ * +* SIZE: 8 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +SCPOS MAC + LDX ]1 ; {2C2B} HORIZONTAL POSITION + STX CURSH ; {3C2B} + LDX ]2 ; {2C2B} VERTICAL POSITION + STX CURSV ; {3C2B} + JSR VTAB ; {????} EXECUTE VTAB ROUTINE + <<< +* + +``` + + + +--- + + + +### THE SETCX MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------------------------- | +| Name | `SETCX` | +| Type | Macro | +| File | `MAC.COUT.STDOUT.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Set COUT cursor to given X-coordinate (column) | +| Input | ]1 = X-coordinate | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 10+ | +| Bytes | 8 | +| Notes | none | +| See Also | `SCPOS` `SETCY` | + +--- + +*DETAILS* + +The `SETCX` macro simply sets the X-coordinate of the COUT cursor, independent of the Y-coordinate. While the `SCPOS` macro can be used to replace `SETCX` and `SETCY` (next) alike, there are cases in which a user may only wish to change one or the other, saving a couple spare cycles in the process. + + + +`LISTING 2.27: The SETCX Macro Source` + +```assembly +* +*``````````````````````````````* +* SETCX * +* * +* SETS THE CURSOR X POSITION. * +* * +* PARAMETERS * +* * +* ]1 = X POSITION * +* * +* CYCLES: 4+ * +* SIZE: 5 * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +SETCX MAC + LDX ]1 ; {2C2B} GET HORIZONTAL POS + STX CURSH ; {2C3B} AND STORE IN CURSH + JSR VTAB ; {????} CALL VTAB ROUTINE + <<< +* + +``` + + + +--- + + + +### THE SETCY MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------------------------- | +| Name | `SETCXY` | +| Type | Macro | +| File | `MAC.COUT.STDOUT.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Set COUT cursor to given Y-coordinate (column) | +| Input | ]1 = X-coordinate | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 4+ | +| Bytes | 5 | +| Notes | none | +| See Also | `SCPOS` `SETCX` | + +--- + +*DETAILS* + +The `SETCY` macro sets the Y-coordinate of the COUT cursor, independent of the X-coordinate. Again, the `SCPOS` can be used to this effect, but there are cases in which a user wants to set the Y-coordinate alone. + + + +`LISTING 2.28: The SETCY Macro Source` + +```assembly +* +*``````````````````````````````* +* SETCY * +* * +* SET THE CURSOR Y POSITION. * +* * +* PARAMETERS * +* * +* ]1 = Y POSITION * +* * +* CYCLES: 4+ * +* SIZE: 5+ * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +SETCY MAC + LDY ]1 ; {2C2B} VERTICAL POSITION + STY CURSV ; {2C3B} INTO CURSV LOCATION + JSR VTAB ; {????} CALL VTAB ROUTINE + <<< +* + +``` + + + +--- + + + +### THE CURF MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------------------------- | +| Name | `CURF` | +| Type | Macro | +| File | `MAC.COUT.STDOUT.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Move COUT cursor forward by a number of spaces | +| Input | ]1 = number of spaces to move | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 10+ | +| Bytes | 7 | +| Notes | none | +| See Also | `CURB` `CURD` `CURU` | + +--- + +*DETAILS* + +The `CURF` macro increases the current X-position of the COUT cursor by a given number of spaces. + + + +`LISTING 2.29: The CURF Macro Source` + +```assembly +* +*``````````````````````````````* +* CURF * +* * +* MOVE CURSOR FORWARD A NUMBER * +* OF SPACES. * +* * +* PARAMETERS * +* * +* ]1 = # OF SPACES TO MOVE * +* * +* CYCLES: 10+ * +* SIZE: 7+ BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +CURF MAC + LDA ]1 ; {2C2B} SPACES TO ADD TO + CLC ; {2C1B} CURRENT POSITION + ADC CURSH ; {3C2B} ADD CURRENT POSITION + STA CURSH ; {3C2B} STORE NEW IN CURSH + JSR VTAB ; {????} CALL VTAB ROUTINE + <<< +* + +``` + + + +--- + + + +### THE CURB MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ----------------------------------------------- | +| Name | `CURB` | +| Type | Macro | +| File | `MAC.COUT.STDOUT.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Move COUT cursor backward by a number of spaces | +| Input | ]1 = number of spaces to move | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 10+ | +| Bytes | 7 | +| Notes | none | +| See Also | `CURD` `CURF` `CURU` | + +--- + +*DETAILS* + +The `CURB` macro decreases the current X-position of the COUT cursor by a given number of spaces. + + + +`LISTING 2.30: The CURB Macro Source` + +```assembly +* +*``````````````````````````````* +* CURB * +* * +* MOVE THE CURSOR BACKWARD BY * +* A NUMBER OF SPACES. * +* * +* PARAMETERS * +* * +* ]1 = # OF SPACES TO MOVE * +* * +* CYCLES: 10+ * +* SIZE: 7 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +CURB MAC + LDA CURSH ; {2C2B} GET CURRENT CURSOR XPOS + SEC ; {2C1B} SET CARRY + SBC ]1 ; {3C2B} SUBTRACT # OF SPACES PASSED + STA CURSH ; {3C2B} AND STORE BACK IN CURSH + JSR VTAB ; {????} EXEC VTAB SUBROUTINE + <<< +* + + +``` + + + +--- + + + +### THE CURU MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | --------------------------------------------- | +| Name | `CURU` | +| Type | Macro | +| File | `MAC.COUT.STDOUT.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Move COUT cursor upward by a number of spaces | +| Input | ]1 = number of spaces to move | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 10+ | +| Bytes | 7 | +| Notes | none | +| See Also | `CURB` `CURD` `CURF` | + +--- + +*DETAILS* + +The `CURU` macro decreases the current Y-position of the COUT cursor by a given number of spaces. + + + +`LISTING 2.31: The CURU Macro Source` + +```assembly +* +*``````````````````````````````* +* CURU * +* * +* MOVE CURSOR UP BY A NUMBER * +* OF SPACES. * +* * +* PARAMETERS * +* * +* ]1 = # OF SPACES TO GO UP * +* * +* CYCLES: 10+ * +* SIZE: 7 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +CURU MAC + LDA CURSV ; {2C2B} GET CURRENT YPOS + SEC ; {2C1B} SET CARRY + SBC ]1 ; {3C2B} SUBTRACT GIVEN PARAM + STA CURSV ; {3C2B} AND STORE BACK IN CURSV + JSR VTAB ; {????} VTAB MONITOR ROUTINE + <<< +* + +``` + + + +--- + + + +### THE CURD MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ----------------------------------------------- | +| Name | `CURD` | +| Type | Macro | +| File | `MAC.COUT.STDOUT.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Move COUT cursor downward by a number of spaces | +| Input | ]1 = number of spaces to move | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 10+ | +| Bytes | 7 | +| Notes | none | +| See Also | `CURB` `CURF` `CURU` | + +--- + +*DETAILS* + +The `CURD` macro increases the current Y-position of the COUT cursor by a given number of spaces. + + + +`LISTING 2.32: The CURD Macro Source` + +```assembly +* +*``````````````````````````````* +* CURD * +* * +* MOVE THE CURSOR DOWN BY A * +* NUMBER OF SPACES. * +* * +* PARAMETERS * +* * +* ]1 = # OF SPACES TO MOVE * +* * +* CYCLES: 9+ * +* SIZE: 8 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +CURD MAC + LDA CURSV ; {2C2B} GET CURRENT YPOS + CLC ; {2C1B} CLEAR CARRY + ADC ]1 ; {3C2B} ADD GIVEN PARAMETER + STA CURSV ; {2C3B} STORE BACK IN CURSV + JSR VTAB ; {????} EXEC VTAB SUBROUTINE + <<< +* + +``` + + + +--- + + + +### THE TMORE MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `TMORE` | +| Type | Macro | +| File | `MAC.COUT.STDOUT.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | word-wrap a null-terminated string
across the screen and pause listing at given intervals | +| Input | ]1 = String Address
]2 = Line Length
]3 = Pausing Interval | +| Output | Zero-terminated string scrolling to the screen | +| Dependencies | `SUB.TXTMORE.ASM` | +| Flags Destroyed | NZCV | +| Cycles | 482+ | +| Bytes | 472 | +| Notes | none | +| See Also | `TXTMORE` | + +--- + +*DETAILS* + +The `TMORE` macro prints a null-terminated string to the screen, word-wrapping it as the lines are printed. Additionally, the macro can be set to pause at a specific interval of lines or, by passing zero in the third parameter, pausing the scroll of the string every time a line feed is encountered. The macro passes these parameters via the zero page to the `TXTMORE` subroutine, which is considerably more complicated than most subroutines in the STDIO collection. See the `TXTMORE`entry for a description of how the word-wrapping implementation works. + + + +`LISTING 2.33: The TMORE Macro Source` + +```assembly +* +*``````````````````````````````* +* TMORE * +* * +* SCROLL THROUGH A ZERO- * +* TERMINATED STRING ON THE * +* SCREEN, PAUSING EVERY GIVEN * +* INTERVAL _OR_ PAUSING EVERY * +* TIME A NEWLINE CHARACTER IS * +* ENCOUNTERED. * +* * +* PARAMETERS * +* * +* ]1 = STRING ADDRESS * +* ]2 = LINE MAXIMUM LENGTH * +* ]3 = LINE PAUSE INTERVAL, * +* EVERY NEWLINE IF = 0 * +* * +* CYCLES: 482+ * +* SIZE: 472 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +TMORE MAC + _MLIT ]1;WPAR1 ; {16C12B} PARSE ADDR TO ZERO PAGE + LDA ]2 ; {2C2B} LOAD MAX LENGTH + STA WPAR2 ; {3C2B} STORE IN ZERO PAGE + LDA ]3 ; {2C2B} LOAD LINE INTERVAL + STA BPAR1 ; {3C2B} STORE IN ZERO PAGE + JSR TXTMORE ; {????} CALL TXTMORE SUBROUTINE + <<< + + +``` + + + +--- + + + +### THE TXTMORE SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `TXTMORE` | +| Type | Subroutine | +| File | `MAC.COUT.STDOUT.ASM` | +| Author | Nathan Riggs | +| Last Revision | 18-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | word-wrap a null-terminated string
across the screen and pause listing at given intervals | +| Input | ]1 = String Address
]2 = Line Length
]3 = Pausing Interval | +| Output | Zero-terminated string scrolling to the screen | +| Dependencies | `SUB.TXTMORE.ASM` | +| Flags Destroyed | NZCV | +| Cycles | 482+ | +| Bytes | 472 | +| Notes | none | +| See Also | `TMORE` | + +--- + +*DETAILS* + +The `TXTMORE` subroutine takes a null-terminated string and prints it to the screen, word-wrapping it and pausing for a keypress at a given interval. Alternatively, a zero interval can be passed to the subroutine that signals that a pause for a keypress should happen every time a newline character is read (#$8D). + +`TXTMORE` looks considerably more complicated than most other subroutines in the STDIO collection, and thus deserves some explanation; it is not, however, as complicated as it might seem. First, a substring between the start of the string and the string start plus the maximum line length is checked for a null character. If one is found, then it means the end of the string is found in the substring, and control is passed to a final routine that prints what is left over of the string. If not found, then the subroutine prepares for the next steps for the substring. + +Next, `TXTMORE` needs to find the last space found in the given substring. The index of this space is stored in temporary memory (if it is not found, then the index is the maximum line length). This means that we are ready to print the substring: each character is read and sent to COUT. If the character is a linefeed (#$8D) then the tracking of the number of printed lines is increased, and if the maximum number of lines is zero (as passed during the call to the subroutine) then the scrolling is paused until a user initiates a keypress. + +Afterwards, we now have a line printed at the desired length; if the line count is equal to the given interval, execution is paused for a keypress. Now a new substring is created started where the last substring ended, and the new substring's end address is calculated by adding the maximum line length. These are placed in the beginning and ending address variables, and the whole process loops back to checking for a null character. Once the null character is found in a substring, that substring is printed, and control is returned back to the calling program. + + + +`LISTING 2.34: TXTMORE Subroutine Source` + +```assembly +* +*``````````````````````````````* +* TXTMORE (NATHAN RIGGS) * +* THIS SUBROUTINE PRINTS A * +* NULL-TERMINATED STRING TO * +* THE SCREEN, WRAPPING THE * +* TEXT FOR A GIVEN LENGTH OF * +* EACH LINE. AFTER A GIVEN * +* NUMBER OF LINES ARE * +* PRINTED, SCROLLING PAUSES * +* WHILE THE READER CATCHES * +* UP. * +* * +* INPUT: * +* * +* WPAR1 = STRING ADDRESS (2B) * +* WPAR2 = LINE LENGTH * +* BPAR1 = VERTICAL SCROLL * +* LENGTH TO PAUSE * +* * +* DESTROYS: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 466+ * +* SIZE: 452 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]ADDR EQU WPAR1 ; STRING ADDRESS +]LEN EQU WPAR2 ; MAXIMUM LENGTH OF STRING +]SCROLL EQU BPAR1 ; MAXIMUM LINES BEFORE PAUSING +* +]START EQU VARTAB ; STARTING POINT OF CURRENT LINE +]END EQU VARTAB+2 ; ENDING OF CURRENT LINE +]PTR EQU WPAR3 ; POINTER BETWEEN START AND END +]LAST EQU VARTAB+4 ; LOCATION OF NULL TERMINATION +]LINES EQU VARTAB+6 ; LINE COUNTER +]LSPACE EQU VARTAB+8 ; LAST SPACE FOUND +* +TXTMORE +* +** INIT +* + LDA ]ADDR ; {2C2B} GET STRING ADDRESS LOW BYTE + STA ]START ; {3C2B} STORE AS START ADDRESS LOW BYTE + LDA ]ADDR+1 ; {2C2B} GET STRING ADDRESS HIGH BYTE + STA ]START+1 ; {3C2B} STORE AS START ADDRESS HIGH BYTE +* + LDA ]ADDR ; {2C2B} LOAD STRING ADDRESS LOW BYTE + CLC ; {2C1B} CLEAR CARRY + ADC ]LEN ; {2C2B} ADD LINE LENGTH + STA ]END ; {3C2B} STORE AS LINE END ADDR LOW BYTE + LDA ]ADDR+1 ; {2C2B} LOAD ADDRESS HIGH BYTE + ADC #0 ; {2C2B} ADD ZERO FOR CARRY + STA ]END+1 ; {2C3B} STORE AS LINE END ADDR HIGH BYTE +* + LDA #0 ; {2C2B} CLEAR REGISTERS + TAY ; {2C1B} + TAX ; {2C1B} +* +** FIRST, CHECK LINE FOR NULL TERMINATION +* +:ENTER + LDA ]START ; {2C2B} FIRST, COPY START INTO + STA ]PTR ; {3C32} OUR POINTER ADDRESS + LDA ]START+1 ; {2C2B} BOTH LOW BYTE AND HIGH BYTE + STA ]PTR+1 ; {3C2B} +* +** CHECK FOR NULL VALUE IN STRING +** +** LOOP WILL EXIT ONLY IF #$00 IS FOUND AT POINTER +** POSITION OR THE POINTER ADDRESS EQUALS THE ENDING ADDRESS +* +:NULCHK + LDY #0 ; {2C2B} + LDA (]PTR),Y ; {6C2B} LOAD CHAR AT CURRENT POINTER + CMP #0 ; {2C2B} COMPARE TO NULL + BEQ :FNDNULL ; {3C2B} IF NULL THEN GOTO :FNDNULL + LDA ]PTR+1 ; {2C2B} CHECK IF HIGH BYTE OF POINTER IS + CMP ]END+1 ; {2C2B} EQUAL TO HIGH BYTE OF ENDING + BNE :CNTLP1 ; {3C2B} IF NOT, CONTINUE LOOP + LDA ]PTR ; {2C2B} IF SO, CHECK LOW BYTE OF POINTER + CMP ]END ; {2C2B} TO SEE IF IT MATCHES END LOW BYTE + BEQ :NULCHKX ; {3C2B} IF IT DOES, THEN EXIT NULL FINDER +:CNTLP1 + LDA ]PTR ; {2C2B} NULL NOT FOUND AND LOOP NOT DONE + CLC ; {2C1B} SO ADD ONE TO THE POINTER + ADC #1 ; {2C2B} LOW BYTE + STA ]PTR ; {3C2B} + LDA ]PTR+1 ; {2C2B} AND ADD CARRY TO THE HIGH BYTE + ADC #0 ; {2C2B} IF NEEDED + STA ]PTR+1 ; {3C2B} + JMP :NULCHK ; {3C3B} AND CONTINUE LOOP +* +** NULL VALUE HAS BEEN FOUND, SO STORE POINTER AND +** GO TO PRINTING LAST LINE OF THE STRING +* +:FNDNULL + LDA ]PTR ; {2C2B} COPY POINTER ADDRESS LOW BYTE + STA ]LAST ; {3C2B} TO THE ]LAST VAR FOR FINAL PRINT + LDA ]PTR+1 ; {2C2B} DO THE SAME WITH THE HIGH BYTE + STA ]LAST+1 ; {3C2B} + JMP :PRNLAST ; {3C3B} JUMP TO FINAL PRINTING +* +** END OF CHECKING FOR NULL. NO NULL WAS FOUND +* +:NULCHKX +* +** NOW FIND THE LAST SPACE BETWEEN ]START POSITION +** AND THE ]END POSITION BY COUNTING BACKWARDS UNTIL +** A SPACE IS FOUND +* + LDA ]END ; {2C2B} LOAD LAST ADDRESS LOW BYTE + STA ]PTR ; {3C2B} STORE IN POINTER + LDA ]END+1 ; {2C2B} LOAD THE HIGH BYTE AND + STA ]PTR+1 ; {3C2B} STORE POINTER HIGH BYTE +* +:SPCLOOP + LDY #0 ; {2C2B} CLEAR .Y INDEX + LDA (]PTR),Y ; {6C2B} LOAD CHAR AT CURRENT POINTER + CMP #" " ; {2C2B} COMPARE CHAR TO SPACE + BEQ :FNDSPC ; {3C2B} IF EQUAL, THEN GOTO :FNDSPC + LDA ]PTR ; {2C2B} OTHERWISE LOAD POINTER LOW BYTE + SEC ; {2C1B} SET CARRY + SBC #1 ; {2C2B} SUBTRACT 1 FROM ADDRESS + STA ]PTR ; {3C2B} STORE BACK IN LOW BYTE + LDA ]PTR+1 ; {2C2B} LOAD POINTER HIGH BYTE + SBC #0 ; {2C2B} SUBTRACT CARRY, IF NEEDED + STA ]PTR+1 ; {3C2B} STORE BACK IN HIGH BYTE + JMP :SPCLOOP ; {3C3B} LOOP UNTIL A SPACE IS FOUND +* +** THE LAST SPACE BETWEEN ]START AND ]END HAS BEEN FOUND! +* +:FNDSPC + LDA ]PTR ; {2C2B} LOAD LOW BYTE OF CURRENT PTR + STA ]LSPACE ; {3C2B} AND STORE INTO LAST SPACE VAR + STA ]END ; {3C2B} + LDA ]PTR+1 ; {2C2B} AND THEN DO THE SAME FOR THE + STA ]LSPACE+1 ; {3C2B} HIGH BYTE + STA ]END+1 ; {3C2B} +* +** RESET POINTER AGAIN +* + LDA ]START ; {2C2B} LOAD STARTING POINT + STA ]PTR ; {3C2B} PACK INTO POINTER ADDRESS + LDA ]START+1 ; {2C2B} BOTH LOW BYTE AND + STA ]PTR+1 ; {3C2B} HIGH BYTE +* +** NOW PRINT THE LINE +* +:PRNLP ; PRINTING LOOP + LDA ]PTR+1 ; {2C2B} LOAD HIGH BYTE OF POINTER + CMP ]LSPACE+1 ; {2C2B} COMPARE TO HIGH BYTE OF LAST SPACE + BNE :PRNLP0 ; {3C2B} IF !=, THEN SKIP TO INNER LOOP + LDA ]PTR ; {2C2B} IF =, THEN COMPARE LOW BYTES + CMP ]LSPACE ; {2C2B} + BNE :PRNLP0 ; {3C2B} IF !=, GOTO INNER LOOP + LDA ]LINES ; {2C2B} OTHERWISE, LOAD CURRENT # OF LINES + CLC ; {2C1B} CLEAR CARRY + ADC #1 ; {3C2B} ADD A LINE + STA ]LINES ; {3C2B} STORE BACK INTO LINES + LDA ]LINES+1 ; {2C2B} LOAD HIGH BYTE OF LINES + ADC #0 ; {3C2B} ADD CARRY + STA ]LINES+1 ; {3C2B} STORE BACK INTO HIGH BYTE + LDA #$8D ; {2C2B} LOAD A LINE FEED CHARACTER + JSR COUT ; {6+C3B} SEND TO COUT (PRESS RETURN) + JMP :XXX ; {3C3B} SKIP REPRINTING SPACE +:PRNLP0 ; INNER PRINT LOOP + LDY #0 ; {2C2B} RESET .Y (JUST IN CASE?) + LDA (]PTR),Y ; {6C2B} LOAD CHARACTER AT POINTER + JSR COUT ; {6+C2B} PRINT CHARACTER + LDA (]PTR),Y ; {2C2B} LOAD AGAIN, JUST IN CASE + CMP #$8D ; {2C2B} IF CHAR != LINE FEED + BNE :XXX ; {3C2B} SKIP LINE ADDING + LDA ]LINES ; {2C2B} ELSE LOAD LINES COUNTER + CLC ; {2C1B} CLEAR CARRY + ADC #1 ; {3C2B} ADD A LINE + STA ]LINES ; {3C2B} STORE BACK INTO LOW BYTE + LDA ]LINES+1 ; {2C2B} LOAD HIGH BYTE OF LINES + ADC #0 ; {3C2B} ADD CARRY + STA ]LINES+1 ; {3C2B} STORE BACK INTO HIGH BYTE + LDA ]SCROLL ; {2C2B} LOAD MAX NUMBER OF LINES + CMP #0 ; {2C2B} COMPARE TO 0 + BNE :XXX ; {3C2B} IF MAX != 0, SKIP PAUSING +:WLP LDA ]KYBD ; {2C2B} CHECK FOR KEYSTROKE + BPL :WLP ; {3C2B} IF .A HAS CLEAR HIGH BYTE + AND #$7F ; {2C2B} THEN NOT PRESSED; LOOP UNTIL + STA ]STROBE ; {3C2B} CLEAR THE KEYBOARD STROBE + LDA #0 ; {2C2B} CLEAR .A + STA ]LINES ; {3C2B} RESET LINE COUNTER + STA ]LINES+1 ; {3C2B} RESET LINE COUNTER HIGH BYTE +* +:XXX + LDA ]PTR+1 ; {2C2B} LOAD POINTER HIGH BYTE + CMP ]END+1 ; {2C2B} COMPARE TO ENDING HIGH BYTE + BNE :CNTPRN ; {3C2B} IF !=, CONTINUE PRINTING + LDA ]PTR ; {2C2B} ELSE LOAD POINTER LOW BYTE + CMP ]END ; {2C2B} COMPARE TO ENDING LOW BYTE + BEQ :PRNLPX ; {3C2B} IF EQUAL, EXIT PRINTING +:CNTPRN + LDA ]PTR ; {2C2B} LOAD POINTER LOW BYTE + CLC ; {2C1B} CLEAR CARRY + ADC #1 ; {3C2B} INCREASE ADDRESS BY ONE + STA ]PTR ; {3C2B} STORE BACK INTO LOW BYTE + LDA ]PTR+1 ; {2C2B} LOAD POINTER HIGH BYTE + ADC #0 ; {3C2B} ADD THE CARRY + STA ]PTR+1 ; {3C2B} STORE BACK INTO HIGH BYTE + JMP :PRNLP ; {3C3B} RESTART THE PRINTING LOOP +* +** FINISHED PRINTING LINE, SO NOW SWAP POINTER VARIABLES +** TO MOVE ON TO THE NEXT SECTION +* +:PRNLPX ; END PRINTING + LDA ]SCROLL ; {2C2B} LOAD MAX LINES + CMP ]LINES ; {2C2B} COMPARE TO CURRENT LINES + BNE :SWAPPP ; {3C2B} IF !=, SKIP WAIT +:WLP3 LDA ]KYBD ; {2C2B} WAIT FOR A KEYPRESS + BPL :WLP3 ; {3C2B} + AND #$7F ; {2C2B} + STA ]STROBE ; {3C2B} RESET KEYBOARD STROBE + LDA #0 ; {2C2B} RESET .A + STA ]LINES ; {3C2B} RESET LINE COUNTER LOW BYTE + STA ]LINES+1 ; {3C2B} RESET LINE COUNTER HIGH BYTE +:SWAPPP + LDA ]END ; {2C2B} LOAD ENDING ADDRESS LOW BYTE + CLC ; {2C1B} CLEAR CARRY + ADC #1 ; {3C2B} INCREASE ADDRESS BY ONE + STA ]END ; {3C2B} STORE BACK INTO LOW BYTE + LDA ]END+1 ; {2C2B} LOAD ENDING HIGH BYTE + ADC #0 ; {3C2B} ADD THE CARRY + STA ]END+1 ; {3C2B} STORE BACK INTO HIGH BYTE +* + LDA ]END ; {2C2B} RELOAD ENDING LOW BYTE + STA ]START ; {3C2B} STORE IN STARTING LOW BYTE + LDA ]END+1 ; {2C2B} RELOAD ENDING HIGH BYTE + STA ]START+1 ; {3C2B} STORE IN STARTING HIGH BYTE + LDA ]END ; {2C2B} RELOAD ENDING LOW BYTE + CLC ; {2C1B} CLEAR CARRY + ADC ]LEN ; {3C2B} ADD MAX LENGTH TO ENDING + STA ]END ; {3C2B} STORE BACK INTO LOW BYTE + LDA ]END+1 ; {2C2B} LOAD ENDING HIGH BYTE + ADC #0 ; {3C2B} ADD THE CARRY + STA ]END+1 ; {3C2B} STORE BACK INTO HIGH BYTE + JMP :ENTER ; {3C3B} BACK TO BEGINNING OF SUBROUTINE! +:PRNLAST + LDA ]START ; {2C2B} LOAD STARTING ADDRESS + STA ]PTR ; {3C2B} STORE LOW BYTE INTO POINTER LOW + LDA ]START+1 ; {2C2B} LOAD STARTING HIGH BYTE + STA ]PTR+1 ; {3C2B} STORE IN POINTER HIGH BYTE +:LASTLP + LDY #0 ; {2C2B} CLEAR .Y JUST IN CASE + LDA (]PTR),Y ; {6C2B} LOAD CHARACTER + JSR COUT ; {6+C3B} SEND CHARACTER TO COUT + LDA ]PTR+1 ; {2C2B} LOAD POINTER HIGH BYTE + CMP ]LAST+1 ; {2C2B} COMPARE IT TO NULL POINTER POS + BNE :LASTCNT ; {3C2B} IF !=, THEN LOOP + LDA ]PTR ; {2C2B} LOAD POINTER LOW BYTE AGAIN + CMP ]LAST ; {2C2B} COMPARE IT TO NULL POSITION LOW + BEQ :EXIT ; {3C2B} IF EQUAL, THEN EXIT +:LASTCNT + LDA ]PTR ; {2C2B} OTHERWISE, LOAD POINTER LOW BYTE + CLC ; {2C1B} CLEAR CARRY + ADC #1 ; {3C2B} ADD ONE TO ADDRESS + STA ]PTR ; {3C2B} STORE LOW BYTE AGAIN + LDA ]PTR+1 ; {2C2B} LOAD POINTER HIGH BYTE + ADC #0 ; {3C2B} ADD THE CARRY + STA ]PTR+1 ; {3C2B} STORE BACK IN POINTER HIGH BYTE + JMP :LASTLP ; {3C3B} LOOP AGAIN UNTIL LAST IS REACHED +:EXIT + LDA ]SCROLL ; {2C2B} LOAD MAX LINES + CMP #0 ; {2C2B} IF = 0, THEN JUST EXIT + BEQ :EXIT2 ; {3C2B} +:WLPZ LDA ]KYBD ; {2C2B} OTHERWISE, PAUSE FOR KEYPRESS + BPL :WLPZ ; {3C2B} BEFORE LEAVING SUBROUTINE + AND #$7F ; {2C1B} + STA ]STROBE ; {3C2B} CLEAR KEYBOARD STROBE +:EXIT2 + RTS ; {6C1B} + +``` + + + +--- + + + +## Screen Memory Macros and Subroutines + +There are many instances when avoiding COUT for screen output is either necessary or desirable, whether it be due to needing much greater speed or wanting to write a custom input/output system that might compete with COUT. In such cases, the macros and subroutines listed as part of this collection help plot text characters to the screen memory directly. + +The `CPUT` and `SPUT` macros are especially pertinent to such uses; however, there are many other optional macros and their respective subroutines that plot to screen memory in order to create rudimentary shapes for creating interfaces, ASCII art, or text animations. This includes macros to create 1) vertical lines and horizontal lines as well as diagonals, 2) open and closed rectangles, 3) circles and 4) screen fills. In addition, this library carries the `RCPOS` macro, which reveals a character in screen memory when given an X, Y coordinate. + +All of these macros are held in the `MAC.SCRMEM.STDIO.ASM` file. The heading of the file is listed below. + + + +`LISTING 2.40: Screen Memory Macro File Heading` + +```assembly +* +*``````````````````````````````* +* MAC.SCRMEM.STDIO.ASM * +* * +* THIS IS A MACRO LIBRARY FOR * +* STANDARD INPUT AND OUTPUT. * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 11-MAR-2021 * +* ASSEMBLER: MERLIN 8 PRO * +* OS: DOS 3.3 * +* * +* SUBROUTINES FILES USED: * +* * +* SUB.TVLINE * +* SUB.THLINE * +* SUB.TRECTF * +* SUB.TBLINE * +* SUB.TCIRCLE * +* SUB.TXTPUT * +* SUB.TXTCLR * +* SUB.STRPUT * +* SUB.TRECT * +* * +* LIST OF MACROS * +* * +* RCPOS : READ CURSOR POSITION * +* TLINE : DIAGONAL TEXT LINE * +* TCIRC : TEXT CIRCLE * +* TVLIN : TEXT VERTICAL LINE * +* THLIN : TEXT HORIZ LINE * +* TRECF : TEXT FILL RECTANGLE * +* CPUT : TEXT CHAR PLOT AT XY * +* TCLR : FILL SCREEN W/ CHAR * +* TREC : CREATE UNFILLED RECT * +* SPUT : STRING PUT * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* + +``` + + + +--- + + + +### THE RCPOS MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------------------------------------- | +| Name | `RCPOS` | +| Type | Macro | +| File | `MAC.SCRMEM.STDIO.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Return the character value at the given screen coordinates | +| Input | ]1 = X-position
]2 = Y-position | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 10+ | +| Bytes | 6 | +| Notes | none | +| See Also | `SCPOS` | + +--- + +*DETAILS* + +The `RCPOS` macro retrieves the character on screen at the given X and Y coordinates, returning the character via the **.A** register. Technically, this works regardless of whether COUT is being used, but it is included in the Screen Memory Access group because it simply reads the data at an encoded address in screen memory. Neither Applesoft nor Integer BASIC has an equivalent routine for text, although this works much like the SCRN() statement for Lo-Res graphics in Applesoft. +`LISTING 2.41: The RCPOS Macro Source` + +```assembly +* +*``````````````````````````````* +* RCPOS * +* * +* READ THE CHARACTER AT POS * +* X,Y AND LOADS INTO ACCUM * +* * +* PARAMETERS * +* * +* ]1 = X POSITION * +* ]2 = Y POSITION * +* * +* CYCLES: 10+ * +* SIZE: 6 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +RCPOS MAC + LDY ]1 ; {2C2B} PUT ROW INTO .Y + LDA ]2 ; {2C2B} PUT COLUMN IN .A + JSR GBCALC ; {????} GET MEM ADDRESS FOR COORDS + LDA (GBPSH),Y ; {6C2B} GET CHARACTER FROM MEMORY + <<< +* + +``` +--- +### THE TVLIN MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `TVLIN` | +| Type | Macro | +| File | `MAC.SCRMEM.STDIO.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Draw a vertical line to screen memory | +| Input | ]1 = Y-position Origin
]2 = Y-position Destination
]3 = X-position
]4 = Fill Character | +| Output | A vertical line made up of a given character
directly plotted to screen memory | +| Dependencies | `SUB.TVLINE.ASM` | +| Flags Destroyed | NZCV | +| Cycles | 104+ | +| Bytes | 45 | +| Notes | none | +| See Also | `TVLINE` `THLIN` `THLINE` | + +--- + +*DETAILS* + +The `TVLIN` macro draws a vertical line to screen memory made up of a given fill character at the passed X, Y coordinate. Since the output is plotted directly to screen memory, this does not "play nicely" with COUT. + +It may seem trivial to have this macro and dedicated subroutine for creating vertical lines, since the `TBLINE` subroutine can create any line: vertical, horizontal, or diagonal. However, `TBLINE` and its associated macro (`TLINE`) uses many more cycles to accomplish the same functionality here; therefore, when speed is a concern, `TVLIN` should be used to create lines that are already known to be perfectly vertical lines unless the size of the program is of utmost concern. + + + +`LISTING 2.42: TVLIN Macro Source` + +```assembly +* +*``````````````````````````````* +* TVLIN * +* * +* CREATE A VERTICAL LINE WITH * +* A GIVEN TEXT FILL CHARACTER * +* * +* PARAMETERS * +* * +* ]1 = START OF VERT LINE * +* ]2 = END OF VERT LINE * +* ]3 = X POSITION OF LINE * +* ]4 = FILL CHARACTER * +* * +* CYCLES: 104+ * +* SIZE: 45 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +TVLIN MAC + LDA ]1 ; {2C2B} Y-COORDINATE ORIGIN + STA WPAR2 ; {3C2B} PASS VIA ZERO PAGE + LDA ]2 ; {2C2B} Y-COORDINATE DESTINATION + STA WPAR2+1 ; {3C2B} PASS VIA ZERO PAGE + LDA ]3 ; {2C2B} X-COORDINATE OF LINE + STA WPAR1 ; {3C2B} PASS VIA ZERO PAGE + LDA ]4 ; {3C2B} CHARACTER TO PLOT + STA BPAR1 ; {3C2B} PASS VIA ZERO PAGE + JSR TVLINE ; {83C29B} CALL TVLINE SUBROUTINE + <<< +* + +``` + + + +--- + + + +### THE TVLINE Subroutine + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `TVLINE` | +| Type | Subroutine | +| File | `SUB.TVLINE.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Draw a vertical line to screen memory | +| Input | WPAR1 = X-position
WPAR2 = Y-position Origin
WPAR2+1 = Y-position Destination
BPAR1 = Fill Character | +| Output | A vertical line made up of a given character
directly plotted to screen memory | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 77+ | +| Bytes | 26 | +| Notes | none | +| See Also | `TVLIN` `THLIN` `THLINE` | + +--- + +*DETAILS* + +The `TVLINE` subroutine takes an X-coordinate, a Y-origin and a Y-destination as well as a fill character as parameters (sent via the zero page), then "paints" a vertical line to screen memory with the passed fill character. Although the `TBLINE` subroutine can create vertical lines as well, this subroutine takes substantially fewer cycles and therefore should be used in its stead when possible. + +The subroutine first loads the Y-origin into the **.A** register and the X-position in the **.Y** register in preparation for calling **GBCALC**, which returns the address in screen memory of a given coordinate (this address is returned in the zero page at the **GBPSH** location). The fill character is then plotted at the resulting address, and a loop is begun that increments the Y-origin every iteration, plotting each character in the line until the Y-origin equals the Y-destination. Once finished, control is returned back to the calling program or routine. + + + +`FIGURE 2.43: TVLINE Subroutine Source` + +```assembly +* +*``````````````````````````````* +* TVLINE (NATHAN RIGGS) * +* * +* INPUT: * +* * +* ]X1 STORED AT WPAR1 * +* ]Y1 STORED AT WPAR2 * +* ]Y2 STORED AT WPAR2+1 * +* ]F STORED AT BPAR1 * +* * +* OUTPUT: VERT LINE TO SCREEN * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 77+ * +* SIZE: 26 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]X1 EQU WPAR1 ; X COLUMN OF LINE +]Y1 EQU WPAR2 ; Y POSITION ORIGIN +]Y2 EQU WPAR2+1 ; Y POSITION DESTINATION +]F EQU BPAR1 ; LINE FILL CHARACTER +* +TVLINE +* + LDA ]Y1 ; {4C3B} LOAD Y ORIGIN {NZ} + LDY ]X1 ; {4C3B} LOAD X POSITION {NZ} +:LOOP + JSR GBCALC ; {43C3B} GET POS SCREEN ADDRESS {NZCV} + LDA ]F ; {4C3B} LOAD FILL CHARACTER {NZ} + STA (GBPSH),Y ; {6C2B} PLOT TO SCREEN MEMORY + INC ]Y1 ; {6C3B} INCREASE Y POSITION {NZ} + LDA ]Y1 ; {4C3B} RELOAD Y POSITION {NZ} + CMP ]Y2 ; {4C3B} IF Y1 < Y2 {NZC} + BNE :LOOP ; {3C2B} LOOP; ELSE, CONTINUE +:EXIT + RTS ; {6C1B} + +``` + + + +--- + + + +### THE THLIN MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `THLIN` | +| Type | Macro | +| File | `MAC.SCRMEM.STDIO.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Draw a horizontal line to screen memory | +| Input | ]1 = X-position Origin
]2 = X-position Destination
]3 = Y-position
]4 = Fill Character | +| Output | A horizontal line made up of a given character
directly plotted to screen memory | +| Dependencies | `SUB.THLINE.ASM` | +| Flags Destroyed | NZCV | +| Cycles | 107+ | +| Bytes | 38 | +| Notes | none | +| See Also | `TVLIN` `TVLINE` `THLINE` | + +--- + +*DETAILS* + +The `THLIN` macro plots a horizontal line made up of a given fill character directly to screen memory. Like the `TVLIN` macro, `THLIN` can be replaced by the `TLINE` macro, but only if program size is of utmost concern; the subroutine called, `THLINE`, uses substantially fewer cycles than the `TLINE` macro due to the fact that its flat, horizontal nature is already known. + + + +`FIGURE 2.44: THLIN Macro Source` + +```assembly +* +*``````````````````````````````* +* THLIN * +* * +* CREATE A HORIZONTAL LINE * +* FROM A FILL CHARACTER. * +* * +* PARAMETERS * +* * +* ]1 = START OF HORIZ LINE * +* ]2 = END OF HORIZ LINE * +* ]3 = Y POSITION OF LINE * +* ]4 = FILL CHARACTER * +* * +* CYCLES: 107+ * +* SIZE: 38 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +THLIN MAC + LDA ]1 ; {2C2B} X-COORDINATE ORIGIN + STA WPAR1 ; {3C2B} PASS VIA ZERO PAGE + LDA ]2 ; {2C2B} X-COORDINATE DESTINATION + STA WPAR1+1 ; {3C2B} PASS VIA ZERO PAGE + LDA ]3 ; {2C2B} Y-COORDINATE OF LINE + STA BPAR1 ; {3C2B} PASS VIA ZERO PAGE + LDA ]4 ; {2C2B} CHARACTER TO FILL + STA BPAR2 ; {3C2B} PASS VIA ZERO PAGE + JSR THLINE ; {87C22B} CALL THLINE SUBROUTINE + <<< +* + +``` + + + +--- + + + +### THE THLINE Subroutine + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `THLINE` | +| Type | Subroutine | +| File | `SUB.THLINE.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Draw a horizontal line to screen memory | +| Input | WPAR1 = X-position Origin
WPAR1+1 = X-position Destination
BPAR1 = Y-position
BPAR2 = Fill Character | +| Output | A horizontal line made up of a given character
directly plotted to screen memory | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 77+ | +| Bytes | 26 | +| Notes | none | +| See Also | `TVLIN` `TVLINE` `THLIN` | + +--- + +*DETAILS* + +The `THLINE` subroutine accepts parameters from the zero page that include an X-position origin, X-position destination, Y-position, and a fill character to draw a horizontal line made of the given text character directly to screen memory. For the most part this is complementary to the `TVLINE` subroutine, and like `TVLINE` this subroutine could be replaced by the `TBLINE` subroutine on its own. However, like with `TVLINE`, this subroutine uses substantially fewer cycles than `TBLINE`, and should be used when it is known that the line will be a straight, horizontal line. + +`THLINE` works much the same as `TVLINE`, but differs in the fact that it creates a horizontal line rather than a vertical one. First the Y-position is loaded into **.A** with the X-position origin loaded in **.Y** in order to retrieve the starting address via the GBCALC routine (often known as GBASCALC in other implementations; the name is abbreviated here for convenience). Afterwards, the X-position origin is increased with the passing of a loop and a new character is plotted. This is repeated until the X-position origin equals the X-position destination, at which point control is then returned back to the calling routine. + + + +`FIGURE 2.45: The THLINE Subroutine Source` + +```assembly +* +*``````````````````````````````* +* THLINE (NATHAN RIGGS) * +* * +* INPUT: * +* * +* WPAR1 = X ORIGIN * +* WPAR1+1 = X DESTINATION * +* BPAR1 = Y POSITION * +* BPAR2 = FILL CHARACTER * +* * +* OUTPUT: HORIZONTAL LINE TO * +* SCREEN * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 81+ * +* SIZE: 19 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]X1 EQU WPAR1 ; X POS ORIGIN +]X2 EQU WPAR1+1 ; X POS DESTINATION +]Y1 EQU BPAR1 ; Y POSITION +]F EQU BPAR2 ; FILL CHAR +* +THLINE + LDA ]Y1 ; {3C2B} LOAD ROW {NZ} + LDY ]X1 ; {3C2B} LOAD X START POS {NZ} +:LOOP + JSR GBCALC ; {49C3B} GOSUB GBASCALC ROUTINE, {NZCV} + ; WHICH FINDS MEMLOC FOR + ; POSITION ON SCREEN + LDA ]F ; {3C2B} LOAD FILL CHAR {NZ} + STA (GBPSH),Y ; {6C2B} PUSH ]F TO SCREEN MEM + LDA ]Y1 ; {3C2B} LOAD Y POSITION {NZ} + INY ; {2C1B} INCREASE X POS {NZ} + CPY ]X2 ; {3C2B} IF < X DEST THEN END {NZC} + BNE :LOOP ; {3C2B} REPEAT UNTIL DONE +:EXIT + RTS ; {6C1B} + +``` + + + +--- + + + +### THE TRECF MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `TRECF` | +| Type | Macro | +| File | `MAC.SCRMEM.STDIO.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Draw a filled rectangle to screen memory | +| Input | ]1 = X-position Origin
]2 = Y-position Origin
]3 = X-position Destination
]4 = Y-position Destination
]5 = Fill Character | +| Output | A filled rectangle made up of a given character
directly plotted to screen memory | +| Dependencies | `SUB.TRECTF.ASM` | +| Flags Destroyed | NZCV | +| Cycles | 157+ | +| Bytes | 77 | +| Notes | none | +| See Also | `TRECTF` `TREC` `TRECF` | + +--- + +*DETAILS* + +The `TRECF` macro uses the `TRECTF` subroutine to draw a rectangle to screen memory that is filled with the character passed in the parameters (5th parameter). These parameters are passed to the subroutine via the zero page. + + + +`FIGURE 2.46: TRECF Macro Source` + +```assembly +* +*``````````````````````````````* +* TRECF * +* * +* CREATE A RECTANGLE FILLED * +* WITH A GIVEN TEXT CHARACTER * +* * +* PARAMETERS * +* * +* ]1 = HORIZ START POSITION * +* ]2 = VERT START POSITION * +* ]3 = HORIZ END POSITION * +* ]4 = VERT END POSITION * +* ]5 = FILL CHARACTER * +* * +* CYCLES: 157+ * +* SIZE: 77 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +TRECF MAC + LDA ]1 ; {2C2B} X-COORDINATE ORIGIN + STA WPAR1 ; {3C2B} PASS VIA ZERO PAGE + LDA ]2 ; {2C2B} Y-COORDINATE ORIGIN + STA WPAR2 ; {3C2B} PASS VIA ZERO PAGE + LDA ]3 ; {2C2B} X-COORDINATE DESTINATION + STA WPAR1+1 ; {3C2B} PASS VIA ZERO PAGE + LDA ]4 ; {2C2B} Y-COORDINATE DESTINATION + STA WPAR2+1 ; {2C2B} PASS VIA ZERO PAGE + LDA ]5 ; {2C2B} CHARACTER TO PLOT + STA BPAR1 ; {3C2B} PASS VIA ZERO PAGE + JSR TRECTF ; {133C57B} CALL TRECTF SUBROUTINE + <<< +* + +``` + + + +--- + + + +### THE TRECTF Subroutine + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `TRECTF` | +| Type | Subroutine | +| File | `SUB.TRECTF.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Draw a filled rectangle line to screen memory | +| Input | WPAR1 = X-position Origin
WPAR1+1 = X-position Destination
WPAR2 = Y-position Origin
WPAR2+1 = Y-position Destination
BPAR1 = Fill Character | +| Output | A filled rectangle made up of a given character
directly plotted to screen memory | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 127+ | +| Bytes | 54 | +| Notes | none | +| See Also | `TRECF` `TREC` `TRECF` | + +--- + +*DETAILS* + +The `TRECTF` subroutine reads parameters from the zero page that include an X, Y origin, and X, Y destination, and a fill character to create a filled rectangle on the text screen. This is plotted via direct access to screen memory, and thus does not interfere with--or play nice with--COUT routines. + +This subroutine creates a successive series of horizontal lines in screen memory that constitute a rectangle of the given dimensions together. First, the X and Y origins are stored in temporary variable storage as the change in X and the change in Y, then the lookup and plotting loop is started. The Change in X and change in Y are loaded into **.A** and **.Y** respectively in order to find the screen memory address of the coordinates using **GBCALC**. This is then plotted to the screen after retrieving the address from the zero page (**GBPSH**). Each X position in the line is plotted as the **.Y** index counter is increased to form a horizontal line until the line is a proper length (the length of the desired rectangle), at which point the Y position is increased, the X position is reset, and the loop continues plotting the next line until the Y destination is reached. Afterward, control is returned to the calling program or routine. + +`FIGURE 2.47: The TRECTF Subroutine Source` + +```assembly +* +*``````````````````````````````* +* TRECTF (NATHAN RIGGS) * +* * +* INPUT: * +* * +* WPAR1 = X ORIGIN * +* WPAR1+1 = X DESTINATION * +* WPAR2 = Y ORIGIN * +* WPAR2+1 = Y DESTINATION * +* BPAR1 = FILL CHARACTER * +* * +* OUTPUT * +* * +* FILLED RECTANGLE TO SCREEN * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 127+ * +* SIZE: 54 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]X1 EQU WPAR1 ; TOP LEFT OF RECTANGLE +]X2 EQU WPAR1+1 ; BOTTOM RIGHT OF RECTANGLE +]Y1 EQU WPAR2 ; TOP Y POSITION OF RECTANGLE +]Y2 EQU WPAR2+1 ; BOTTOM POSITION OF RECTANGLE +]F EQU BPAR1 ; FILL CHARACTER +* +]XC EQU VARTAB ; CHANGE IN X +]YC EQU VARTAB+1 ; CHANGE IN Y +* +TRECTF + LDA ]X1 ; {4C3B} LOAD TOP LEFT X ORIGIN {NZ} + STA ]XC ; {4C3B} STORE AS INITIAL INDEX + LDA ]Y1 ; {3C2B} LOAD TOP LEFT Y ORIGIN {NZ} + STA ]YC ; {4C3B} STORE AS INITIAL INDEX +:LP1 ; PRINT HORIZONTAL LINE + LDA ]YC ; {4C3B} LOAD FIRST Y INDEX {NZ} + LDY ]XC ; {4C3B} LOAD FIRST X INDEX IN Y {NZ} + JSR GBCALC ; {43C3B} GET SCREEN MEMORY ADDR {NZCV} + LDA ]F ; {3C2B} LOAD FILL CHARACTER {NZ} + STA (GBPSH),Y ; {6C2B} PUT CHAR IN SCREEN MEMORY {NZ} + LDA ]YC ; {4C3B} LOAD Y INDEX {NZ} + INY ; {2C1B} INCREASE XPOS INDEX {NZ} + STY ]XC ; {4C3B} STORE NEW X INDEX + CPY ]X2 ; {4C3B} IF XPOS < XMAX, {NZC} + BNE :LP1 ; {3C2B} KEEP PRINTING LINE +* + LDA ]X1 ; {4C3B} OTHERWISE, RESET XPOS {NZ} + STA ]XC ; {4C3B} AND STORE IN INDEX + INC ]YC ; {6C3B} AND INCREASE YPOS {NZ} + LDA ]YC ; {4C3B} RELOAD Y INDEX {NZ} + CMP ]Y2 ; {4C3B} IF YPOS < YMAX {NZC} + BNE :LP1 ; {3C2B} PRINT HORIZONTAL LINE +:EXIT + RTS ; {6C1B} + +``` + + + +--- + + + +### THE CPUT MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `CPUT` | +| Type | Macro | +| File | `MAC.SCRMEM.STDIO.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Plot a single character to screen memory | +| Input | ]1 = X-position
]2 = Y-position
]3 = Fill Character | +| Output | A single character is plotted to screen memory
at the desired X, Y coordinate. | +| Dependencies | `SUB.TXTPUT.ASM` | +| Flags Destroyed | NZCV | +| Cycles | 91+ | +| Bytes | 31 | +| Notes | none | +| See Also | `TXTPUT` `SPUT` `STRPUT` `PRN` | + +--- + +*DETAILS* + +The `CPUT` macro uses the `TXTPUT` subroutine to plot a character directly to screen memory, bypassing COUT. While this is functionally similar to the COUT routine, it is much faster due to the fact that it does not consider the extra functionality that COUT provides, and only plots a character on the screen. + +`CPUT` does no more than loads parameters into the registers for passing to the `TXTPUT` subroutine, which handles the actual implementation. The X-position is loaded into **.X**, the Y-position in **.Y**, and the fill character in **.A**. + + + +`FIGURE 2.48: CPUT Macro Source` + +```assembly +* +*``````````````````````````````* +* CPUT * +* * +* PLOT A SINGLE TEXT CHARACTER * +* DIRECTLY TO SCREEN MEMORY AT * +* A GIVEN X,Y POSITION. * +* * +* PARAMETERS * +* * +* ]1 = X POSITION * +* ]2 = Y POSITION * +* ]3 = CHARACTER TO PLOT * +* * +* CYCLES: 91+ * +* SIZE: 31 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +CPUT MAC + LDX ]1 ; {2C2B} CARRY X-COORD IN .X + LDY ]2 ; {2C2B} CARRY Y-COORD IN .Y + LDA ]3 ; {2C2B} CARRY FILL CHAR IN .A + JSR TXTPUT ; {85C25B} CALL TXTPUT SUBROUTINE + <<< +* + +``` + + + +--- + + + +### THE TXTPUT Subroutine + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `TXTPUT` | +| Type | Subroutine | +| File | `SUB.TXTPUT.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Plot a single character to screen memory | +| Input | .A = Fill Character
.X = X-position
.Y = Y-position | +| Output | A single character is placed in screen memory,
thus displaying on the screen. | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 79+ | +| Bytes | 22 | +| Notes | none | +| See Also | `CPUT` `SPUT` `STRPUT` `PRN` | + +--- + +*DETAILS* + +The `TXTPUT` subroutine retrieves a fill character and X, Y coordinates from the registers and plots the character directly to screen memory at the desired location. This is done via using **GBCALC** (also known as GBASCALC) to determine the memory address of the given coordinates. + +Technically, this subroutine could use some optimization, especially since its purpose is so fundamental to possible extensions of its use. Changes to the subroutine and its calling macro, `CPUT`, can prevent the extra cycles and bytes used in storing the coordinates and fill character in temporary variable space, and if this is a concern then a user should make the appropriate changes. + +However, `TXTPUT` is implemented, like all of the library's routines, so that a beginner can easily understand how it works. Most of the library holds variables either in the zero page or in temporary storage, and the less-than-optimal implementation of `TXTPUT`reflects this strategy. This may be changed in future revision, if the need seems to outweigh the benefits of beginner-level understanding. + + + +`FIGURE 2.49: TXTPUT Subroutine Source` + +```assembly +* +*``````````````````````````````* +* TXTPUT (NATHAN RIGGS) * +* * +* INPUT: * +* * +* .A = FILL CHAR * +* .X = X POSITION * +* .Y = Y POSITION * +* * +* OUTPUT * +* * +* CHAR TO SCREEN AT X,Y * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 79+ * +* SIZE: 22 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]Y1 EQU VARTAB ; Y COORDINATE (1 BYTE) +]X1 EQU VARTAB+1 ; X COORDINATE (1 BYTE) +]F EQU VARTAB+3 ; CHARACTER TO PLOT (1 BYTE) +* +TXTPUT +* +* WITH SOME REARRANGEMENT, STORING VARS FOR LATER +* CAN BE ERASED TO SAVE CYCLES AND BYTES, BUT THIS WOULD +* BE AT THE EXPENSE OF INTERNAL LIBRARY CONSISTENCY +* + STA ]F ; {4C3B} STORE CHARACTER TO PLOT + STY ]Y1 ; {4C3B} STORE Y POSITION + STX ]X1 ; {4C3B} STORE X POSITION + LDA ]Y1 ; {4C3B} LOAD Y POS INTO .A AND {NZ} + LDY ]X1 ; {4C3B} X POS IN .Y TO CALL GBCALC {NZ} + JSR GBCALC ; {43C24B} GET SCREEN ADDRESS TO PLOT {NZCV} + LDA ]F ; {4C3B} LOAD .A WITH CHARACTER TO PLOT {NZ} + STA (GBPSH),Y ; {6C3B} PUSH CHARACTER TO SCREEN ADDR +:EXIT ; THAT WAS RETURNED BY GBCALC + RTS ; {6C1B} +* +``` + + + +--- + + + +### THE TLINE MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `TLINE` | +| Type | Macro | +| File | `MAC.SCRMEM.STDIO.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Plot any line to the text display using a fill character | +| Input | ]1 = X-position Origin
]2 = Y-position Origin
]3 = X-position destination
]4 = Y-position destination
5 = Fill Character | +| Output | A line-shape displayed on the screen,
made up of the given fill character. | +| Dependencies | `SUB.TBLINE.ASM` | +| Flags Destroyed | NZCV | +| Cycles | 305+ | +| Bytes | 198 | +| Notes | Please see the `TVLIN` and `THLIN` macros for
vertical and horizontal text line plotting that is
more efficient in both cycles and size. | +| See Also | `TBLINE` | + +*DETAILS* + +The `TLINE` macro uses Bresenham's line algorithm to draw a line of text characters directly to screen memory. Parameters are passed to the `TBLINE` subroutine via the zero page, including an X-position origin, Y-position origin, X-position destination, Y-position destination, and a designated fill character. For an explanation of Bresenham's line algorithm at work, please see the listing for the `TBLINE` subroutine. + + + +`FIGURE 2.50: TLINE Macro Source` + +```assembly +* +*``````````````````````````````* +* TLINE * +* * +* USE THE BRESSENHAM LINE * +* ALGORITHM TO DRAW A LINE * +* WITH A FILL CHARACTER. * +* * +* PARAMETERS * +* * +* ]1 = X-ORIGIN * +* ]2 = Y-ORIGIN * +* ]3 = X-DESTINATION * +* ]4 = Y-DESTINATION * +* ]5 = FILL CHARACTER * +* * +* CYCLES: 411+ * +* SIZE: 187 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +TLINE MAC + LDA ]1 ; {2C2B} LOAD X-ORIGIN + STA WPAR1 ; {3C2B} PASS VIA ZERO PAGE + LDA ]2 ; {2C2B} LOAD Y-ORIGIN + STA WPAR1+1 ; {3C2B} PASS VIA ZERO PAGE + LDA ]3 ; {2C2B} LOAD X-DESTINATION + STA WPAR2 ; {3C2B} PASS VIA ZERO PAGE + LDA ]4 ; {2C2B} LOAD Y-DESTINATION + STA WPAR2+1 ; {3C2B} PASS VIA ZERO PAGE + LDA ]5 ; {2C2B} LOAD FILL CHARACTER + STA BPAR1 ; {3C2B} PASS VIA ZERO PAGE + JSR TBLINE ; {286C167B} CALL TBLINE SUBROUTINE + <<< +* + +``` + + + +--- + + + +### THE TBLINE Subroutine + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `TBLINE` | +| Type | Subroutine | +| File | `SUB.TBLINE.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Plot a line of text at any slop to screen memory | +| Input | WPAR1 = X-position Origin
WPAR1+1 = Y-position Origin
WPAR2 = X-position Destination
WPAR2+1 = Y-position Destination
BPAR1 = Fill Character | +| Output | A line composed of the fill character is placed
in screen memory, thus displaying on the screen. | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 280+ | +| Bytes | 178 | +| Notes | See the `TCIRCLE` subroutine for an implementation
of Bresenham's Circle algorithm. | +| See Also | `TLINE` | + +--- + +*DETAILS* + +The `TBLINE` subroutine creates a line drawn by a specified fill character from an X,Y origin to an X,Y destination on the text screen using Bresenham's line algorithm, plotting directly to screen memory. Of all the STDIO subroutines, `TBLINE` is one of the more complicated algorithms (with `TCIRCLE` coming in at a close second), and therefore demands more explanation than other STDIO subroutines. By having an overall understanding of how `TBLINE` works, a user will be more than prepared to understand how later graphics routines are implemented. + +Parameters for `TBLINE` are passed via the zero page; this includes the origin and destination coordinates as well as the fill character, totaling five bytes in total. Before any other calculations can be made, it first needs to be determined whether the slope of the line is positive or negative. This is done by subtracting the Y-position destination from the Y-position origin. If the result is positive, then the Y-value of the slope is negative; otherwise, both the X and Y values of the slope are positive. Either way, the change in Y is stored in temporary variable storage as ]DY, and the positive or negative step of the slope is stored in ]SY. The step in the X-position is equally tested and stored in ]DX, with the step of 1 or -1 stored in ]SX. + +After the slope has been established, the next step is to determine the error variable, ]ERR, which is used to calculate whether the path of the line to be plotted should be increased or decreased by a pixel (this error represents the difference between Euclidian space and the raster-based plotting of most computers). This is the key to the drawing of the line, and it replaces what would normally be a cycle-hungry process of division to determine the current slope on the line segment. The current point is drawn to the screen memory using GBCALC, and the slope is then recalculated for every point (or character) on the line, altering the change in Y and change in X as needed. The process then loops, continuing until the segment being worked on has all of its points plotted. + +Put together, the series of points created constitutes the shape of a line in discrete space. In this implementation, those discrete points are character spaces representing large pixels, but the same principle applies to any raster system: the algorithm is the same for a line in the Low Resolution subroutines and the high resolution subroutines alike. + + + +`FIGURE 2.51: TBLINE Subroutine Source` + +```assembly +* +*``````````````````````````````* +* TBLINE (NATHAN RIGGS) * +* * +* INPUT: * +* * +* ]X0 = X ORIGIN * +* ]X1 = X DESTINATION * +* ]Y0 = Y ORIGIN * +* ]Y1 = Y DESTINATION * +* ]F = LINE FILL CHARACTER * +* * +* OUTPUT: * +* * +* OUTPUTS A LINE FROM COORDS * +* X0,Y0 TO X1,Y1 USING THE * +* BRESSENHAM LINE ALOGORITHM * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 280+ * +* SIZE: 178 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]X0 EQU WPAR1 ; XPOS ORIGIN PASSED VIA ZP +]X1 EQU WPAR2 ; XPOS DEST PASSED VIA ZP +]Y0 EQU WPAR1+1 ; YPOS ORIGIN PASSED VIA ZP +]Y1 EQU WPAR2+1 ; YPOS DEST PASSED VIA ZP +]F EQU BPAR1 ; FILL VALUE +* +]DX EQU ADDR1 ; CHANGE IN X +]DY EQU ADDR1+1 ; CHANGE IN Y +]SX EQU ADDR2 ; X POSITION STEP +]SY EQU ADDR2+1 ; Y POSITION STEP +]ERR EQU ADDR3 ; SLOPE ERROR +]ERRX2 EQU ADDR3+1 ; COMPARISON COPY OF ]ERR +* +TBLINE + LDA ]X1 ; {3C2B} SUBTRACT X0 FROM X1 + SEC ; {2C1B} + SBC ]X0 ; {3C2B} + BPL :ABSF1 ; {3C2B} IF POS, SKIP ABSOLUTE VALUE + SEC ; {2C1B} SUBTRACT 1 AND EOR #$FF + SBC #1 ; {3C2B} TO GET THE ABSOLUTE VALUE + EOR #$FF ; {2C2B} +:ABSF1 + STA ]DX ; {3C2B} STORE VALUE AS CHANGE IN X +* + LDA ]Y1 ; {3C2B} SUBTRACT Y0 FROM Y1 + SEC ; {2C1B} + SBC ]Y0 ; {3C2B} + BPL :ABSF2 ; {3C2B} IF POSITIVE, SKIP ABS VALUE + SEC ; {2C1B} SUBTRACT 1 AND EOR #$FF + SBC #1 ; {3C2B} TO GET THE ABSOLUTE VALUE + EOR #$FF ; {2C2B} +:ABSF2 + STA ]DY ; {3C2B} STORE VALUE AS CHANGE IN Y +* + LDA ]DX ; {3C2B} ]ERR = DX - DY + SEC ; {2C1B} + SBC ]DY ; {3C2B} + STA ]ERR ; {3C2B} +* + LDX #$FF ; {3C2B} .X = -1 + LDA ]X0 ; {3C2B} IF X0 >= X1 + CMP ]X1 ; {3C2B} + BCS :NONEG ; {3C2B} THEN SKIP CHANGE IN .X + LDX #$01 ; {3C2B} ELSE, CHANGE .X TO +1 +:NONEG STX ]SX ; {3C2B} STORE EITHER -1 OR +1 IN SX +* + LDX #$FF ; {3C2B} .X = -1 + LDA ]Y0 ; {3C2B} IF Y0 >= Y1 + CMP ]Y1 ; {3C2B} + BCS :NONEG2 ; {3C2B} THEN SKIP CHANGE IN .X + LDX #$01 ; {3C2B} ELSE CHANGE .X TO +1 +:NONEG2 STX ]SY ; {3C2B} STORE EITHER -1 OR +1 IN SY +* +** MAIN LOOP +* +:LOOP + LDA ]Y0 ; {3C2B} .A = Y POSITION {NZ} + LDY ]X0 ; {3C2B} .Y = X POSITION {NZ} + JSR GBCALC ; {43C24B} FIND SCREEN MEM LOCATION {NZCV} + LDA ]F ; {3C2B} LOAD FILL INTO .A {NZ} + STA (GBPSH),Y ; {6C2B} PUSH TO SCREEN MEMORY +* + LDA ]X0 ; {3C2B} IF X0 != X1, KEEP LOOPING + CMP ]X1 ; {3C2B} + BNE :CONT ; {3C2B} + LDA ]Y0 ; {3C2B} IF Y0 != Y1, KEEP LOOPING + CMP ]Y1 ; {3C2B} + BNE :CONT ; {3C2B} + JMP TBLEXIT ; {3C3B} ELSE, EXIT LOOP +:CONT +* + LDA ]ERR ; {3C2B} ]ERR = ]ERR * 2 + ASL ; {2C1B} + STA ]ERRX2 ; {3C2B} +* + LDA ]DY ; {3C2B} NEGATE ]DY + EOR #$FF ; {3C2B} + CLC ; {2C1B} + ADC #1 ; {3C2B} + SEC ; {2C1B} USE SBC FOR SIGNED COMPARE + SBC ]ERRX2 ; {3C2B} + BMI :NFSETX ; {3C2B} IF N FLAG SET, GO CHECK V FLAG + BVC :GEX ; {3C2B} IF V = 0 & N = 0, VAL >= .A REG +:LTX ; N = 0 AND V = 1, SO LESS THAN + LDA ]ERR ; {3C2B} ]ERR = ]ERR - ]DY + SEC ; {2C1B} + SBC ]DY ; {3C2B} + STA ]ERR ; {3C2B} + LDA ]X0 ; {3C2B} X0 = X0 + SX + CLC ; {2C1B} + ADC ]SX ; {3C2B} + STA ]X0 ; {3C2B} + JMP :GEX ; {3C3B} +:NFSETX BVC :LTX ; {3C2B} IF N = 1 & V = 0, VAL < .A REG +:GEX ; N = 1 & V = 1, SO VAL >= .A REG +* + LDA ]ERRX2 ; {3C2B} IF ER * 2 < DX, GOTO :LTY + SEC ; {2C1B} + SBC ]DX ; {3C2B} + BMI :SKIPY ; {3C2B} IF N FLAG = 1, GO CHECK V FLAG + BVC :GEY ; {3C2B} N = 0 & V = 0, SO VAL >= .A REG +:LTY LDA ]ERR ; {3C2B} N = 0 AND V = 1, SO LESS THAN + CLC ; {2C1B} + ADC ]DX ; {3C2B} ]ERR = ]ERR + ]DX + STA ]ERR ; {3C2B} + LDA ]Y0 ; {3C2B} ]Y0 = ]Y0 + ]SY + CLC ; {2C1B} + ADC ]SY ; {3C2B} + STA ]Y0 ; {3C2B} + JMP :GEY ; {3C3B} +:SKIPY BVC :LTY ; {3C2B} IF N = 1 & V = 0, VAL < .A REG +:GEY ; {3C2B} N = 1 & V = 1, SO VAL >= .A REG +* + JMP :LOOP ; {3C3B} +TBLEXIT + RTS ; {6C1B} + + +``` + + + +--- + + + +### THE TCIRC MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `TCIRC` | +| Type | Macro | +| File | `MAC.SCRMEM.STDIO.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Plot a circle to the text screen | +| Input | ]1 = Center X-position
]2 = Center Y-position
]3 = Radius
]4 = Fill Character | +| Output | A circle is plotted directly to screen memory
composed of the fill character specified. | +| Dependencies | `SUB.TCIRCLE.ASM` | +| Flags Destroyed | NZCV | +| Cycles | 627+ | +| Bytes | 290 | +| Notes | none | +| See Also | `TCIRCLE` | + +*DETAILS* + +The `TCIRC` macro draws a circle of text on the screen with the given center X, Y coordinates and a given radius. This is directly plotted to screen memory, bypassing the slower execution of COUT. These parameters are passed to the `TCIRCLE` subroutine via the zero page. It should be noted that this implementation uses Bresenham's Circle Algorithm, which is something of an extension of Bresenham's Line Algorithm, which can be found in the `TBLINE` subroutine. To gain a broad understanding of how the algorithm works, it is suggested that the reader consult the `TCIRCLE` subroutine entry. + + + +`FIGURE 2.52: TCIRC Macro Source` + +```assembly +* +*``````````````````````````````* +* TCIRC * +* * +* USE THE BRESSENHAM CIRCLE * +* ALGORITHM TO DRAW A CIRCLE * +* WITH A FILL CHARACTER. * +* * +* PARAMETERS * +* * +* ]1 = CENTER X-LOCATION * +* ]2 = CENTER Y-LOCATION * +* ]3 = RADIUS * +* ]4 = FILL CHARACTER * +* * +* CYCLES: 627+ * +* SIZE: 290 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +TCIRC MAC + LDA ]1 ; {2C2B} LOAD CENTER X-COORD + STA WPAR1 ; {3C2B} PASS VIA ZERO PAGE + LDA ]2 ; {2C2B} LOAD CENTER Y-COORD + STA WPAR2 ; {3C2B} PASS VIA ZERO PAGE + LDA ]3 ; {2C2B} LOAD RADIUS + STA BPAR1 ; {3C2B} PASS VIA ZERO PAGE + LDA ]4 ; {2C2B} LOAD FILL CHARACTER + STA BPAR2 ; {3C2B} PASS VIA ZERO PAGE + JSR TCIRCLE ; {607C374B} CALL TCIRCLE SUBROUTINE + <<< +* + +``` + + + +--- + + + +### THE TCIRCLE Subroutine + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `TCIRCLE` | +| Type | Subroutine | +| File | `SUB.TCIRCLE.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Plot a circle of text at any slop to screen memory | +| Input | WPAR1 = Center X-position
WPAR2 = Center Y-position
BPAR1 = Radius
BPAR2 = Fill Character | +| Output | A circle composed of the fill character is placed
in screen memory, thus displaying on the screen. | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 601+ | +| Bytes | 371 | +| Notes | See the `TBLINE` subroutine for an implementation
of Bresenham's Line algorithm. | +| See Also | `TCIRC` | + +--- + +*DETAILS* + +The `TCIRCLE` subroutine uses Bresenham's Circle Algorithm to draw a circle made up of a given text character directly to screen memory. This algorithm is akin to an extension of Bresenham's line algorithm; the difference is that the slope of an arc is being calculated, and only a single octant is actually calculated: thanks to the uniform nature of a circle, a single octant can be created and then transformed and translated to make the whole circle, saving a number of cycles in the process. See the `TBLINE` subroutine for a more comprehensive overview of how the algorithm works. + +Note that because Bresenham's circle algorithm is inexact (thus the use of an error value), not all spaces will be filled if one were to superimpose circles of different radii. It is impractical, as such, to use the algorithm for a filled circle shape. + + + +`FIGURE 2.53: TCIRCLE Subroutine Source` + +```assembly +* +*``````````````````````````````* +* TCIRCLE (NATHAN RIGGS) * +* * +* INPUT: * +* * +* WPAR1 = X CENTER POS * +* WPAR2 = Y CENTER POS * +* BPAR1 = RADIUS * +* BPAR2 = FILL CHARACTER * +* * +* OUTPUT: * +* * +* USES BRESENHAM'S CIRCLE * +* ALGORITHM TO DRAW A CIRCLE * +* TO THE 40-COLUMN TEXTMODE * +* SCREEN. * +* * +* DESTROY: NZCIDV * +* * +* * +* CYCLES: 601+ * +* SIZE: 371 BYTES * +* * +* SUBSTANTIAL DEBT IS OWED TO * +* MARC GOLOMBECK AND HIS GREAT * +* IMPLEMENTATION OF THE * +* BRESENHAM CIRCLE ALGORITHM * +* IN 6502 AND APPLESOFT, WHICH * +* IS BASED ON THE GERMAN LANG * +* VERSION OF WIKIPEDIA'S ENTRY * +* ON THE ALGORITHM THAT HAS A * +* BASIC PSEUDOCODE EXAMPLE. * +* THAT EXAMPLE, WITH CHANGED * +* VARIABLE NAMES, IS INCLUDED * +* BELOW. * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]XC EQU WPAR1 ; X CENTER POSITION +]YC EQU WPAR2 ; Y CENTER POSITION +]R EQU BPAR1 ; RADIUS +]F EQU BPAR2 ; FILL CHAR +* +]Y EQU VARTAB ; CENTER YPOS +]X EQU VARTAB+1 ; CENTER XPOS +]DY EQU VARTAB+2 ; CHANGE IN Y +]DX EQU VARTAB+4 ; CHANGE IN X +]ERR EQU VARTAB+6 ; ERROR VALUE +]DIAM EQU VARTAB+8 ; DIAMETER +]XT EQU VARTAB+10 ; INVERTED X VALUE +]YT EQU VARTAB+12 ; INVERTED Y VALUE +* +******************************** +* * +* BASIC PSEUDOCODE * +* * +******************************** +* +* X = R +* Y = 0 +* ERROR = R +* SETPIXEL XC + X, YC + Y +* WHILE Y < X +* DY = Y * 2 + 1 +* Y = Y + 1 +* ERROR = ERROR - DY +* IF ERROR < 0 THEN +* DX = 1 - X * 2 +* X = X - 1 +* ERROR = ERROR - DX +* END IF +* SETPIXEL XC + X, YC + Y +* SETPIXEL XC - X, YC + Y +* SETPIXEL XC - X, YC - Y +* SETPIXEL XC + X, YC - Y +* SETPIXEL XC + Y, YC + X +* SETPIXEL XC - Y, YC + X +* SETPIXEL XC - Y, YC - X +* SETPIXEL XC + Y, YC - X +* WEND +* +******************************** +* +TCIRCLE +* +** FIRST, INITIALIZE VARIABLES +* + LDA #0 ; {2C2B} CLEAR YPOS {NZ} + STA ]Y ; {4C3B} + LDA ]R ; {4C3B} LOAD RADIUS {NZ} + STA ]X ; {4C3B} X = RADIUS + STA ]ERR ; {4C3B} ERROR = RADIUS + ASL ; {2C1B} R * 2 {NZC} + STA ]DIAM ; {4C3B} STORE DIAMETER +* +** NOW DRAW FIRST PART OF CIRCLE +* +** CALCULATE -X AND -Y +* + LDA ]X ; {4C3B} GET XPOS {NZ} + NEGA ; {6C5B} {NZCV} + STA ]XT ; {4C3B} STORE NEGATED IN XT + LDA ]Y ; {4C3B} GET YPOS {NZ} + NEGA ; {6C5B} {NZCV} + STA ]YT ; {4C3B} STORE NEGATED IN YT +* +** PLOT XC+X,YC +* + LDA ]XC ; {4C3B} LOAD CIRCLE CENTER XPOS {NZ} + CLC ; {2C1B} CLEAR CARRY {C=0} + ADC ]X ; {4C3B} ADD CURRENT XPOS {NZCV} + TAY ; {2C1B} TRANSER TO .Y {NZ} + TAX ; {2C1B} AND .X {NZ} + LDA ]YC ; {4C3B} LOAD CIRCLE CENTER YPOS {NZ} + JSR GBCALC ; {43C3B} GET X,Y SCREEN MEMORY POS + LDA ]F ; {4C3B} LOAD FILL CHAR {NZ} + STA (GBPSH),Y ; {5C3B} STORE IN SCREEN MEMORY +* +** PLOT XC-X,YC +* + LDA ]XC ; {4C3B} LOAD CIRCLE CENTER XPOS {NZ} + CLC ; {2C1B} CLEAR CARRY {C=0} + ADC ]XT ; {4C3B} ADD NEGATED CURRENT XPOS {NZCV} + TAX ; {2C1B} TRANSFER TO .X {NZ} + TAY ; {2C1B} AND .Y {NZ} + LDA ]YC ; {4C3B} LOAD CIRCLE CENTER YPOS {NZ} + JSR GBCALC ; {43C3B} GET X,Y SCREEN MEMORY POS + LDA ]F ; {4C3B} LOAD FILL CHAR {NZ} + STA (GBPSH),Y ; {5C3B} STORE IN SCREEN MEMORY +* +** PLOT XC,YC+X +* + LDA ]XC ; {4C3B} LOAD CIRCLE CENTER XPOS {NZ} + TAY ; {2C1B} TRANSFER TO .Y {NZ} + TAX ; {2C1B} AND .X {NZ} + LDA ]YC ; {4C3B} LOAD CIRCLE CENTER YPOS {NZ} + CLC ; {2C1B} CLEAR CARRY {C=0} + ADC ]X ; {4C3B} ADD CURRENT XPOS {NZCV} + JSR GBCALC ; {43C3B} GET X,Y SCREEN MEMORY POS + LDA ]F ; {4C3B} LOAD FILL CHAR {NZ} + STA (GBPSH),Y ; {5C3B} STORE IN SCREEN MEMORY +* +** PLOT XC,YC-X +* + LDA ]XC ; {4C3B} LOAD CIRCLE CENTER XPOS {NZ} + TAY ; {2C1B} TRANSFER TO .Y {NZ} + TAX ; {2C1B} AND .X {NZ} + LDA ]YC ; {4C3B} LOAD CIRCLE CENTER YPOS {NZ} + CLC ; {2C1B} CLEAR CARRY {C=0} + ADC ]XT ; {4C3B} ADD NEGATED CURRENT XPOS {NZCV} + JSR GBCALC ; {43C3B} GET X,Y SCREEN MEMORY POS + LDA ]F ; {4C3B} LOAD FILL CHAR {NZ} + STA (GBPSH),Y ; {5C3B} STORE IN SCREEN MEMORY +* +** NOW LOOP UNTIL CIRCLE IS FINISHED +* +:LOOP +* +** CHECK IF CIRCLE FINISHED +* + LDA ]Y ; {4C3B} IF Y > X {NZ} + CMP ]X ; {4C3B} {NZC} + BCC :LPCONT ; {3C2B} CONTINUE LOOPING + JMP :EXIT ; {3C3B} OTHERWISE, CIRCLE DONE +:LPCONT +:STEPY ; STEP THE Y POSITION + LDA ]Y ; {4C3B} LOAD YPOS {NZ} + ASL ; {2C1B} MULTIPLY BY 2 {NZC} +*CLC + ADC #1 ; {3C2B} ADD +1 {NZCV} + STA ]DY ; {4C3B} STORE CHANGE OF Y + INC ]Y ; {6C3B} INCREASE YPOS {NZ} + LDA ]DY ; {3C2B} NEGATE {NZ} + NEGA ; {6C5B} {NZCV} + ADC ]ERR ; {4C3B} ADD ERR {NZCV} + STA ]ERR ; {4C3B} ERR = ERR - DY + BPL :PLOT ; {3C2B} IF ERR IS +, SKIP TO PLOT +:STEPX + LDA ]X ; {3C2B} LOAD XPOS {NZ} + ASL ; {2C1B} MULTIPLY BY 2 {NZC} + NEGA ; {6C5B} NEGATE {NZCV} + ADC #1 ; {3C2B} (X*2) + 1 {NZCV} + STA ]DX ; {4C3B} STORE CHANGE OF X + DEC ]X ; {6C3B} DECREASE YPOS {NZ} + LDA ]DX ; {3C2B} NEGATE {NZ} + NEGA ; {6C5B} {NZCV} + ADC ]ERR ; {4C3B} ADD ERR {NZCV} + STA ]ERR ; {4C3B} ERR = ERR - DX +* +:PLOT +* +** NOW CALCULATE -X AND -Y +* + LDA ]X ; {3C2B} {NZ} + NEGA ; {6C5B} NEGATE {NZCV} + STA ]XT ; {4C3B} + LDA ]Y ; {3C2B} {NZ} + NEGA ; {6C5B} NEGATE {NZCV} + STA ]YT ; {4C3B} +* +** NOW PLOT CIRCLE OCTANTS +* +** PLOT XC+X,YC+Y +* + LDA ]XC ; {3C2B} LOAD CIRCLE CENTER XPOS {NZ} + CLC ; {2C1B} CLEAR CARRY {C=0} + ADC ]X ; {4C3B} ADD CURRENT XPOS {NZCV} + TAY ; {2C1B} TRANSFER TO .Y {NZ} + TAX ; {2C1B} AND .X {NZ} + LDA ]YC ; {3C2B} LOAD CIRCLE CENTER YPOS {NZ} + CLC ; {2C1B} CLEAR CARRY {C=0} + ADC ]Y ; {4C3B} ADD CURRENT YPOS {NZCV} + JSR GBCALC ; {43C3B} GET X,Y SCREEN ADDRESS {NZCV} + LDA ]F ; {3C2B} LOAD FILL CHAR {NZ} + STA (GBPSH),Y ; {6C2B} STORE AT SCREEN ADDRESS +* +** PLOT XC-X,YC+Y +* + LDA ]XC ; {3C2B} LOAD CIRCLE CENTER XPOS {NZ} + CLC ; {2C1B} CLEAR CARRY {C=0} + ADC ]XT ; {4C3B} ADD NEGATED CURRENT XPOS {NZCV} + TAY ; {2C1B} TRANSFER TO .Y {NZ} + TAX ; {2C1B} AND TO .X {NZ} + LDA ]YC ; {3C2B} LOAD CIRCLE CENTER YPOS {NZ} + CLC ; {2C1B} CLEAR CARRY {C=0} + ADC ]Y ; {4C3B} ADD CURRENT YPOS {NZCV} + JSR GBCALC ; {43C3B} GET X,Y SCREEN ADDRESS {NZCV} + LDA ]F ; {3C2B} LOAD FILL CHAR {NZ} + STA (GBPSH),Y ; {6C2B} STORE AT SCREEN ADDRESS +* +** PLOT XC-X,YC-Y +* + LDA ]XC ; {3C2B} LOAD CIRCLE CENTER XPOS {NZ} + CLC ; {2C1B} CLEAR CARRY {C=0} + ADC ]XT ; {4C3B} ADD NEGATED CURRENT XPOS {NZCV} + TAY ; {2C1B} TRANSFER TO .Y {NZ} + TAX ; {2C1B} AND .X {NZ} + LDA ]YC ; {3C2B} LOAD CIRCLE CENTER YPOS {NZ} + CLC ; {2C1B} CLEAR CARRY {C=0} + ADC ]YT ; {4C3B} ADD NEGATED CURRENT YPOS {NZCV} + JSR GBCALC ; {43C3B} GET X,Y SCREEN ADDRESS {NZCV} + LDA ]F ; {3C2B} LOAD FILL CHARACTER {NZ} + STA (GBPSH),Y ; {6C2B} STORE AT SCREEN ADDRESS +* +** PLOT XC+X,YC-Y +* + LDA ]XC ; {3C2B} LOAD CIRCLE CENTER XPOS {NZ} + CLC ; {2C1B} CLEAR CARRY {C=0} + ADC ]X ; {4C3B} ADD CURRENT XPOS {NZCV} + TAY ; {2C1B} TRANSFER TO .Y {NZ} + TAX ; {2C1B} AND .X {NZ} + LDA ]YC ; {3C2B} LOAD CIRCLE CENTER YPOS {NZ} + CLC ; {2C1B} CLEAR CARRY {C=0} + ADC ]YT ; {4C3B} ADD NEGATE CURRENT YPOS {NZCV} + JSR GBCALC ; {43C3B} GET X,Y SCREEN ADDRESS {NZCV} + LDA ]F ; {3C2B} LOAD FILL CHAR {NZ} + STA (GBPSH),Y ; {6C2B} STORE AT SCREEN ADDRESS +* +** PLOT XC+Y,YC+X +* + LDA ]XC ; {3C2B} LOAD CIRCLE CENTER XPOS {NZ} + CLC ; {2C1B} CLEAR CARRY {C=0} + ADC ]Y ; {4C3B} ADD CURRENT YPOS {NZCV} + TAX ; {2C1B} TRANSFER TO .X {NZ} + TAY ; {2C1B} AND .Y {NZ} + LDA ]YC ; {3C2B} LOAD CIRCLE CENTER YPOS {NZ} + CLC ; {2C1B} CLEAR CARRY {C=0} + ADC ]X ; {4C3B} ADD CURRENT XPOS {NZCV} + JSR GBCALC ; {43C3B} GET X,Y SCREEN ADDRESS {NZCV} + LDA ]F ; {3C2B} LOAD FILL CHAR {NZ} + STA (GBPSH),Y ; {6C2B} STORE AT SCREEN ADDRESS +* +** PLOT XC-Y,YC+X +* + LDA ]XC ; {3C2B} LOAD CIRCLE CENTER XPOS {NZ} + CLC ; {2C1B} CLEAR CARRY {C=0} + ADC ]YT ; {4C3B} ADD NEGATED CURRENT YPOS {NZCV} + TAX ; {2C1B} TRANSFER TO .X {NZ} + TAY ; {2C1B} AND .Y {NZ} + LDA ]YC ; {3C2B} LOAD CIRCLE CENTER YPOS {NZ} + CLC ; {2C1B} CLEAR CARRY {C=0} + ADC ]X ; {4C3B} ADD CURRENT XPOS {NZCV} + JSR GBCALC ; {43C3B} GET X,Y SCREEN ADDRESS {NZCV} + LDA ]F ; {3C2B} LOAD FILL CHAR {NZ} + STA (GBPSH),Y ; {6C2B} STORE AT SCREEN ADDRESS +* +** PLOT XC-Y,YC-X +* + LDA ]XC ; {3C2B} LOAD CIRCLE CENTER XPOS {NZ} + CLC ; {2C1B} CLEAR CARRY {C=0} + ADC ]YT ; {4C3B} ADD NEGATED CURRENT YPOS {NZCV} + TAX ; {2C1B} TRANSFER TO .X {NZ} + TAY ; {2C1B} AND .Y {NZ} + LDA ]YC ; {3C2B} LOAD CIRCLE CENTER YPOS {NZ} + CLC ; {2C1B} CLEAR CARRY {C=0} + ADC ]XT ; {4C3B} ADD NEGATED CURRENT XPOS {NZCV} + JSR GBCALC ; {43C3B} GET X,Y SCREEN ADDRESS {NZCV} + LDA ]F ; {3C2B} LOAD FILL CHAR {NZ} + STA (GBPSH),Y ; {6C2B} STORE AT SCREEN ADDRESS +* +** PLOT XC+Y,YC-X +* + LDA ]XC ; {3C2B} LOAD CIRCLE CENTER XPOS {NZ} + CLC ; {2C1B} CLEAR CARRY {C=0} + ADC ]Y ; {4C3B} ADD CURRENT YPOS {NZCV} + TAY ; {2C1B} TRANSFER TO .Y {NZ} + TAX ; {2C1B} AND .X {NZ} + LDA ]YC ; {3C2B} LOAD CIRCLE CENTER YPOS {NZ} + CLC ; {2C1B} {C=0} + ADC ]XT ; {4C3B} ADD NEGATED CURRENT XPOS {NZCV} + JSR GBCALC ; {43C3B} GET X,Y SCREEN ADDRESS {NZCV} + LDA ]F ; {3C2B} LOAD FILL CHAR {NZ} + STA (GBPSH),Y ; {6C2B} STORE AT SCREEN ADDRESS + JMP :LOOP ; {3C3B} LOOP UNTIL FINISHED +:EXIT + RTS ; {6C1B} + +``` + + + +--- + + + +### THE TREC MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `TREC` | +| Type | Macro | +| File | `MAC.SCRMEM.STDIO.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Plot an unfilled rectangle to the text screen | +| Input | ]1 = X Origin
]2 = Y Origin
]3 = X Destination
]4 = Y Destination
]5 = Fill Value | +| Output | An unfilled rectangle made of the specified
character is placed in screen memory. | +| Dependencies | `SUB.TRECT.ASM` | +| Flags Destroyed | NZCV | +| Cycles | 362+ | +| Bytes | 115 | +| Notes | none | +| See Also | `TRECT` `TRECTF` `TRECF` | + +*DETAILS* + +The `TREC` macro plots an unfilled rectangle directly to screen memory at the given coordinates, composed of the passed fill character. The macro itself is fairly standard, and only functions to pass parameters to the `TRECT` subroutine via the proper addresses on the zero page. + + + +`FIGURE 2.54: TREC Macro Source` + +```assembly +* +*``````````````````````````````* +* TREC * +* * +* CREATE A RECTANGULAR BORDER * +* ON THE SCREEN. NOTE THAT THE * +* INSIDE IS NOT FILLED WITH * +* ANY CHARACTER, INCLUDING * +* BLANK SPACES. * +* * +* PARAMETERS * +* * +* ]1 = X ORIGIN * +* ]2 = Y ORIGIN * +* ]3 = X DESTINATION * +* ]4 = Y DESTINATION * +* ]5 = BORDER CHARACTER * +* * +* CYCLES: 362+ * +* SIZE: 115 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +TREC MAC + LDA ]1 ; {2C2B} LOAD X-ORIGIN + STA WPAR1 ; {3C2B} PASS VIA ZERO PAGE + LDA ]2 ; {2C2B} LOAD Y-ORIGIN + STA WPAR1+1 ; {3C2B} PASS VIA ZERO PAGE + LDA ]3 ; {2C2B} LOAD X-DESTINATION + STA WPAR2 ; {3C2B} PASS VIA ZERO PAGE + LDA ]4 ; {2C2B} LOAD Y-DESTINATION + STA WPAR2+1 ; {2C2B} PASS VIA ZERO PAGE + LDA ]5 ; {2C2B} LOAD FILL CHARACTER + STA BPAR1 ; {3C2B} PASS VIA ZERO PAGE + JSR TRECT ; {338C95B} CALL TRECT SUBROUTINE + <<< +* + +``` + + + +--- + + + +### THE TRECT Subroutine + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `TRECT` | +| Type | Subroutine | +| File | `SUB.TRECT.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Plot an open rectangle to screen memory | +| Input | WPAR1 = X-position Origin
WPAR1+1 = X-position Destination
WPAR2 = Y-position Origin
WPAR2+1 = Y-position Destination
BPAR1 = Fill Character | +| Output | An unfilled rectangle is painted to screen memory | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 332+ | +| Bytes | 92 | +| Notes | none | +| See Also | `TREC` `TRECTF` `TRECF` | + +--- + +*DETAILS* + +The `TRECT` subroutine draws an unfilled rectangle on the screen (via direct memory access) made up of a given text character. It accepts an X,Y origin and an X,Y destination along with the fill character as parameters passed via the zero page. + +For the most part, `TRECT` combines the algorithms found in `TVLINE` and `THLINE` to create the horizontal and vertical sides of the rectangle. The memory address for each given point is determined by GBCALC (GBASCALC), and thus does not use or interfere with any COUT activities or machine states. + + + +`FIGURE 2.55: TRECT Subroutine Source` + +```assembly +* +*``````````````````````````````* +* TRECT (NATHAN RIGGS) * +* * +* INPUT: * +* * +* WPAR1 = X ORIGIN * +* WPAR1+1 = X DESTINATION * +* WPAR2 = Y ORIGIN * +* WPAR2+1 = Y DESTINATION * +* BPAR1 = FILL CHARACTER * +* * +* OUTPUT: * +* * +* UNFILLED BOX BORDER TO THE * +* SCREEN MEMORY. * +* * +* DESTROYS: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 332+ * +* SIZE: 92 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]X1 EQU WPAR1 ; XPOS ORIGIN +]X2 EQU WPAR2 ; XPOS DESTINATION +]Y1 EQU WPAR1+1 ; YPOS ORIGIN +]Y2 EQU WPAR2+1 ; YPOS DESTINATION +]F EQU BPAR1 ; FILL CHARACTER (BORDER) +* +]YB1 EQU VARTAB +* +TRECT +* + LDA ]Y1 ; {3C2B} LOAD 1ST ROW {NZ} + LDY ]X1 ; {3C2B} LOAD XPOS START {NZ} +:LP1 + JSR GBCALC ; {49C3B} GOSUB GBASCALC {NZCV} + LDA ]F ; {3C2B} LOAD FILL CHAR {NZ} + STA (GBPSH),Y ; {6C2B} PUSH ]F TO SCREEN MEM + LDA ]Y1 ; {3C2B} LOAD YPOS {NZ} + INY ; {2C1B} INCREASE XPOS {NZ} + CPY ]X2 ; {3C2B} IF < X DEST THEN END {NZC} + BNE :LP1 ; {3C2B} REPEAT UNTIL DONE +* +** NEXT HORIZONTAL LINE ROUTINE +* + LDA ]Y2 ; {3C2B} LOAD 2ND ROW {NZ} + LDY ]X1 ; {3C2B} LOAD XPOS START {NZ} +:LP2 + JSR GBCALC ; {49C3B} GOSUB GBASCALC {NZCV} + LDA ]F ; {2C2B} LOAD FILL CHAR {NZ} + STA (GBPSH),Y ; {6C2B} PUSH ]F TO SCREEN MEM + LDA ]Y2 ; {3C2B} LOAD YPOS + INY ; {2C1B} INCREASE XPOS {NZ} + CPY ]X2 ; {3C2B} IF < X DEST THEN END {NZC} + BNE :LP2 ; {3C2B} REPEAT UNTIL DONE +* +** VERTICAL LINES ROUNTINE +* + LDA ]Y1 ; {4C3B} LOAD Y ORIGIN + STA ]YB1 ; {3C2B} COPY OF Y ORIGIN + LDY ]X1 ; {4C3B} LOAD X POSITION {NZ} +:LP3 + JSR GBCALC ; {49C3B} GET POS SCREEN ADDR {NZCV} + LDA ]F ; {4C3B} LOAD FILL CHAR + STA (GBPSH),Y ; {6C2B} PUSH ]F TO SCREEN MEM + INC ]YB1 ; {6C3B} INCREASE Y POSITION {NZ} + LDA ]YB1 ; {4C3B} RELOAD Y POSITION {NZ} + CMP ]Y2 ; {4C3B} IF Y1 < Y2 {NZC} + BNE :LP3 ; {3C2B} THEN LOOP; ELSE END +* + INC ]Y2 ; {6C3B} ADD 1 TO Y2 FOR RECT {NZ} + LDA ]Y1 ; {4C3B} LOAD Y ORIGIN + LDY ]X2 ; {4C3B} LOAD X POSITION {NZ} +:LP4 + JSR GBCALC ; {49C3B} GET SCREEN ADDRESS {NZCV} + LDA ]F ; {4C3B} LOAD FILL CHAR + STA (GBPSH),Y ; {6C2B} PUSH ]F TO SCREEN MEM + INC ]Y1 ; {6C3B} INCREASE Y POSITION {NZ} + LDA ]Y1 ; {4C3B} RELOAD Y POSITION {NZ} + CMP ]Y2 ; {4C3B} IF Y1 < Y2 {NZC} + BNE :LP4 ; {3C2B} THEN LOOP; ELSE EXIT LOOP + RTS ; {6C1B} + +``` + + + +--- + + + +### THE SPUT MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `SPUT` | +| Type | Macro | +| File | `MAC.SCRMEM.STDIO.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Plot a string to the text screen | +| Input | ]1 = X Coordinate
]2 = Y Coordinate
]3 = String Address | +| Output | A string of characters at the give X,Y
position. | +| Dependencies | `SUB.STRPUT.ASM` | +| Flags Destroyed | NZCV | +| Cycles | 124+ | +| Bytes | 50 | +| Notes | none | +| See Also | `STRPUT` `CPUT` `TXTPUT` `SPRN` | + +*DETAILS* + +The `SPUT` macro plots a regular string of characters to screen memory, passing the necessary parameters via the zero page. Like other macros in this file, `SPUT` leaves COUT untouched; a string can be plotted at any place on the screen without, for instance, changing the COUT cursor values. Additionally, emphasis should be put on the fact that this subroutine works with **regular** strings, not null-terminated strings. A regular string has a preceding byte that holds the length of the string. + +As with any time a memory address is passed to a subroutine, the parameter is parsed based on whether a literal value is given or a memory address alone. If the value is literal (begins with a # sign), then the address is to be read as pointing directly to the string to be printed; if the value points to a specific address, on the other hand, then the value passed is considered an indirect memory address: the address given holds another address at which the string resides. + + + +`FIGURE 2.56: SPUT Macro Source` + +```assembly +* +*``````````````````````````````* +* SPUT * +* * +* THIS MACRO PLOTS A CHARACTER * +* TO THE SCREEN VIA DIRECT * +* ACCESS TO SCREEN MEMORY. * +* * +* PARAMETERS * +* * +* ]1 = X COORDINATE * +* ]2 = Y COORDINATE * +* ]3 = ADDRESS OF STRING * +* TO PRINT ON SCREEN * +* * +* CYCLES: 124+ * +* SIZE: 50 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +SPUT MAC + LDA ]1 ; {2C2B} GET X COORDINATE + STA WPAR1 ; {3C2B} PASS VIA ZERO PAGE + LDA ]2 ; {2C2B} GET Y COORDINATE + STA WPAR1+1 ; {3C2B} PASS VIA ZERO PAGE + _MLIT ]3;WPAR2 ; {16C12B} PARSE STRING ADDRESS + JSR STRPUT ; {98C30B} CALL STRPUT SUBROUTINE + <<< +* + +``` + + + +--- + + + +### THE STRPUT Subroutine + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `STRPUT` | +| Type | Subroutine | +| File | `SUB.STRPUT.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Print a regular string to screen memory | +| Input | WPAR1 = X Coordinate
WPAR1+1 =Y Coordinate
WPAR2 = String Address | +| Output | A given string is printed at the given coordinates | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 98+ | +| Bytes | 30 | +| Notes | none | +| See Also | `SPUT` `CPUT` `TXTPUT` `SPRN` | + +--- + +*DETAILS* + +The `STRPUT` subroutine plots a string directly to screen memory at a given starting coordinate, bypassing COUT in the process. This works much like the `CPUT` routine, except that each character in the string is cycled through and plotted instead of storing a single character value. Like most other subroutines on the STDIO disk, the appropriate address is determined using the GBCALC (GBASCALC) subroutine that is already provided by the Monitor. + + + +`FIGURE 2.57: STRPUT Subroutine Source` + +```assembly +* +*``````````````````````````````* +* STRPUT (NATHAN RIGGS) * +* * +* THIS SUBROUTINE PLOTS A * +* STRING OF CHARACTERS * +* DIRECTLY TO SCREEN MEMORY, * +* AVOIDING THE SLOW-DOWN OF * +* USING COUT ROUTINES. * +* * +* INPUT: * +* * +* WPAR1 = X COORDINATE * +* WPAR1+1 = Y COORDINATE * +* WPAR2 = STRING ADDRESS * +* * +* DESTROYS: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 98+ * +* SIZE: 30 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]X1 EQU WPAR1 ; X-COORDINATE TO PLOT +]Y1 EQU WPAR1+1 ; Y-COORDINATE TO PLOT +]ADDR EQU WPAR2 ; ADDRESS OF STRING TO PRINT +* +]LEN EQU VARTAB ; LENGTH OF STRING +]CNT EQU VARTAB+2 ; INDEX COUNTER +]CHAR EQU VARTAB+4 ; CHARACTER TO PRINT +* +STRPUT +* + LDY #0 ; {2C2B} CLEAR .Y COUNTER + STY ]CNT ; {3C2B} CLEAR ]CNT VARIABLE + LDA (]ADDR),Y ; {6C2B} GET STRING LENGTH + STA ]LEN ; {3C2B} STORE LENGTH IN ]LEN +:LP + INC ]CNT ; {6C3B} INCREMENT ]CNT BY 1 + INC ]X1 ; {6C3B} INCREMENT X-COORD BY 1 + LDY ]CNT ; {2C2B} PASS ]CNT TO .Y + LDA (]ADDR),Y ; {6C2B} LOAD CHARACTER FROM STRING + STA ]CHAR ; {3C2B} WITH Y OFFSET; HOLD IN ]CHAR + LDA ]Y1 ; {2C2B} LOAD Y COORDINATE + LDY ]X1 ; {2C2B} RELOAD X COORDINATE + JSR GBCALC ; {43C3B} CALCULATE SCREEN MEMORY ADDRESS + LDA ]CHAR ; {2C2B} LOAD FILL CHARACTER INTO .A + STA (GBPSH),Y ; {6C2B} STORE CHARACTER IN SCREEN MEMORY + LDY ]CNT ; {2C2B} RELOAD COUNTER + CPY ]LEN ; {2C2B} IF COUNTER != STRING LENGTH + BNE :LP ; {2C2B} THEN KEEP LOOPING + RTS ; {6C1B} OTHERWISE, RETURN FROM CALL + +``` + + + +--- + + + +### THE TCLR MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ----------------------------------------------------- | +| Name | `TCLR` | +| Type | Macro | +| File | `MAC.SCRMEM.STDIO.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | fill text screen with a given character | +| Input | ]1 = Fill Character | +| Output | The entire screen fills with the given fill character | +| Dependencies | `SUB.TXTCLR.ASM` | +| Flags Destroyed | NZCV | +| Cycles | 50+ | +| Bytes | 42 | +| Notes | none | +| See Also | `TXTCLR` | + +*DETAILS* + +The `TCLR` macro simply fills the entirety of screen memory with the same value, providing a whole-screen clearing function that does not interact with COUT and additionally has the ability to paint the whole screen with more than blank spaces. The fill character is passed to the `TXTCLR` subroutine via the **.A** register. + + + +`FIGURE 2.58: TCLR Macro` + +```assembly +* +*``````````````````````````````* +* TCLR * +* * +* THE TCLR MACRO FILLS (OR * +* CLEARS) THE ENTIRE SCREEN * +* WITH A GIVEN CHARACTER. * +* * +* PARAMETERS * +* * +* ]1 = FILL CHARACTER * +* * +* CYCLES: 50+ * +* SIZE: 42 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +TCLR MAC + LDA ]1 ; {2C2B} LOAD FILL CHARACTER + JSR TXTCLR ; {48C40B} CALL TXTCLR SUBROUTINE + <<< + +``` + + + +--- + + + +### THE TXTCLR Subroutine + +_SUMMARY_ + +| Condition | Value | +| --------------- | ----------------------------------------- | +| Name | `TXTCLR` | +| Type | Subroutine | +| File | `SUB.TXTCLR.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Fill screen memory with a given character | +| Input | .A = Fill Character | +| Output | Full-screen fill with a single character | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 42+ | +| Bytes | 37 | +| Notes | none | +| See Also | `TCLR` | + +--- + +*DETAILS* + +The `TXTCLR` subroutine fills page 1 of the screen memory with a given value, filling the display with the associated character. Note that this fill is done in sections as they are naturally interweaved by the Apple II; this allows for a quicker fill while also avoiding a write overtop the fabled "screen hole" memory locations used by various devices. + + + +`FIGURE 2.59: TXTCLR Subroutine Source` + +```assembly +* +*``````````````````````````````* +* TXTCLR (NATHAN RIGGS) * +* * +* INPUT: * +* * +* .A = FILL CHARACTER * +* * +* OUTPUT: * +* * +* FILLS THE SCREEN WITH THE * +* GIVEN CHARACTER. FILLS BY * +* SECTION TO AVOID CHANGING * +* SCREENHOLE VALUES. * +* * +* DESTROYS: NZCIDV * +* ^^ * +* * +* CYCLES: 42+ * +* SIZE: 37 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]F EQU VARTAB ; FILL CHARACTER +* +TXTCLR +* + STA ]F ; {4C3B} STORE FILL CHAR IN MEM + LDY #$78 ; {2C2B} LENGTH OF EACH SCREEN SECTION {NZ} +:LP1 + STA $400,Y ; {5C3B} 1ST SECTION + STA $480,Y ; {5C3B} 2ND SECTION + STA $500,Y ; {5C3B} 3RD SECTION + STA $580,Y ; {5C3B} 4TH SECTION + CPY #120 ; {2C2B} {NZC} -- #80 + BPL :NDB ; {3C2B} + STA $600,Y ; {5C3B} 5TH SECTION + STA $680,Y ; {5C3B} 6TH SECTION + STA $700,Y ; {5C3B} 7TH SECTION + STA $780,Y ; {5C3B} 8TH SECTION +:NDB + DEY ; {2C1B} DECREASE BYTE COUNTER {NZ} + BPL :LP1 ; {3C2B} IF POSITIVE, KEEP LOOPING + RTS ; {6C1B} RETURN TO CALLING ROUTINE +* + +``` + + + +--- + + + +## Standard Input Macros and Subroutines + +While the STDIO collection of macros and subroutines primarily focuses on screen output, methods of retrieving input from the keyboard or the paddles are essential for almost any program, and thus are provided here. Most of the macros and subroutines here use pre-existing routines provided by the Monitor and are thus integrated with COUT; when this is not the case, the reasoning behind the macro or subroutine will be explained in detail. + + + +`FIGURE 2.60: MAC.STDIN.ASM Header Listing` + +```assembly +* +*``````````````````````````````* +* MAC.STDIN.ASM * +* * +* THIS IS A MACRO LIBRARY FOR * +* STANDARD INPUT AND OUTPUT. * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 11-MAR-2021 * +* ASSEMBLER: MERLIN 8 PRO * +* OS: DOS 3.3 * +* * +* SUBROUTINES FILES USED: * +* * +* SUB.SINPUT * +* * +* LIST OF MACROS * +* * +* INP : STRING INPUT * +* GKEY : GET SINGLE KEY * +* PDL : READ PADDLE STATE * +* PBX : READ PDL BTN X * +* WAIT : WAIT FOR KEYPRESS * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* + +``` + + + +--- + + + +### THE INP MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `INP` | +| Type | Macro | +| File | `MAC.STDIN.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | input a string from the keyboard | +| Input | none | +| Output | keystrokes are printed to the screen
as they are entered | +| Dependencies | `SUB.SINPUT.ASM` | +| Flags Destroyed | NZCV | +| Cycles | 64+ | +| Bytes | 41 | +| Notes | none | +| See Also | `SINPUT` | + +*DETAILS* + +The `INP` macro is as simple as a macro can get: it simply calls the `SINPUT` subroutine, which works much like the `INPUT` command in Applesoft BASIC. Please see the `SINPUT` entry for details about how the subroutine works. + + + +`FIGURE 2.61: INP Macro Source` + +```asm +* +*``````````````````````````````* +* INP * +* * +* INPUTS A STRING FROM KEYBRD * +* AND STORES IT IN [RETURN] * +* * +* PARAMETERS * +* * +* NONE * +* * +* CYCLES: 64+ * +* SIZE: 41 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +INP MAC + JSR SINPUT + <<< +* + +``` + + + +--- + + + +### THE SINPUT SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `SINPUT` | +| Type | Subroutine | +| File | `SUB.SINPUT.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | input a string from the keyboard | +| Input | none | +| Output | keystrokes are printed to the screen
as they are entered | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 58+ | +| Bytes | 38 | +| Notes | none | +| See Also | `INP` | + +*DETAILS* + +The `SINPUT` subroutine waits for a line of input to be entered by the user via the keyboard, terminated by a press of the **RETURN** key. The subroutine can be thought of as a wrapper around the `GETLN` routine already provided on the Apple II: `GETLN` is first called, which handles all of the necessary details of inputting from the keyboard. After the user hits **RETURN**, the `SINPUT` subroutine then copies the string from its usual location to the library's **RETURN** address, with its length in bytes stored at **RETLEN**, which resides at the byte just prior to **RETURN** (making it accessible as a normal string). + +Of course, `SINPUT` can be abandoned in favor of `GETLN`, as `SINPUT` does nothing but integrate `GETLN` into the logic of the AppleIIASM library. This would save a number of cycles, but the savings would be rather pointless: it does not matter how many cycles an input routine will take. For the sake of consistency, it is advised that `SINPUT` be used in place of `GETLN` alone so that the programmer need not remember the extra data location used by `GETLN`. + + + +`FIGURE 2.62: SINPUT Subroutine Source` + +```assembly +* +*``````````````````````````````* +* SINPUT (NATHAN RIGGS) * +* * +* INPUT * +* * +* USER TYPES UP TO 255 CHARS * +* * +* OUTPUT: * +* * +* .X = LENGTH OF STRING * +* RETURN = STRING TYPED * +* RETLEN = LENGTH OF STRING * +* * +* DESTROY: NZCIDV * +* ^^^ * +* * +* CYCLES: 58+ * +* SIZE: 38 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]STRLEN EQU VARTAB ; STRING LENGTH (1 BYTE) +* +SINPUT + LDX #$00 ; {2C2B} RESET LENGTH {NZ} + JSR GETLN ; {6C3B} GOSUB GETLN ROUTINE. AS JOHN ROMERO + ; HILARIOUSLY POINTED OUT ON FACEBOOK, + ; COMING OUT OF NOWHERE, + ; WHICH I AM BOTH ASHAMED AND PROUD OF, + ; IT'S PRETTY POINTLESS TO CYCLE-COUNT + ; AN INPUT FUNCTION, SO I WON'T BE DOING + ; SO HERE, THOUGH I STARTED TO TRY +* + STX ]STRLEN ; {4C3B} STRING LENGTH RETURNED IN .X; STORE + CPX #0 ; {2C2B} IF LENGTH = 0, EXIT {NZC} + BNE :INP_CLR ; {3C2B} OTHER, COPY STRING + STX RETLEN ; {4C3B} OTHERWISE, STORE IN RETURN LENGTH + JMP :EXIT ; {6C5B} JUMP TO EXIT +:INP_CLR + STX RETLEN ; {4C3B} STORE STRING LENGTH + LDY #255 ; {2C2B} RESET COUNTER TO -1 {NZ} +:LOOP + INY ; {2C1B} INCREASE COUNTER BY 1 {NZ} + LDA KEYBUFF,Y ; {5C3B} CHAR FROM KEYBOARD BUFFER {NZ} + STA RETURN,Y ; {5C3B} STORE IN RETURN AT INDEX + CPY ]STRLEN ; {4C3B} IF COUNTER < STRING LENGTH {NZC} + BNE :LOOP ; {3C2B} LOOP; ELSE, EXIT +:EXIT + RTS ; {6C1B} RETURN TO CALLING ROUTINE + +``` + + + +--- + + + +### THE GKEY MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ----------------------------------- | +| Name | `GKEY` | +| Type | Macro | +| File | `MAC.STDIN.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | input a character from the keyboard | +| Input | none | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 11+ | +| Bytes | 7 | +| Notes | none | +| See Also | `WAIT` | + +*DETAILS* + +The `GKEY` macro simply serves as a shortcut for the `GETKEY` routine provided by the Monitor. This routine pauses execution until a key is pressed, and then stores the value of the key pressed in the **.A** register. Afterward, the keyboard strobe is reset, which is required for any other future `GETKEY` uses to work correctly. + + + +`Figure 2.63: GKEY Macro Source` + +```assembly +* +*``````````````````````````````* +* GKEY * +* * +* WAITS FOR USER TO PRESS A * +* KEY, THEN STORES THAT IN .A * +* * +* PARAMETERS * +* * +* NONE * +* * +* CYCLES: 11+ * +* SIZE: 7 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +GKEY MAC + JSR GETKEY ; {6+C3B} MONITOR GET SUBROUTINE + LDY #0 ; {2C2B} + STY STROBE ; {3C2B} RESET KBD STROBE + <<< +* + +``` + + + +--- + + + +### THE PDL MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------- | +| Name | `PDL` | +| Type | Macro | +| File | `MAC.STDIN.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Reads state of the given paddle wheel | +| Input | ]1 = Paddle Address | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 8+ | +| Bytes | 5 | +| Notes | none | +| See Also | `PBX` | + +*DETAILS* + +The `PDL` macro returns the state value of a specified paddle wheel (#0 or #1, passed via **.X**), ranging from 0 to 255, and returns it in the **.Y** register. For the most part this is another wrapper macro, and adds very little in the way of extra functionality. + + + +`FIGURE 2.64: PDL Macro Source` + +```assembly +* +*``````````````````````````````* +* PDL * +* * +* SIMPLY READS STATE OF PADDLE * +* NUMBER [NUM] AND STORES IT * +* IN THE Y REGISTER. * +* * +* PARAMETERS * +* * +* ]1 = PADDLE # TO READ * +* * +* CYCLES: 8+ * +* SIZE: 5 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +PDL MAC ; GET PADDLE VALUE + LDX ]1 ; {2C2B} READ PADDLE # ]1 (USUALLY 0) + JSR PREAD ; {6+C3B} PADDLE READING STORED IN Y + <<< +* + +``` + + + +--- + + + +### THE PBX MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | -------------------------------------- | +| Name | `PBX` | +| Type | Macro | +| File | `MAC.STDIN.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Reads state of the given paddle button | +| Input | ]1 = Paddle button address | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 8+ | +| Bytes | 8 | +| Notes | none | +| See Also | `PDL` | + +*DETAILS* + +The `PBX` macro returns the state of a specified paddle button. A button can either be pressed (or on, value #1) or unpressed (off, #0). The state of the button is returned via the **.X** register: 0 for for unpressed, 1 for pressed. + +This library also provides convenient labels for the paddle button addresses to be passed as parameters and read. There are four possible buttons that can be read, each with their own memory address byte. These can be accessed via the labels PB0, PB1, PB2 and PB3. These correlate, in order, to the addresses $C061, $C062, $C063, and $C060. + + + +`FIGURE 2.65: PBX Macro Source` + +```assembly +* +*``````````````````````````````* +* PBX * +* * +* READ THE SPECIFIED PADDLE * +* BUTTON. * +* * +* PARAMETERS * +* * +* ]1 = PADDLE BUTTON TO READ * +* * +* PB0: $C061 PB1: $C062 * +* PB2: $C063 PB4: $C060 * +* * +* CYCLES: 8+ * +* SIZE: 8 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +PBX MAC + LDX #1 ; {2C2B} + LDA ]1 ; {2C2B} IF BTN = PUSHED + BMI EXIT ; {2C2B} IF HIBYTE SET, BUTTON PUSHED + LDX #0 ; {2C2B} OTHERWISE, BUTTON NOT PUSHED +EXIT + <<< +* + +``` + + + +--- + + + +### THE WAIT MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------- | +| Name | `WAIT` | +| Type | Macro | +| File | `MAC.STDIN.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Pauses execution until key is pressed | +| Input | none | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 9+ | +| Bytes | 8 | +| Notes | none | +| See Also | `GKEY` | + +*DETAILS* + +The `WAIT` macro is rather self-explanatory: program execution is paused until a key is pressed. While this is similar to the `GKEY` macro, `WAIT` does not use COUT; instead, the loop is handled by the macro itself. Once a button is pressed, its ASCII value can be found in the **.A** register. + + + +`FIGURE 2.66: WAIT Macro Source` + +```assembly +* +*``````````````````````````````* +* WAIT * +* * +* WAIT FOR A KEYPRESS WITHOUT * +* INTERFERING WITH COUT. KEY * +* CODE IS STORED IN .A. * +* * +* PARAMETERS * +* * +* NONE * +* * +* CYCLES: 9 * +* SIZE: 8 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +WAIT MAC +]WTLP LDA KYBD ; {2C2B} READ KEYBOARD BUFFER + BPL ]WTLP ; {2C2B} IF 0, KEEP LOOPING + AND #$7F ; {2C2B} OTHERWISE, SET HI BIT + STA STROBE ; {3C2B} CLEAR STROBE + <<< + +``` + + + +--- + + + +## Miscellaneous STDIO Macros and Subroutines + +The final collection of macros on the STDIO disk contains miscellaneous items that do not necessarily fit neatly into the COUT or direct screen memory collections. Some of these macros may work only in systems with a Language card for 80-column mode, or versions of the Apple II that can use Mousetext characters. For the most part, these macros are less complicated or less useful than other STDIO macros, and may be added to your own work easier on a one-by-one basis. + +`FIGURE 2.67: MAC.MSC.STDIO.ASM Header` + +```assembly +* +*``````````````````````````````* +* MAC.MISC.STDIO.ASM * +* * +* THIS IS A MACRO LIBRARY FOR * +* STANDARD INPUT AND OUTPUT. * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 11-MAR-2021 * +* ASSEMBLER: MERLIN 8 PRO * +* OS: DOS 3.3 * +* * +* REQUIRED SUBROUTINES * +* * +* SUB.TXTCENT.ASM * +* * +* LIST OF MACROS * +* * +* COL40 : FORCE 40COL MODE * +* COL80 : FORCE 80COL MODE * +* DIE80 : KILL 80COL FIRMWARE * +* TCTR : CALCULATE TEXT CENTER * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* + +``` + + + +--- + + + +### THE DIE80 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ----------------------------------------- | +| Name | `DIE80` | +| Type | Macro | +| File | `MAC.MISC.STDIO.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | kill 80-column mode, restoring 40 columns | +| Input | none | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 8+ | +| Bytes | 5 | +| Notes | none | +| See Also | `COL80` `COL40` | + +*DETAILS* + +The `DIE80` macro sends a CTRL-U command to COUT, which tells COUT to force an exit from 80-column mode. + + + +`FIGURE 2.68: DIE80 Macro Source` + +```assembly +* +*``````````````````````````````* +* DIE80 * +* * +* SEND CTRL-U TO COUT, FORCING * +* 40 COLUMN MODE. * +* * +* PARAMETERS * +* * +* NONE * +* * +* CYCLES: 8+ * +* SIZE: 5 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +DIE80 MAC + LDA #21 ; {2C2B} CTRL-U CHARACTER + JSR COUT ; {6+C3B} SEND TO SCREEN + <<< +* + +``` + + + +--- + + + +### THE COL80 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------- | +| Name | `COL80` | +| Type | Macro | +| File | `MAC.MISC.STDIO.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | turn on 80-column mode | +| Input | none | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 8+ | +| Bytes | 5 | +| Notes | none | +| See Also | `DIE80` `COL40` | + +*DETAILS* + +The `COL80` Macro turns on 80-column mode, if it is available, by sending a CTRL-R signal to COUT. + + + +`FIGURE 2.69: COL80 Macro Source` + +```assembly +* +*``````````````````````````````* +* COL80 * +* * +* FORCE 80-COLUMN MODE. * +* * +* PARAMETERS * +* * +* NONE * +* * +* CYCLES: 8+ * +* SIZE: 5 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +COL80 MAC + LDA #18 ; {2C2B} CTRL-R CHARACTER + JSR COUT ; {6+C3B} SEND TO SCREEN + <<< +* + +``` + + + +--- + + + +### THE COL40 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------- | +| Name | `COL40` | +| Type | Macro | +| File | `MAC.MISC.STDIO.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | turn on 40-column mode | +| Input | none | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 8+ | +| Bytes | 5 | +| Notes | none | +| See Also | `DIE80` `COL80` | + +*DETAILS* + +The `COL40` Macro turns on 40-column mode by sending a CTRL-Q character to COUT. + + + +`FIGURE 2.70: COL40 Macro Source` + +```assembly +* +*``````````````````````````````* +* COL40 * +* * +* FORCE 40-COLUMN MODE * +* * +* PARAMETERS * +* * +* NONE * +* * +* CYCLES: 8+ * +* SIZE: 5 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +COL40 MAC + LDA #17 ; {2C2B} CTRL-Q CHARACTER + JSR COUT ; {6+C3B} SEND TO SCREEN + <<< +* + +``` + + + +--- + + + +### THE TCTR MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `TCTR` | +| Type | Macro | +| File | `MAC.MISC.STDIO.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Calculate the point a string fits inside
of another in order to center it | +| Input | ]1 = short string length
]2 = long string length | +| Output | none | +| Dependencies | `SUB.TXTCENT.ASM` | +| Flags Destroyed | NZCV | +| Cycles | 47+ | +| Bytes | 27 | +| Notes | none | +| See Also | `TXTCENT` | + +*DETAILS* + +The `TCTR` macro accepts two string lengths, the first smaller than the second, and then calculates the position the first string needs to be placed within the second string (or line) in order to center it. The numeric result is stored in the RETURN address. This is primarily helpful for centering strings of text on the screen. + + + +`FIGURE 2.71: TCTR Macro Source` + +```assembly +* +*``````````````````````````````* +* TCTR * +* * +* THE TCTR MACRO TAKES A LINE * +* LENGTH AND A SMALLER STRING * +* LENGTH, THEN CALCULATES THE * +* STARTING POSITION OF THE * +* STRING IN THE LINE SO THAT * +* IT CAN BE CENTERED. * +* * +* PARAMETERS * +* * +* ]1 = STRING LENGTH * +* ]2 = LINE LENGTH * +* * +* CYCLES: 47+ * +* SIZE: 27 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +TCTR MAC + LDA ]1 ; {2C2B} LOAD STRING LENGTH + STA WPAR1 ; {3C2B} PASS VIA ZERO PAGE + LDA ]2 ; {2C2B} LOAD LINE LENGTH + STA WPAR1+1 ; {3C2B} PASS VIA ZERO PAGE + JSR TXTCENT ; {37C19B} CALL TXTCENT SUBROUTINE + <<< + +``` + + + +--- + + + +### THE TXTCENT Subroutine + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `TXTCENT` | +| Type | Subroutine | +| File | `SUB.TXTCENT.ASM` | +| Author | Nathan Riggs | +| Last Revision | 14-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Calculate a position in a line for a
string to be centered | +| Input | WPAR1 = Short String Length
WPAR1+1 = Longer String Length | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 31+ | +| Bytes | 16 | +| Notes | none | +| See Also | `TCTR` | + +--- + +*DETAILS* + +The `TXTCENT` subroutine calculates the center position of a string within a larger string or possible characters. The formula for this is fairly simple, and is easy to implement: (LONG / 2) - (SHORT / 2). The reason a subroutine exists for this is that when such a subroutine is needed--usually to center text on the screen--it will be used repeatedly, meriting a separate subroutine. If a situation arises where centering text needs to be done once alone in a program, then the subroutine should simply be copied in-line to the program rather than have it be called. + + + +`FIGURE 2.72: TXTCENT Subroutine Source` + +```assembly +* +*``````````````````````````````* +* TXTCENT (NATHAN RIGGS) * +* * +* INPUT: * +* * +* WPAR1 = STRING LENGTH * +* WPAR1+1 = LINE LENGTH * +* * +* DESTROYS: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 31+ * +* SIZE: 16 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]SLEN EQU WPAR1 ; STRING LENGTH +]LNLEN EQU WPAR1+1 ; LINE LENGTH +* +TXTCENT +* + LSR ]SLEN ; {5C2B} DIVIDE STRING LENGTH IN HALF + LSR ]LNLEN ; {5C2B} DIVIDE LINE LENGTH IN HALF + LDA ]LNLEN ; {2C2B} GET LINE LENGTH + SEC ; {2C1B} SET CARRY + SBC ]SLEN ; {3C2B} SUBTRACT STRING LENGTH/2 + STA RETURN ; {3C2B} + LDX #1 ; {2C2B} SET RETURN LENGTH BYTE + STX RETLEN ; {3C2B} + RTS ; {6C1B} + +``` + + + +--- + + + +# PART II: STDIO Collection Demo File + +The following listing shows the usage of each macro in the STDIO Collection, with extensive commentary about how to use the macro. This covers all of the macros in the `MAC.COUT.STDOUT.ASM` file, `MAC.SCRMEM.STDIO.ASM` file, and the `MAC.MISC.STDIO.ASM` file. + + + +`FIGURE 2.73: DEMO.STDIO.ASM Source Listing` + +```assembly +* +*``````````````````````````````* +* DEMO.STDIO * +* * +* A DEMO OF THE MACROS AND * +* SUBROUTINES IN THE STDIO * +* APPLEIIASM LIBRARY. * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 11-MAR-2021 * +* ASSEMBLER: MERLIN 8 PRO * +* OS: DOS 3.3 * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** ASSEMBLER DIRECTIVES +* + CYC AVE + EXP OFF + TR ON + DSK DEMO.STDIO + OBJ $BFE0 + ORG $6000 +* +*``````````````````````````````* +* TOP INCLUDES (HOOKS,MACROS) * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* + PUT MIN.HEAD.REQUIRED.ASM + USE MIN.MAC.REQUIRED.ASM +* + PUT MIN.HEAD.STDIO.ASM +* + USE MIN.MAC.COUT.STDOUT.ASM + USE MIN.MAC.STDIN.ASM + USE MIN.MAC.SCRMEM.STDIO.ASM + USE MIN.MAC.MISC.STDIO.ASM +* +]HOME2 EQU $FC58 +* +*``````````````````````````````* +* PROGRAM MAIN BODY * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* + JSR ]HOME2 ; CLEAR SCREEN +* +*``````````````````````````````* +* OUTPUT MACRO EXAMPLES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE STDIO LIBRARY CONTAINS A NUMBER OF +** MACROS AND SUBROUTINES FOR OUTPUTTING +** CHARACTERS TO THE 40 COLUMN SCREEN. +** THESE ARE: +* +** - CPUT : PUT CHARACTER INTO SCREEN MEMORY +** - CURB : MOVE COUT CURSOR BACKWARDS +** - CURD : MOVE COUT CURSOR DOWN +** - CURF : MOVE COUT CURSOR FORWARDS +** - CURU : MOVE COUT CURSOR UP +** - PRN : PRINT A STRING OR TEXT LITERAL +** - SCPOS : SET CURSOR POSITION +** - SETCX : SET CURSOR X POSITION +** - SETCY : SET CURSOR Y POSITION +** - SPRN : PRINT A PRECEDING LENGTH BYTE STRING +** - SPUT : PUT STRING INTO SCREEN MEMORY +** - TCIRC : PUT TEXT CIRCLE INTO SCREEN MEMORY +** - TCLR : FILL THE SCREEN MEMORY +** - TCTR : CALCULATE CENTER POSITION +** - THLIN : PUT HORIZONTAL TEXT LINE TO SCREEN MEMORY +** - TMORE : WORD-WRAP AND PAUSE SCROLLING OF NULL-TERM STRING +** - TREC : PUT UNFILLED RECTANGLE INTO SCREEN MEMORY +** - TRECF : PUT FILLED RECTANGLE INTO SCREEN MEMORY +** - TLINE : PLOT DIAGONAL LINE TO SCREEN MEMORY +** - TVLIN : PLOT VERTICAL LINE TO SCREEN MEMORY +* +*``````````````````````````````* +* CPUT MACRO * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE CPUT MACRO PLACES A CHARACTER PROVIDED +** IN THE PARAMETERS INTO SCREEN MEMORY, +** CALCULATING THE MEMORY ADDRESS IN REAL-TIME. +** THIS IS SIGNIFICANTLY FASTER THAN USING COUT +** SUBROUTINES, BUT THE OBVIOUS DOWNSIDE IS +** THAT COUT SUBROUTINES WILL NOT PLAY NICE +** WITH CPUT. +* +** USAGE: +* + JSR ]HOME2 ; FILL SCREEN NOT COVERED YET + _PRN "CPUT MACRO",8D + _PRN "==========",8D8D + CPUT #10;#10;#"C" + CPUT #11;#11;#"P" + CPUT #12;#12;#"U" + CPUT #13;#11;#"T" + CPUT #16;#10;#"W" + CPUT #17;#11;#"O" + CPUT #18;#12;#"O" + CPUT #19;#11;#"T" + CPUT #20;#10;#"!" + _WAIT +* +*``````````````````````````````* +* CURB MACRO * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE CURB MACRO MOVES THE COUT CURSOR +** BACKWARDS BY THE GIVEN NUMBER OF SPACES. +* +** USAGE: +* + JSR ]HOME2 + _PRN "CURB MACRO",8D + _PRN "==========",8D8D + _PRN "0123456789" + _WAIT + CURB #3 + _PRN "--",8D + _WAIT +* +*``````````````````````````````* +* CURD MACRO * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE CURD MACRO MOVES THE COUT CURSOR +** DOWNWARDS BY THE GIVEN NUMBER OF SPACES. +* +** USAGE: +* + JSR ]HOME2 + _PRN "CURD MACRO",8D + _PRN "==========",8D8D + _PRN "HA" + CURD #2 + _PRN "HAA" + CURD #3 + _PRN "HAAAA" + CURD #4 + _PRN "HAAAAAAAA!" + _WAIT +* +*``````````````````````````````* +* CURF * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE CURF MACRO MOVES THE COUT CURSOR +** FORWARD BY THE NUMBER OF GIVEN SPACES. +* +** USAGE: +* + JSR ]HOME2 + _PRN "CURF MACRO",8D + _PRN "==========",8D8D + _PRN " ",8D8D8D + _PRN "HEEEEEE" + CURF #3 + _PRN "HEEEE" + CURF #2 + _PRN "HEE" + CURF #1 + _PRN "HE!",8D8D + _WAIT +* +*``````````````````````````````* +* CURU * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE CURU MACRO MOVES THE COUT CURSOR +** UP THE NUMBER OF GIVEN SPACES. +* +** USAGE: +* + JSR ]HOME2 + _PRN "CURU MACRO",8D + _PRN "==========",8D8D8D8D8D8D8D8D8D8D8D8D8D8D + _PRN "HO" + CURU #2 + _PRN "HO HO" + CURU #3 + _PRN "HOOO HO HO" + CURU #4 + _PRN "HOOOOO!" + _WAIT +* +*``````````````````````````````* +* PRN * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE PRN MACRO PRINTS A LITERAL STRING +** TO THE CURSOR POSITION, OR PRINTS A +** NULL-TERMINATED STRING AT A GIVEN ADDRESS. +** NOTE THAT A SEPARATE MACRO, SPRN, IS USED +** FOR PRINTING PRECEDING LENGTH-BYTE STRINGS. +* +** USAGE: +* + JSR ]HOME2 + PRN "PRN MACRO",8D + PRN #ESGN + PRN " ",8D8D8D8D8D8D + PRN #MSG1 + _WAIT +* +*``````````````````````````````* +* SCPOS * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE SCPOS MACRO POSITIONS THE COUT CURSOR +** AT THE GIVEN X,Y COORDINATES. +* +** USAGE: +* + JSR ]HOME2 + PRN "SCPOS MACRO",8D + PRN "===========" + SCPOS #15;#8 + PRN "NEW CURSOR POSITION" + _WAIT +* +*``````````````````````````````* +* SETCX * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE SETCX MACRO SETS THE X POSITION +** OF THE CURSOR TO A GIVEN VALUE. +* +** USAGE: +* + JSR ]HOME2 + PRN "SETCX MACRO",8D + PRN "===========" + PRN " ",8D8D8D8D + SETCX #10 + PRN "X POSITION HERE!" + _WAIT +* +*``````````````````````````````* +* SETCY * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE SETCY MACRO SETS THE Y-POSITION +** OF THE CURSOR TO A GIVEN VALUE. +* +** USAGE: +* + JSR ]HOME2 + PRN "SETCY MACRO",8D + PRN "===========" + SETCY #10 + PRN "10 LINE DOWN FROM TOP!" + _WAIT +* +*``````````````````````````````* +* SPRN * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE SPRN MACRO PRINTS A REGULAR STRING +** THAT HAS A PRECEDING LENGTH-BYTE TO +** COUT. NOTE THAT THIS ONLY COVERS STRINGS +** LESS THAN 256 BYTES LONG. +* +** USAGE: +* + JSR ]HOME2 + PRN "SPRN MACRO",8D + PRN "==========",8D8D8D + SPRN #STR1 + _WAIT +* +*``````````````````````````````* +* SPUT * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE SPUT MACRO DIRECTLY PLOTS A STRING OF +** CHARACTERS TO THE SCREEN MEMORY. NOTE THAT +** THERE IS NO ERROR HANDLING, SO CARE MUST BE +** TAKEN TO NOT EXCEED THE SCREEN BOUNDARIES. +** THIS DOES NOT INTERACT WITH COUT. +* +** USAGE: +* +* + JSR ]HOME2 + PRN "SPUT MACRO",8D + PRN "==========",8D8D + SPUT #10;#10;#STR1 + _WAIT +* +*``````````````````````````````* +* TCIRC * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE TCIRC MACRO DRAWS A CIRCLE WITH +** THE FILL CHARACTER SPECIFIED ONTO THE +** SCREEN MEMORY. THIS MACRO PLOTS DIRECTLY +** TO MEMORY, AND THEREFORE IS NOT SUBJECT TO +** THE WHIMS OF COUT. +* +** USAGE: +* + JSR ]HOME2 + PRN "TCIRC MACRO",8D + PRN "===========",8D8D + TCIRC #19;#10;#9;#"X" + TCIRC #19;#10;#7;#"+" + TCIRC #19;#10;#4;#"-" + TCIRC #19;#10;#2;#"^" + TCIRC #19;#10;#1;#"." + _WAIT +* +*``````````````````````````````* +* TCLR * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE TCLR MACRO FILLS THE SCREEN MEMORY +** WITH A GIVEN CHARACTER VALUE. THE DATA +** STORED IN THE "SCREEN HOLES" IS KEPT +** INTACT. +* +** USAGE: +* + JSR ]HOME2 + PRN "TCLR MACRO",8D + PRN "==========",8D + _WAIT + TCLR #"@" + _WAIT + TCLR #"%" + _WAIT + TCLR #"$" + _WAIT + TCLR #"&" + _WAIT + TCLR #"!" + _WAIT + TCLR #":" + _WAIT + TCLR #"'" + _WAIT + TCLR #"`" + _WAIT + TCLR #"," + _WAIT + TCLR #"." + _WAIT +* +*``````````````````````````````* +* TCTR * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE TCTR MACRO CALCULATES THE X POSITION +** OF A STRING IN RELATION TO THE AVAILABLE +** LINE LENGTH TO CENTER THE STRING ON THE +** LINE. THE X POSITION IS PASSED BACK VIA +** THE [RETURN] LOCATION. +* +** USAGE: +* + JSR ]HOME2 + PRN "TCTR MACRO",8D + PRN "==========",8D8D + SETCY #12 + TCTR CTRSTR;#40 + SETCX RETURN + SPRN #CTRSTR + _WAIT +* +*``````````````````````````````* +* THLIN * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE THLIN MACRO DRAWS A HORIZONTAL LINE +** ON TO SCREEN MEMORY WITH THE GIVEN +** CHARACTER. THIS DOES NOT INTERACT WITH +** COUT. +* +** USAGE: +* + JSR ]HOME2 + PRN "THLIN MACRO",8D + PRN "===========",8D8D + THLIN #5;#35;#6;#"@" + THLIN #5;#35;#8;#"#" + THLIN #5;#35;#10;#"$" + THLIN #5;#35;#12;#"%" + THLIN #5;#35;#14;#":" + _WAIT +* +*``````````````````````````````* +* TMORE * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE TMORE MACRO SCROLLS A ZERO-TERMINATING STRING +** ON THE SCREEN, WORD-WRAPPING EACH LINE AS IT GOES. +** THE FIRST PARAMETER IS THE STRING ADDRESS, OR AN +** INDIRECT ADDRESS. THE SECOND PARAMETER IS THE +** DESIRED MAXIMUM LINE LENGTH, AND THE THIRD PARAMETER +** IS THE NUMBER OF LINES TO PRINT BEFORE PAUSING +** THE SCROLL UNTIL A KEY IS PRESSED. +* +** IF THE THIRD PARAMETER (SCROLL) IS #0, THEN SCROLLING +** IS PAUSED ANY TIME A RETURN CHARACTER IS FOUND (8D). +** YOU CAN THEN HAVE IT SCROLL A PARAGRAPH AT A TIME BY +** HAVING A LINE WITH A SPACE FOLLOWED BY #$8D. +* +** USAGE: +* + JSR ]HOME2 + TMORE #LONGSTR;#39;#15 + _WAIT +* +*``````````````````````````````* +* TREC * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE TREC MACRO CREATES A RECTANGULAR +** BORDER IN SCREEN MEMORY, INDEPENDENT OF +** THE STATE OF COUT. NOTE THAT THIS DOES +** NOT FILL IN THE EMPTY SPACE INSIDE OF THE +** RECTANGULAR BORDER. +* +** USAGE: +* + JSR ]HOME2 + PRN "TREC MACRO",8D + PRN "==========",8D8D + TREC #5;#6;#35;#17;#"@" + TREC #7;#8;#33;#15;#"#" + _WAIT +* +*``````````````````````````````* +* TRECF * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE TRECF MACRO WRITES A FILLED +** RECTANGLE TO THE SCREEN MEMORY SPACE. +** THIS DOES NOT INTERACT WITH COUT. +* +** USAGE: +* + JSR ]HOME2 + PRN "TRECF MACRO",8D + PRN "===========",8D8D + TRECF #5;#5;#14;#14;#"+" + TRECF #16;#5;#25;#14;#":" + _WAIT +* +*``````````````````````````````* +* TLINE * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE TLINE MACRO CREATES A DIAGONAL +** LINE STARTING AT AN ORIGIN X,Y +** POSITION TO A DESTINATION X,Y COORDINATE. +** THIS PRINTS DIRECTLY TO SCREEN MEMORY, +** AND DOES NOT INTERACT WITH COUT. +* +** USAGE: +* + JSR ]HOME2 + PRN "TLINE MACRO",8D + PRN "===========",8D8D + TLINE #5;#5;#15;#10;#"@" + TLINE #5;#10;#15;#5;#"#" + _WAIT +* +*``````````````````````````````* +* TVLIN * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE TVLIN MACRO CREATES A VERTICAL LINE +** IN SCREEN MEMORY WITH THE GIVEN CHARACTER. +** THIS DOES NOT INTERACT WITH COUT. +* +** USAGE: +* + JSR ]HOME2 + PRN "TVLIN MACRO",8D + PRN "===========",8D8D + TVLIN #5;#15;#19;#"#" + _WAIT +* +*``````````````````````````````* +* INPUT MACRO EXAMPLES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE STDIO LIBRARY INCLUDES MACROS +** AND SUBROUTINES DEDICATED TO RETRIEVING +** INPUT DATA FROM THE KEYBOARD, FROM +** THE SCREEN MEMORY, AND FROM THE +** PADDLE. +* +** - GKEY : GET KEY FROM KEYBOARD INPUT +** - INP : INPUT A STRING FROM KEYBOARD +** - PBX : INPUT FROM PADDLE BUTTONS +** - PDL : INPUT FROM PADDLE WHEELS +** - RCPOS : GET VALUE FROM SCREEN POSITION +** - WAIT : WAIT FOR A KEYBOARD KEY PRESS +* +*``````````````````````````````* +* GKEY * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE GKEY MACRO WAITS FOR THE USER +** TO PRESS A KEY ON THE KEYBOARD, THEN +** RETURNS THE VALUE RECEIVED IN .A. +* +** USAGE: +* + JSR ]HOME2 + PRN "GKEY MACRO",8D + PRN "==========",8D8D + GKEY + JSR COUT + _WAIT +* +*``````````````````````````````* +* INP * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE INP MACRO INPUTS A STRING OF +** TEXT FROM THE USER VIA THE KEYBOARD, +** ENDING WHEN THE RETURN KEY IS PRESSED. THE +** STRING IS THEN STORED IN [RETURN], WITH +** ITS LENGTH IN THE PRECEDING BYTE KNOWN +** AS [RETLEN]. +* +** USAGE: +* + JSR ]HOME2 + PRN "INP MACRO",8D + PRN "=========",8D8D8D + INP + PRN " ",8D8D8D + SPRN #RETLEN + _WAIT +* +*``````````````````````````````* +* PBX * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE PBX MACRO READS THE STATE OF +** THE SPECIFIED PADDLE BUTTON. FOR THE +** SAKE OF EASE, THE BUTTONS HAVE BEEN +** GIVEN ALIASES OF PB0 - PB1, WITH THE +** CORRESPONDING VALUES: +* +** - PB0 = $C061 +** - PB1 = $CO62 +** - PB2 = $CO63 +** - PB3 = $CO60 +* +** NOTE THAT THE OPEN AND CLOSED APPLE KEYS +** CAN BE READ AS PADDLE BUTTONS, SO A PADDLE +** IS NOT NECESSARY FOR THE FOLLOWING EXAMPLE. +* +** USAGE: +* + JSR ]HOME2 + PRN "PBX MACRO",8D + PRN "=========",8D8D + LDX #1 +:MLP1 + PBX PB0 + CPX #1 + BNE :MLP1 + PRN "PB0 PRESSED!",8D8D + _WAIT +* +*``````````````````````````````* +* PDL * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE PDL MACRO READS THE PADDLE WHEEL +** AND RETURNS THE VALUE READ FROM IT +** IN .Y. SINCE WE WON'T ASSUME THE USER +** HAS A PADDLE HERE, A WORKING EXAMPLE +** WILL NOT BE GIVEN. HOWEVER, THE MACRO +** ACCEPTS A SINGLE PARAMETER THAT DENOTES +** THE PADDLE NUMBER TO READ, AS SUCH: +* +** PDL #0 +* +*``````````````````````````````* +* RCPOS * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE RCPOS MACRO READS A GIVEN ROW +** AND COLUMN OF THE SCREEN AND RETURNS +** THE CHARACTER VALUE IN .A. +* +** USAGE: +* + JSR ]HOME2 + PRN "RCPOS MACRO",8D + PRN "===========",8D8D + RCPOS #0;#0 + JSR COUT + _WAIT +* +*``````````````````````````````* +* WAIT * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE WAIT MACRO IS FUNCTIONALLY +** EQUIVALENT TO THE GKEY MACRO, BUT +** DOES NOT RELY ON THE GETKEY SUBROUTINE. +* +** USAGE: +* + JSR ]HOME2 + PRN "WAIT MACRO",8D + PRN "==========",8D8D + WAIT + JSR COUT + WAIT +* +*``````````````````````````````* +* OTHER STDIO MACROS * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE STDIO LIBRARY CONTAINS A FEW +** MACROS THAT CANNOT BE EASILY CATEGORIZED +** TOGETHER, AND THUS ARE THROWN INTO A +** MISCELLANEOUS SECTION HERE. THE MACROS +** ARE: +* +** - COL40 : SET SCREEN TO 40-COLUMN MODE +** - COL80 : SET SCREEN TO 80-COLUMN MODE +** - DIE80 : KILL 80-COLUMN MODE, FORCING 40 +* +*``````````````````````````````* +* COL40 * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE COL40 MACRO TURNS ON 40-COLUMN +** MODE. OBVIOUSLY, THIS ONLY HAS A +** NOTICEABLE EFFECT IF THE USER IS +** ALREADY IN 80-COLUMN MODE. +* +** USAGE: +* + COL40 +* +*``````````````````````````````* +* COL80 * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE COL80 MACRO TURNS ON 80-COLUMN +** MODE. +* + JSR ]HOME2 + COL80 + PRN "80 COLUMNS!" + WAIT +* +*``````````````````````````````* +* DIE80 * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE DIE80 MACRO KILLS 80-COLUMN +** MODE, RETURNING TO 40-COLUMN MODE +** BY DEFAULT. +* +** USAGE: +* + JSR ]HOME2 + DIE80 + PRN "BACK TO 40 COLUMNS!" +* + JMP REENTRY +* +*``````````````````````````````* +* BOTTOM INCLUDES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* + PUT MIN.LIB.REQUIRED.ASM +* +** INDIVIDUAL SUBROUTINE INCLUDES +* +* STDIO SUBROUTINES +* + PUT MIN.SUB.XPRINT.ASM + PUT MIN.SUB.DPRINT.ASM + PUT MIN.SUB.THLINE.ASM + PUT MIN.SUB.TVLINE.ASM + PUT MIN.SUB.TRECTF.ASM + PUT MIN.SUB.TXTPUT.ASM + PUT MIN.SUB.TBLINE.ASM + PUT MIN.SUB.TCIRCLE.ASM + PUT MIN.SUB.PRNSTR.ASM + PUT MIN.SUB.TXTMORE.ASM + PUT MIN.SUB.TXTCENT.ASM + PUT MIN.SUB.STRPUT.ASM + PUT MIN.SUB.TRECT.ASM + PUT MIN.SUB.TXTCLR.ASM + PUT MIN.SUB.SINPUT.ASM +* +ESGN ASC "=========" + HEX 00 +MSG1 ASC "THIS IS A MESSAGE." + HEX 00 +STR1 STR "THIS IS A STRING." +CTRSTR STR "CENTERED STRING." +LONGSTR ASC "ONE TWO THREE FOUR FIVE SIX SEVEN " + ASC "EIGHT NINE TEN ELEVEN TWELVE THIRTEEN " + ASC "FOURTEEN FIFTEEN SIXTEEN SEVENTEEN " + HEX 8D + ASC "EIGHTEEN NINETEEN TWENTY TWENTY-ONE " + ASC "TWENTY-TWO TWENTY-THREE TWENTY-FOUR " + ASC "25 26 27 28 29 30 31 32 33 34 35 36 37 " + ASC "38 39 40" + HEX 00 + +``` + + + +--- + + + +[Return to Table of Contents](./0.0%20Title_to_TOC) +[Next -- Disk 3: Array Macros and Subroutines](32.0%20Detailed_Reference_D3_Arrays.md) + diff --git a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/32.0 Detailed_Reference_D3_ARRAYS.md b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/32.0 Detailed_Reference_D3_ARRAYS.md new file mode 100644 index 0000000..043e924 --- /dev/null +++ b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/32.0 Detailed_Reference_D3_ARRAYS.md @@ -0,0 +1,3639 @@ +# Disk 3 : 8-Bit and 16-Bit 1D and 2D Arrays + +- [Part I: The Arrays Collection](#part-i-the-arrays-collection) + - [Arrays Components](#arrays-components) + - [Arrays Header File](#arrays-header-file) + - [Arrays Macros and Subroutines](#macros-and-subroutines) + - [8-Bit Arrays](#1-dimensional-8-bit-arrays) + - [1-Dimensional, 8-Bit Arrays](#1-dimensional-8-bit-arrays) + - [The DIM81 Macro](#the-dim81-macro) + - [The ADIM81 Subroutine](#the-adim81-subroutine) + - [The GET81 Macro](#the-get81-macro) + - [The AGET81 Subroutine](#the-aget81-subroutine) + - [The PUT81 Macro](#the-put81-macro) + - [The APUT81 Subroutine](#the-aput81-subroutine) + - [2-Dimensional, 8-Bit Arrays](#the-dim82-macro) + - [The DIM82 Macro](#the-dim82-macro) + - [The ADIM82 Subroutine](#the-adim82-subroutine) + - [The GET82 Macro](#the-get82-macro) + - [The AGET82 Subroutine](#the-aget82-subroutine) + - [The PUT82 Macro](#the-put82-macro) + - [The APUT82 Subroutine](#the-aput82-subroutine) + - [16-Bit Arrays](#1-dimensional-16-bit-arrays) + - [1-Dimensional, 16-Bit Arrays](#1-dimensional-16-bit-arrays) + - [The DIM161 Macro](#the-dim161-macro) + - [The ADIM161 Subroutine](#the-adim161-subroutine) + - [The GET161 Macro](#the-get161-macro) + - [The AGET161 Subroutine](#the-aget161-subroutine) + - [The PUT161 Macro](#the-put161-macro) + - [The APUT161 Subroutine](#the-aput161-subroutine) + - [2-Dimensional, 16-Bit Arrays](#2-dimensional-16-bit-arrays) + - [The DIM162 Macro](#the-dim162-macro) + - [The ADIM162 Subroutine](#the-adim162-subroutine) + - [The GET162 Macro](#the-get162-macro) + - [The AGET162 Subroutine](#the-aget162-subroutine) + - [The PUT162 Macro](#the-put162-macro) + - [The APUT162 Subroutine](#the-aput162-subroutine) +- [Part II: The Arrays Collection Demo File](#part-ii-the-arrays-collection-demo-file) + + + + + +--- + + + +## Part I: The Arrays Collection + + + +The third disk in the AppleIIASM library is dedicated to using 8-bit and 16-bit arrays with either one or two dimensions. The number of bits assigned refers to the number of possible elements: 8-bit arrays can have up to 255 elements in each dimension, with each element having a possible size of 255 bytes, whereas 16-bit arrays can have up to 65,025 elements in each dimension. When possible, 8-bit arrays should be used to save both cycles and bytes. Additionally, it should be apparent that a single 16-bit arrays could easily encompass more memory than is available in most Apple II computers; obviously, care needs to be taken not to fill up memory with a gigantic array. + +There are four types of arrays supported: 8-bit, 1-dimensional arrays, 8-bit, 2-dimensional arrays, 16-bit, 1-dimensional arrays and 16-bit, 2-dimensional arrays. Each type has three main macros (and subroutines) for data manipulation: a DIM command that initializes the array, a GET command that retrieves a value from the array, and a PUT command that enters a value into an array. While more functionality might be desired, these commands for each array type take up most of the disk space available; thus, a new disk would be necessary for additional commands. These may be added, but not until the entire library is nearing completion. + + + +--- + + + +## Arrays Components + + + +The Arrays collection contains the following components: + +- A header file with various hooks, vectors and definitions for the rest of the library as well as use by the end programmer. +- Macros and subroutines for each of the four types of arrays provided. Functionality is given for declaring an array, getting data from an array, and putting data into an array. +- A Demonstration of the macros and subroutines in use. + + + +--- + + + +## Arrays Header File + +Currently, the Arrays header file includes an unused variable declaration only, which is present in the file only to prevent an error from being returned by the Merlin Pro 8 assembler. This is included to keep the same structure as other disks; eventually, more may be added to the header that is necessary for the working of the rest of the collection. For instance, all subroutines use a multiplication implementation; this can be moved to the header, saving space on the disk. + + + +| Condition | Value | +| ------------- | ------------------------------------------------------------ | +| Name | File: HEAD.ARRAYS.ASM | +| Type | Header File | +| Author | Nathan Riggs | +| Last Revision | 19-MAR-2021 | +| Assembler | Merlin 8 Pro | +| OS | Apple DOS 3.3 | +| Purpose | Provide appropriate hooks and routines for the Arrays collection | +| Dependencies | none | +| Bytes | 0 | +| Notes | Repeatedly used subroutines by the library may be placed here in the future | +| See Also | | + +--- + +*DETAILS* + +As with all files in the collection, this header file includes a short heading that provides contact information, date last changed, number of bytes, a short description, etc. + +`Listing 3.0: HEAD.ARRAYS Source` + +```assembly +* +*``````````````````````````````* +* HEAD.ARRAYS.ASM * +* * +* CURRENTLY, THIS HEADER FILE * +* ONLY CONTAINS DUMMY CODE IN * +* ORDER TO PREVENT AN ERROR * +* DURING ASSEMBLY (EMPTY * +* FILE). * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 18-MAR-2021 * +* ASSEMBLER: MERLIN 8 PRO * +* OS: DOS 3.3 * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +ARRMAX EQU 8192 ; MAXIMUM # OF BYTES + ; AN ARRAY CAN USE +* + +``` + + + +--- + + + +## Macros and Subroutines + + + +The Arrays Collection is separated into four different macro files: 8-bit, single dimension arrays; 8-bit, double dimension arrays; 16-bit, single dimension arrays and 16-bit double dimension arrays. These are all independent of one another, but follow similar syntax and data structures. If possible, 8-bit arrays should be used in lieu of 16-bit arrays due to the extra cycles needed to calculate larger numbers for 16-bit values. + + + +--- + + + +### 1-Dimensional, 8-Bit Arrays + +These macros and subroutines are dedicated to using arrays with a maximum of 255 elements per dimension (in this case, a single dimension), with each element being up to 255 bytes in length. The macros for these arrays are located in the MAC.ARR8B1D.ASM file, named so after the types of macros it includes. + +`LISTING 3.10: MAC.ARR8B1D.ASM Header Source` + +```assembly +* +*``````````````````````````````* +* MAC.ARR8B1D.ASM * +* * +* LIBRARY OF MACROS FOR 8-BIT, * +* 1-DIMENSIONAL ARRAYS. * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 18-MAR-2021 * +* ASSEMBLER: MERLIN 8 PRO * +* OS: DOS 3.3 * +* * +* SUBROUTINE FILES USED * +* * +* SUB.ADIM81 * +* SUB.AGET81 * +* SUB.APUT81 * +* * +* LIST OF MACROS * +* * +* DIM81: DIM 1D, 8BIT ARRAY * +* GET81: GET ELEMENT IN 8BIT, * +* 1D ARRAY. * +* PUT81: PUT VALUE INTO 8BIT, * +* 1D ARRAY AT INDEX * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* + +``` + + + +--- + + + +### THE DIM81 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `DIM81` | +| Type | Macro | +| File | `MAC.ARR8B1D.ASM` | +| Author | Nathan Riggs | +| Last Revision | 18-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Initialize an 8-bit, 1-dimensional array | +| Input | ]1 = Address of Array
]2 = Number of Elements
]3 = Size of each Element
]4 = Default Fill Value | +| Output | none | +| Dependencies | `SUB.ADIM81.ASM` | +| Flags Destroyed | NZCV | +| Cycles | 234+ | +| Bytes | 146 | +| Notes | None | +| See Also | `ADIM81` `GET81` `AGET81` `PUT81` `APUT81` | + +--- + +*DETAILS* + +The `DIM81` macro initializes an array that has one dimension and can have up to 255 elements, with each element holding up to a possible 255 bytes. This must be called before any array of its type can be used, as it sets up the data structure used by the `GET81` and `PUT81` macros. This data structure is fairly simple: the first byte of the array holds the number of elements in the array, and the second byte holds the size of each element. Afterwards, the accessible data of the array follows in linear order: element 1 is followed by element 2, 2 by 3, and so on until each element is accounted for. + +Note that the entire block of memory used by the array is initialized once calling DIM81. Without care, this can wipe out data or a subroutine that is already held in memory. **`DIM81` does not check to see what it overwrites**; in fact, it would be incapable of doing so. To be safe, you should always take care to calculate the size of the array yourself, either in code or calculated prior to coding. This can be done by multiplying the element length by the number of elements, then adding two for the preceding bytes that store the length of the array and element size. + + + +`LISTING 3.11: DIM81 Source` + +```assembly +* +*``````````````````````````````* +* DIM81 (NATHAN RIGGS) * +* * +* CREATE A ONE DIMENSIONAL, * +* 8-BIT ARRAY AT THE GIVEN * +* ADDRESS. * +* * +* PARAMETERS * +* * +* ]1 = ARRAY ADDRESS * +* ]2 = ARRAY BYTE LENGTH * +* ]3 = ELEMENT BYTE LENGTH * +* ]4 = FILL VALUE * +* * +* CYCLES: 234+ * +* SIZE: 146 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +DIM81 MAC + _MLIT ]1;WPAR1 ; {16C12B} PARSE IF LITERAL OR NOT + LDA ]2 ; {3C2B} ARRAY LENGTH + STA WPAR2 ; {3C2B} + LDA ]3 ; {3C2B} ELEMENT LENGTH + STA WPAR3 ; {3C2B} + LDA ]4 ; {3C2B} + STA BPAR1 ; {3C2B} FILL VAL + JSR ADIM81 ; {200C132B} + <<< +* + +``` + + + +--- + + + +### THE ADIM81 SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `ADIM81` | +| Type | Subroutine | +| File | `SUB.ADIM81.ASM` | +| Author | Nathan Riggs | +| Last Revision | 18-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Initialize an 8-bit, 1-dimensional array | +| Input | WPAR1 = Array Address
WPAR2 = Number of Elements
WPAR3 = Element Length
BPAR1 = Default Fill Value | +| Output | none | +| Dependencies | `SUB.ADIM81.ASM` | +| Flags Destroyed | NZCV | +| Cycles | 194+ | +| Bytes | 129 | +| Notes | None | +| See Also | `DIM81` `GET81` `AGET81` `PUT81` `APUT81` | + +--- + +*DETAILS* + +The `ADIM81` subroutine initializes an eight-bit, one-dimensional array stored at a given address, with its number of elements and the byte size of each element passed in the parameters, along with the default fill value for each byte of the array. The array created has a simple data structure: the first byte contains the number of elements and the second byte contains the length of each element, followed by the element data. It should be noted that `ADIM81` does not care about the data it overwrites in memory; the programmer must take care to make sure that important data or routines are not overwritten. + +`ADIM81` first multiplies the number of elements specified by the passed size of each element, then adds two to account for the total number of bytes needed for the array. Then, the space is cleared starting at the address given for the array and filled with the specified fill character. The total size of the array is then held in `RETURN` in case the programmer wants to save the array size for future reference. + + + +`LISTING 3.12: SUB.ADIM81.ASM Source` + +```assembly +* +*``````````````````````````````* +* ADIM81 (NATHAN RIGGS) * +* * +* DECLARE THE DIMENSIONS OF A * +* NEW 8BIT, 1D ARRAY. * +* * +*------------------------------* +* 8-BIT MULTIPLICATION CODE * +* ADAPTED FROM WHITE FLAME'S * +* WORK ON CODEBASE64. LICENSE * +* MAY VARY. * +*------------------------------* +* * +* INPUT * +* * +* WPAR1 = ARRAY ADDRESS * +* WPAR2 = # OF ELEMENTS * +* WPAR3 = LENGTH OF ELEMENTS * +* BPAR1 = FILL VALUE * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 194+ * +* SIZE: 129 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]ADDR EQU WPAR1 ; ARRAY ADDRESS +]ASIZE EQU WPAR2 ; NUMBER OF ELEMENTS +]ESIZE EQU WPAR3 ; ELEMENT SIZE, IN BYTES +]FILL EQU BPAR1 ; DEFAULT FILL VALUE +* +]MSIZE EQU VARTAB ; TOTAL BYTES OF ARRAY +]ASZBAK EQU VARTAB+4 ; ARRAY SIZE BACKUP +]ESZBAK EQU VARTAB+6 ; ELEMENT SIZE BACKUP +* +ADIM81 + LDA ]ESIZE ; {2C2B} LOAD ELEMENT SIZE + STA ]ESZBAK ; {3C2B} SAVE A BACKUP + LDA ]ASIZE ; {2C2B} LOAD ARRAY SIZE + STA ]ASZBAK ; {3C2B} SAVE A BACKUP + LDA #0 ; {2C2B} RESET .A + STA ]ASIZE+1 ; {3C2B} CLEAR ARRAY SIZE HIGH BYTE + STA ]ASZBAK+1 ; {3C2B} CLEAR ARRAY SIZE BACKUP HIGH BYTE +* +** MULTIPLY ARRAY SIZE BY ELEMENT SIZE +* + LDY #0 ; {2C2B} RESET HIBYTE FOR MULTIPLY + TYA ; {3C2B} RESET LOBYTE FOR MULTIPLY + LDY ]ASIZE+1 ; {2C2B} + STY SCRATCH ; {3C2B} SAVE HIBYTE IN SCRATCH + BEQ :ENTLP ; {3C2B} IF ZERO, SKIP TO LOOP +:DOADD + CLC ; {2C1B} ADD ASIZE TO LOBYTE + ADC ]ASIZE ; {5C3B} + TAX ; {3C2B} TEMPORARILY STORE IN .X + TYA ; {3C2B} TRANSFER HIBYTE TO .A + ADC SCRATCH ; {5C3B} ADD HIBYTE + TAY ; {3C2B} STORE BACK IN .Y + TXA ; {3C2B} LOAD LOBYTE IN .A AGAIN +:LP ; LOOP START + ASL ]ASIZE ; {6C3B} MULTIPLY ASIZE BY 2 + ROL SCRATCH ; {6C3B} MULTIPLY HIBYTE BY 2 +:ENTLP + LSR ]ESIZE ; {6C3B} DIVIDE ESIZE BY 2 + BCS :DOADD ; {3C2B} IF >= LOBYTE IN .A, ADD AGAIN + BNE :LP ; {3C2B} OTHERWISE, RELOOP +* + STX ]MSIZE ; {3C2B} STORE LOBYTE + STY ]MSIZE+1 ; {3C2B} STORE HIBYTE + LDA ]MSIZE ; {2C2B} NOW ADD TO BYTES + CLC ; {2C1B} TO MSIZE FOR ARRAY HEADER + ADC #2 ; {3C2B} + STA ]MSIZE ; {3C2B} STORE LOBYTE + LDA ]MSIZE+1 ; {2C2B} + ADC #0 ; {3C2B} CARRY FOR HIBYTE + STA ]MSIZE+1 ; {3C2B} +* +** NOW CLEAR MEMORY BLOCKS +* + LDA ]FILL ; {2C2B} GET FILL VALUE + LDX ]MSIZE+1 ; {2C2B} X = # O PAGES TO DO + BEQ :PART ; {3C2B} BRANCH IF HIBYTE = 0 + LDY #0 ; {2C2B} RESET INDEX +:FULL + STA (]ADDR),Y ; {3C2B} FILL CURRENT BYTE + INY ; {2C1B} INCREMENT INDEX + BNE :FULL ; {3C2B} LOOP UNTIL PAGE DONE + INC ]ADDR+1 ; {5C2B} GO TO NEXT PAGE + DEX ; {2C1B} DECREMENT COUNTER + BNE :FULL ; {3C2B} LOOP IF PAGES LEFT +:PART + LDX ]MSIZE ; {2C2B} PARTIAL PAGE BYTES + BEQ :MFEXIT ; {3C2B} EXIT IF LOBYTE = 0 + LDY #0 ; {2C2B} RESENT INDEX +:PARTLP + STA (]ADDR),Y ; {3C2B} STORE VAL + INY ; {2C1B} INCREMENT INDEX + DEX ; {2C1B} DECREMENT COUNTER + BNE :PARTLP ; {3C2B} LOOP UNTIL DONE +:MFEXIT + LDY #0 ; {2C2B} STORE NUMBER OF ELEMENTS + LDA ]ASZBAK ; {2C2B} INTO FIRST BYTE OF ARRAY + STA (]ADDR),Y ; {6C2B} + INY ; {2C1B} + LDA ]ESZBAK ; {2C2B} STORE ELEMENT SIZE INTO + STA (]ADDR),Y ; {6C2B} SECOND BYTE OF ARRAY + LDX ]ADDR ; {2C2B} GET LOBYTE OF ARRAY ADDRESS + LDY ]ADDR+1 ; {2C2B} AND HIBYTE TO RETURN IN .X, .Y + LDA ]ASZBAK ; {2C2B} RETURN NUMBER OF ELEMENTS IN .A + LDA ]MSIZE ; {2C2B} STORE TOTAL ARRAY SIZE + STA RETURN ; {3C2B} IN RETURN + LDA ]MSIZE+1 ; {2C2B} + STA RETURN+1 ; {3C2B} + LDA #2 ; {2C2B} SET RETURN LENGTH TO + STA RETLEN ; {3C2B} 2 BYTES + RTS ; {6C1B} + +``` + + + +--- + + + +### THE GET81 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | --------------------------------------------- | +| Name | `GET81` | +| Type | Macro | +| File | `MAC.ARR8B1D.ASM` | +| Author | Nathan Riggs | +| Last Revision | 18-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Initialize an 8-bit, 1-dimensional array | +| Input | ]1 = Address of Array
]2 = Element Index | +| Output | none | +| Dependencies | `SUB.AGET81.ASM` | +| Flags Destroyed | NZCV | +| Cycles | 193+ | +| Bytes | 125 | +| Notes | None | +| See Also | `DIM81` `ADIM81` `AGET81` `PUT81` `APUT81` | + +--- + +*DETAILS* + +The `GET81` macro retrieves an element's value from an eight-bit, one-dimensional array. Parameters are passed via the registers, and the result is passed back via `RETURN`. The `RETLEN` byte holds the length of the value in `RETURN`. Additionally, the element address is returned in the **.X** (low byte) and **.Y** (high byte) registers, while the length of the return value is also stored in **.A**. + + + +`LISTING 3.13: The GET81 Macro` + +```assembly +* +*``````````````````````````````* +* GET81 (NATHAN RIGGS) * +* * +* RETRIEVE A VALUE FROM THE * +* GIVEN ARRAY AT THE SPECIFIED * +* ELEMENT INDEX AND STORE THE * +* VALUE IN RETURN. * +* * +* PARAMETERS * +* * +* ]1 = ARRAY ADDRESS * +* ]2 = ELEMENT INDEX * +* * +* CYCLES: 193+ * +* SIZE: 125 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +GET81 MAC + _AXLIT ]1 ; {8C6B} PARSE ADDRESS + LDY ]2 ; {3C2B} ELEMENT INDEX + JSR AGET81 ; {182C117B} + <<< +* + +``` + + + +--- + + + +### THE AGET81 SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `AGET81` | +| Type | Subroutine | +| File | `SUB.AGET81.ASM` | +| Author | Nathan Riggs | +| Last Revision | 18-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Initialize an 8-bit, 1-dimensional array | +| Input | .A = Array Address Low Byte
.X = Array Address High Byte
.Y = Array Element Index | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 176+ | +| Bytes | 114 | +| Notes | None | +| See Also | `DIM81` `ADIM81` `GET81` `PUT81` `APUT81` | + +--- + +*DETAILS* + +The `AGET81` subroutine accepts an array address via the **.X** (low byte) and **.Y** (high byte) registers and an element index in the **.A** register and returns the value of the element specified in the given array. The element data is held in `RETURN` after execution, with its length held in both `RETLEN` and **.A**. The memory address of the element is also returned back via the **.X** (low byte) and **.Y** (high byte) registers. This address is calculated by multiplying the passed index by the element length, adding two for the preceding length bytes, then added to the address of the array itself. + + + +`LISTING 3.14: The AGET81 Subroutine Source` + +```assembly +* +*``````````````````````````````* +* AGET81 (NATHAN RIGGS) * +* * +* RETRIEVE A VALUE AT A GIVEN * +* ARRAY INDEX AND STORE IN * +* RETURN AREA WITH APPROPRIATE * +* RETURN LENGTH (RETLEN). * +* * +*------------------------------* +* 8-BIT MULTIPLICATION CODE * +* ADAPTED FROM WHITE FLAME'S * +* WORK ON CODEBASE63. LICENSE * +* MAY VARY. * +*------------------------------* +* * +* INPUT: * +* * +* .A = ARRAY ADDRESS LOBYTE * +* .X = ARRAY ADDRESS HIBYTE * +* .Y = ARRAY ELEMENT INDEX * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 176+ * +* SIZE: 114 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]RES EQU VARTAB ; MATH RESULTS +]IDX EQU VARTAB+2 ; ELEMENT INDEX +]ESIZE EQU VARTAB+4 ; ELEMENT SIZE +]ALEN EQU VARTAB+5 ; NUMBER OF ELEMENTS +* +AGET81 + STA ADDR1 ; {3C2B} .A HOLDS ARRAY ADDRESS LOBYTE + STX ADDR1+1 ; {3C2B} .X HOLDS ADDRESS HIBYTE + STY ]IDX ; {3C2B} .Y HOLDS THE INDEX + LDA #0 ; {2C2B} CLEAR INDEX HIBYTE + STA ]IDX+1 ; {3C2B} + LDY #1 ; {2C2B} GET ELEMENT SIZE FROM ARRAY + LDA (ADDR1),Y ; {6C2B} HEADER + STA ]ESIZE ; {3C2B} + STA RETLEN ; {3C2B} STORE IN RETLEN + DEY ; {2C1B} MOVE TO BYTE 0 OF HEADER + LDA (ADDR1),Y ; {6C2B} GET NUMBER OF ELEMENTS + STA ]ALEN ; {3C2B} FROM THE ARRAY HEADER +* +** MULTIPLY INDEX BY ELEMENT SIZE, ADD 2 +* + TYA ; {2C1B} Y ALREADY HOLDS ZERO + STY SCRATCH ; {3C2B} RESET LO AND HI TO 0 + BEQ :ENTLP ; {3C2B} IF ZERO, SKIP TO LOOP +:DOADD + CLC ; {2C1B} CLEAR CARRY FLAG + ADC ]IDX ; {4C3B} ADD INDEX LOBYTE + TAX ; {2C1B} TEMPORARILY STORE IN .X + TYA ; {2C1B} TRANSFER HIBYTE TO .A + ADC SCRATCH ; {4C3B} ADD HIBYTE + TAY ; {2C1B} STORE BACK INTO .Y + TXA ; {2C1B} RELOAD LOBYTE IN .A +:LP + ASL ]IDX ; {6C3B} MULTIPLY INDEX BY TWO + ROL SCRATCH ; {6C3B} ADJUST HIBYTE CARRY +:ENTLP + LSR ]ESIZE ; {6C3B} DIVIDE ELEMENT SIZE BY 2 + BCS :DOADD ; {3C2B} IF >= LOBYTE IN .A, ADD AGAIN + BNE :LP ; {3C2B} +* + STX ]IDX ; {3C2B} STORE LOBYTE + STY ]IDX+1 ; {3C2B} STORE HIBYTE + CLC ; {2C1B} CLEAR CARRY + LDA #2 ; {2C2B} ADD 2 BYTES TO INDEX + ADC ]IDX ; {4C3B} TO ACCOUNT FOR ARRAY HEADER + STA ]RES ; {3C2B} AND STORE IN RESULT + LDA #0 ; {2C2B} ACCOUNT FOR HIBYTE CARRY + ADC ]IDX+1 ; {4C3B} + STA ]RES+1 ; {3C2B} +* +** NOW ADD TO BASE ADDRESS TO GET ELEMENT ADDRESS +* + CLC ; {2C1B} CLEAR CARRY FLAG + LDA ]RES ; {2C2B} LOAD RESULT FROM EARLIER + ADC ADDR1 ; {4C3B} ADD ARRAY ADDRESS LOBYTE + STA ]RES ; {3C2B} STORE BACK IN RESULT + LDA ]RES+1 ; {2C2B} LOAD PRIOR RESULT HIBYTE + ADC ADDR1+1 ; {4C3B} ADD ARRAY ADDRESS HIBYTE + STA ]RES+1 ; {3C2B} STORE BACK IN RESULT HIBYTE +* +** NOW MOVE ELEMENT DATA TO RETURN LOCATION +* + LDY #0 ; {2C2B} RESENT INDEX + LDA ]RES ; {2C2B} LOAD ADDRESS LOBYTE + STA ADDR1 ; {3C2B} PUT IN ZERO PAGE POINTER + LDA ]RES+1 ; {2C2B} GET RESULT HIBYTE + STA ADDR1+1 ; {3C2B} PUT IN ZERO PAGE POINTER +:LDLOOP + LDA (ADDR1),Y ; {2C2B} LOAD BYTE FROM ELEMENT + STA RETURN,Y ; {3C2B} STORE IN RETURN + INY ; {2C1B} INCREASE BYTE INDEX + CPY RETLEN ; {4C2B} IF .Y <= ELEMENT SIZE + BCC :LDLOOP ; {3C2B} CONTINUE LOOPING + BEQ :LDLOOP ; {3C2B} KEEP LOOPING +* + LDX ]RES ; {2C2B} RETURN ELEMENT ADDRESS + LDY ]RES+1 ; {2C2B} IN .X (LOBYTE) AND .Y (HI) + LDA RETLEN ; {2C2B} RETURN ELEMENT LENGTH IN .A + RTS ; {6C1B} + +``` + + + +--- + + + +### THE PUT81 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `PUT81` | +| Type | Macro | +| File | `MAC.ARR8B1D.ASM` | +| Author | Nathan Riggs | +| Last Revision | 18-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Initialize an 8-bit, 1-dimensional array | +| Input | ]1 = Address of Source Value
]2 = Array Address
]3 = Element Index | +| Output | none | +| Dependencies | `SUB.APUT81.ASM` | +| Flags Destroyed | NZCV | +| Cycles | 216+ | +| Bytes | 131 | +| Notes | None | +| See Also | `DIM81` `ADIM81` `GET81` `AGET81` `APUT81` | + +--- + +*DETAILS* + +The `PUT81` macro inserts a value into an array's specified element, copied from another memory address. This last part is important, as it does depart in some ways from many other routines: `PUT81` **only** accepts an address, and that address either 1) is passed as a literal, meaning that the value anticipated is located at the address (direct), or 2) is passed as a regular address, meaning that the address itself holds another address that points to where the value is held (indirect). Currently, there is no way to pass a literal value to `PUT81`; all values must be held in an address prior to calling the macro. This may change in later revisions, but it is more likely that an extra macro and subroutine pair will be created for placing literal values into an element. + + + +`LISTING 3.15: The PUT81 Macro Source` + +```assembly +* +*``````````````````````````````* +* PUT81 (NATHAN RIGGS) * +* * +* PUTS THE DATA FOUND AT THE * +* GIVEN ADDRESS INTO THE ARRAY * +* AT THE GIVEN INDEX. * +* * +* PARAMETERS * +* * +* ]1 = SOURCE ADDRESS * +* ]2 = ARRAY ADDRESS * +* ]3 = ELEMENT INDEX * +* * +* CYCLES: 216+ * +* SIZE: 131 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +PUT81 MAC + _MLIT ]1;WPAR1 ; {16C12B} PARSE SOURCE ADDRESS + _MLIT ]2;WPAR2 ; {16C12B} PARSE DEST ADDRESS + LDA ]3 ; {3C2B} DEST INDEX + STA BPAR1 ; {3C2B} + JSR APUT81 ; {178C103B} + <<< +* + +``` + + + +--- + + + +### THE APUT81 SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `APUT81` | +| Type | Subroutine | +| File | `SUB.APUT81.ASM` | +| Author | Nathan Riggs | +| Last Revision | 18-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Initialize an 8-bit, 1-dimensional array | +| Input | WPAR1 = Source Address
WPAR2 = Destination Array Address
BPAR1 = Element Index | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 172+ | +| Bytes | 100 | +| Notes | None | +| See Also | `DIM81` `ADIM81` `GET81` `AGET81` `PUT81` | + + + +--- + +*DETAILS* + +The `APUT81` subroutine accepts an address that contains a value to be transferred into the specified element of a given array, then copies that value appropriately. The length to be copied is determined by the array's element length, which should be already initialized by the `ADIM81` subroutine. Afterwards, `APUT81` returns the address of the element specified in **.X** (low byte) and **.Y** (high byte), with the element length held in **.A**. + + + +`LISTING 3.16: The APUT81 Subroutine Source` + +```assembly +* +*``````````````````````````````* +* APUT81 (NATHAN RIGGS) * +* * +* PUT DATA FROM SRC LOCATION * +* INTO 1D, 8BIT ARRAY AT THE * +* SPECIFIED ELEMENT. * +* * +*------------------------------* +* 8-BIT MULTIPLICATION CODE * +* ADAPTED FROM WHITE FLAME'S * +* WORK ON CODEBASE64. LICENSE * +* MAY VARY. * +*------------------------------* +* * +* INPUT: * +* * +* WPAR1 = SOURCE ADDRESS * +* WPAR2 = DESTINATION ADDRESS * +* BPAR1 = ARRAY INDEX * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 172+ * +* SIZE: 100 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]ADDRS EQU WPAR1 ; SOURCE ADDRESS +]ADDRD EQU WPAR2 ; DESTINATION +]AIDX EQU BPAR1 ; ARRAY INDEX +]SCRATCH EQU ADDR1 ; ZEROED HIBYTE +* +]ESIZE EQU VARTAB ; ELEMENT SIZE +]ESIZEBK EQU VARTAB+1 ; ^BACKUP +]ASIZE EQU VARTAB+2 ; # OF ELEMENTS +]IDX EQU VARTAB+5 ; INDEX +]RES EQU VARTAB+7 ; MULTIPLICATION RESULT +* +APUT81 + LDA ]AIDX ; {2C2B} STORE IN 2 LOCATIONS + STA ]IDX ; {3C2B} FOR A BACKUP LATER +* +** MULTIPLY INDEX BY ELEM SIZE AND ADD 2 +* + LDY #1 ; {2C2B} GET ELEMENT LENGTH FROM + LDA (]ADDRD),Y ; {6C2B} BYTE 1 OF ARRAY + STA ]ESIZE ; {3C2B} + STA ]ESIZEBK ; {3C2B} + LDY #0 ; {2C2B} RESET INDEX + LDA (]ADDRD),Y ; {6C2B} GET NUMBER OF ELEMENTS + STA ]ASIZE ; {3C2B} FROM ARRAY + TYA ; {2C1B} .A = 0 + STY ]SCRATCH ; {3C2B} LOBYTE = 0 + STY ]SCRATCH+1 ; {3C2B} HIBYTE = 0 + BEQ :ENTLPA ; {3C2B} IF 0, SKIP TO LOOP +:DOADD + CLC ; {2C1B} CLEAR CARRY FLAG + ADC ]AIDX ; {4C2B} ADD INDEX LOBYTE + TAX ; {2C1B} TEMPORARILY STORE IN .X + TYA ; {2C1B} TRANSFER HIBYTE TO .A + ADC ]SCRATCH ; {4C2B} ADD HIBYTE + TAY ; {2C1B} STORE BACK IN .Y + TXA ; {2C1B} RELOAD LOBYTE TO .A +:LPA + ASL ]AIDX ; {6C3B} MUL INDEX BY TWO + ROL ]SCRATCH ; {6C3B} ADJUST HIBYTE CARRY +:ENTLPA + LSR ]ESIZE ; {6C3B} DIVIDE ELEMENT SIZE BY 2 + BCS :DOADD ; {3C2B} IF >= LOBYTE IN .A, ADD AGAIN + BNE :LPA ; {3C2B} + STX ]IDX ; {3C2B} STORE LOBYTE + STY ]IDX+1 ; {3C2B} STORE HIBYTE + CLC ; {2C1B} CLEAR CARRY FLAG + LDA #2 ; {2C2B} ADD 2 BYTES TO INDEX + ADC ]IDX ; {3C2B} TO ACCOUNT FOR HEADER + STA ]RES ; {3C2B} STORE LOBYTE + LDA #0 ; {2C2B} ACCOUNT FOR HIBYTE CARRY + ADC ]IDX+1 ; {3C2B} + STA ]RES+1 ; {3C2B} +* +** ADD RESULT TO ARRAY ADDRESS TO GET ELEMENT ADDR +* + CLC ; {2C1B} CLEAR CARRY FLAG + LDA ]RES ; {3C2B} LOAD RESULT FROM EARLIER + ADC ]ADDRD ; {3C2B} ADD ARRAY ADDRESS LOBYTE + STA ]RES ; {3C2B} STORE BACK IN RESULT + LDA ]RES+1 ; {3C2B} ADD ARRAY ADDRESS HIBYTE + ADC ]ADDRD+1 ; {3C2B} + STA ]RES+1 ; {3C2B} STORE HIBYTE +* + STA ]ADDRD+1 ; {3C2B} STORE IN ZERO PAGE HIBYTE + LDA ]RES ; {3C2B} STORE LOBYTE TO ZERO PAGE + STA ]ADDRD ; {3C2B} +* +** COPY FROM SRC ADDR3 TO ELEMENT LOCATION ADDR +* +:LP + LDA (]ADDRS),Y ; {6C2B} LOAD BYTE FROM SOURCE + STA (]ADDRD),Y ; {6C2B} STORE IN ELEMENT ADDRESS + INY ; {2C1B} INCREASE BYTE INDEX + CPY ]ESIZEBK ; {4C3B} COMPARE TO ELEMENT SIZE + BNE :LP ; {3C2B} IF !=, KEEP COPYING +* + LDY ]ADDRD+1 ; {3C2B} .Y = ELEMENT ADDRESS HIBYTE + LDX ]ADDRD ; {3C2B} .X = LOBYTE + LDA ]ESIZE ; {3C2B} .A = ELEMENT SIZE + RTS ; {6C1B} + +``` + + + +--- + + + +### 2-Dimensional, 8-Bit Arrays + +These macros and subroutines are dedicated to using arrays with a maximum of 255 elements per dimension (in this case, two dimensions), with each element being up to 255 bytes in length. The macros for these arrays are located in the MAC.ARR8B2D.ASM file, named so after the types of macros it includes (**8** **B**ytes, **2 D**imensions). + +`LISTING 3.20: MAC.ARR8B2D.ASM Header Source` + +```assembly +* +*``````````````````````````````* +* MAC.ARR8B2D.ASM * +* * +* A MACRO LIBRARY FOR 8BIT, * +* 2-DIMENSIONAL ARRAYS. * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 18-MAR-2021 * +* ASSEMBLER: MERLIN 8 PRO * +* OS: DOS 3.3 * +* * +* SUBROUTINE FILES USED * +* * +* SUB.ADIM82 * +* SUB.AGET82 * +* SUB.APUT82 * +* * +* LIST OF MACROS * +* * +* DIM82: DIM A 2D, 8BIT ARRAY * +* GET82: GET ELEMENT IN 8BIT, * +* 2D ARRAY * +* PUT82: PUT VALUE INTO 8BIT, * +* 2D ARRAY AT INDEX * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* + +``` + + + +--- + + + +### THE DIM82 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `DIM82` | +| Type | Macro | +| File | `MAC.ARR8B2D.ASM` | +| Author | Nathan Riggs | +| Last Revision | 18-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Initialize an 8-bit, 2-dimensional array | +| Input | ]1 = Array Address
]2 = First Dimension Length
]3 = Second Dimension Length
]4 = Default Fill Value | +| Output | none | +| Dependencies | `SUB.ADIM82.ASM` | +| Flags Destroyed | NZCV | +| Cycles | 355+ | +| Bytes | 228 | +| Notes | None | +| See Also | `ADIM82` `GET82` `AGET82` `PUT82` `APUT82` | + +--- + +*DETAILS* + +The `DIM82` macro initializes a two-dimensional array that can hold up to 255 elements in each dimension, with a possible element length up to 255 bytes. This macro must be used before `GET82` or `PUT82` can be used, as those macros (and their respective subroutines) expect the data structure that `DIM82` puts into place. This data structure is fairly simple: first, the first dimension (often referred to as the **X** dimension) length is stored at the array's base address, followed by the length of the second dimension (also known as the **Y** dimension). After these two values, which encompass a byte each, the size of the elements is recorded in the third byte. The actual data stored in the array is then placed after these preceding bytes in linear order. After being called, all elements should contain the fill value specified. + +Note that `DIM82` can be dangerous due to the fact that there is no value-checking of the address locations it overwrites; if the programmer is not careful, critical data or even commands could be overwritten by the macro. A four-byte value is placed in `RETURN` that indicates the total amount of memory that the array fills, but it is more sensible to know this value *before* declaring the array. To do so, simply multiply the number of elements in the first dimension by the number of elements in the second dimension, then multiply that product by the element length. Add three to this value to account for the preceding length bytes of the array, and you will then have the number of bytes the array will take as a whole. + + + +`LISTING 3.21: The DIM82 Macro Source` + +```assembly +* +*``````````````````````````````* +* DIM82 (NATHAN RIGGS) * +* * +* INITIALIZES AN 8-BIT ARRAY * +* WITH TWO DIMENSIONS. * +* * +* PARAMETERS * +* * +* ]1 = ARRAY ADDRESS * +* ]2 = X DIMENSION * +* ]3 = Y DIMENSION * +* ]4 = ELEMENT SIZE * +* ]5 = FILL VALUE * +* * +* CYCLES: 355+ * +* SIZE: 228 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +DIM82 MAC + _MLIT ]1;WPAR1 ; {16C12B} PARSE ARRAY ADDRESS + LDA ]2 ; {3C2B} X DIM + STA WPAR2 ; {3C2B} + LDA ]3 ; {3C2B} Y DIM + STA WPAR3 ; {3C2B} + LDA ]4 ; {3C2B} ELEMENT LENGTH + STA BPAR2 ; {3C2B} + LDA ]5 ; {3C2B} FILL VAL + STA BPAR1 ; {3C2B} + JSR ADIM82 ; {315C200B} + <<< +* + +``` + + + +--- + + + +### THE ADIM82 SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `ADIM82` | +| Type | Subroutine | +| File | `SUB.ADIM82.ASM` | +| Author | Nathan Riggs | +| Last Revision | 18-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Initialize an 8-bit, 2-dimensional array | +| Input | WPAR1 = Array Address
WPAR2 = First Dimension Length
WPAR3 = Second Dimension Length
BPAR1 = Default Fill Value
BPAR2 = Element Length in Bytes
| +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 309+ | +| Bytes | 197 | +| Notes | None | +| See Also | `DIM82` `GET82` `AGET82` `PUT82` `APUT82` | + +--- + +*DETAILS* + +The `ADIM82` Subroutine creates an array data structure with two dimensions starting at the given address, with a maximum number of elements per dimension of 255. Elements can be specified to be between 1 and 255 bytes long, and a default value is passed that will fill every byte of the array's data. It should be noted that no protections are given against `ADIM82` writing over pre-existing data or commands in memory, and thus should be used with care. The method used for finding the length of an eight-bit, two-dimensional array can be found in the `DIM82` macro listing. + + + +`LISTING 3.22: SUB.ADIM82.ASM Subroutine Source` + +```assembly +* +*``````````````````````````````* +* ADIM82 (NATHAN RIGGS) * +* * +* DECLARE THE DIMENSIONS OF A * +* NEW 8-BIT, 2D ARRAY. * +* * +*------------------------------* +* 8-BIT MULTIPLICATION CODE * +* ADAPTED FROM WHITE FLAME'S * +* WORK ON CODEBASE64. LICENSE * +* MAY VARY. * +*------------------------------* +* * +* INPUT: * +* * +* WPAR1 = ARRAY ADDRESS * +* WPAR2 = 1ST DIM LENGTH * +* WPAR3 = 2ND DIM LENGTH * +* BPAR1 = FILL VALUE * +* BPAR2 = ELEMENT LENGTH * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 309+ * +* SIZE: 197 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]ADDR EQU WPAR1 ; ARRAY ADDRESS +]AXSIZE EQU WPAR2 ; FIRST DIM # OF ELEMENTS +]AYSIZE EQU WPAR3 ; SECOND DIM # OF ELEMENTS +]FILL EQU BPAR1 ; FILL VALUE +]ESIZE EQU BPAR2 ; ELEMENT SIZE +* +]PROD EQU VARTAB ; PRODUCT +]AXBAK EQU VARTAB+4 ; ARRAY X SIZE BACKUP +]AYBAK EQU VARTAB+5 ; ARRAY Y SIZE BACKUP +]MLIER EQU VARTAB+6 ; MULTIPLIER +]MCAND EQU VARTAB+8 ; MULTIPLICAND, ELEMENT SIZE +* +ADIM82 + LDA ]ESIZE ; {2C2B} ELEMENT LENGTH + STA ]MCAND ; {3C2B} AND STORE AS MULTIPLICAND + LDA ]AYSIZE ; {2C2B} GET ARRAY Y SIZE + STA ]AYBAK ; {3C2B} BACK IT UP + LDA ]AXSIZE ; {2C2B} + STA ]AXBAK ; {3C2B} AND BACK THAT UP TOO + LDA #0 ; {2C2B} CLEAR MCAND HIBYTE + STA ]MCAND+1 ; {3C2B} +* +** MULTIPLY X AND Y +* + TAY ; {2C1B} AND LOBYTE + STY SCRATCH ; {3C2B} + BEQ :ENTLP ; {3C2B} IF ZERO, SKIP TO LOOP +:DOADD + CLC ; {2C1B} CLEAR CARRY FLAG + ADC ]AXSIZE ; {5C3B} ADD X LENGTH + TAX ; {2C1B} TEMPORARILY STORE IN .X + TYA ; {2C1B} TRANSFER HIBYTE TO .A + ADC SCRATCH ; {5C3B} ADD HIBYTE + TAY ; {2C1B} STORE BACK IN .Y + TXA ; {2C1B} RELOAD LOBYTE INTO .A +:LP + ASL ]AXSIZE ; {6C3B} MULTIPLY X LENGTH BY 2 + ROL SCRATCH ; {6C3B} ADJUST HIBYTE +:ENTLP + LSR ]AYSIZE ; {6C3B} DIVIDE Y LENGTH BY 2 + BCS :DOADD ; {3C2B} IF >= LOBYTE IN .A, + BNE :LP ; {3C2B} ADD AGAIN; OTHERWISE, LOOP + STX ]MLIER ; {3C2B} STORE LOBYTE IN MULTIPLIER + STY ]MLIER+1 ; {3C2B} STORE HIBYTE IN MULTIPLIER +* +** NOW MULTIPLY BY LENGTH OF ELEMENTS +* + LDA #0 ; {2C2B} CLEAR PRODUCT LOBYTE + STA ]PROD ; {3C2B} + STA ]PROD+1 ; {3C2B} CLEAR NEXT BYTE + STA ]PROD+2 ; {3C2B} CLEAR NEXT BYTE + STA ]PROD+3 ; {3C2B} CLEAR HIBYTE + LDX #$10 ; {2C2B} LOAD $10 IN .X (#16) +:SHIFTR LSR ]MLIER+1 ; {6C3B} DIVIDE MLIER BY TWO + ROR ]MLIER ; {6C3B} ADJUST LOBYTE + BCC :ROTR ; {3C2B} IF LESS THAN PRODUCT, ROTATE + LDA ]PROD+2 ; {2C2B} LOAD PRODUCT 3RD BYTE + CLC ; {2C1B} CLEAR CARRY + ADC ]MCAND ; {5C3B} ADD MULTIPLICAND + STA ]PROD+2 ; {3C2B} STORE BACK INTO PRODUCT 3RD BYTE + LDA ]PROD+3 ; {2C2B} LOAD PRODUCT HIBYTE + ADC ]MCAND+1 ; {5C3B} ADD MULTIPLICAND HIBYTE +:ROTR + ROR ; {6C3B} ROTATE .A RIGHT + STA ]PROD+3 ; {3C2B} STORE IN PRODUCT HIBYTE + ROR ]PROD+2 ; {6C3B} ROTATE PRODUCT 3RD BYTE + ROR ]PROD+1 ; {6C3B} ROTATE PRODUCT 2ND BYTE + ROR ]PROD ; {6C3B} ROTATE PRODUCT LOBYTE + DEX ; {2C1B} DECREMENT COUNTER + BNE :SHIFTR ; {3C2B} IF NOT 0, BACK TO SHIFTER +* + LDA ]PROD ; {2C2B} LOAD PRODUCT LOBYTE TO .A + CLC ; {2C1B} CLEAR CARRY FLAG + ADC #3 ; {5C3B} ADD 3 + STA ]PROD ; {3C2B} STORE BACK INTO PRODUCT LOBYTE + LDA ]PROD+1 ; {2C2B} + ADC #0 ; {5C3B} INITIATE CARRY FOR 2ND BYTE + STA ]PROD+1 ; {3C2B} + LDA ]PROD+2 ; {2C2B} + ADC #0 ; {5C3B} AND THIRD BYTE + STA ]PROD+2 ; {3C2B} +* +** NOW CLEAR MEMORY BLOCKS, WHOLE PAGES FIRST +* + LDA ]FILL ; {2C2B} GET FILL VALUE + LDX ]PROD+1 ; {2C2B} LOAD SECOND BYTE OF PRODUCT + BEQ :PART ; {3C2B} IF 0, THEN ONLY PARTIAL PAGE + LDY #0 ; {2C2B} CLEAR INDEX +:FULL + STA (]ADDR),Y ; {3C2B} COPY FILL BYTE TO ADDRESS + INY ; {2C1B} INCREASE INDEX + BNE :FULL ; {3C2B} IF NO OVERFLOW, KEEP FILL + INC ]ADDR+1 ; {6C3B} INCREASE ADDRESS HIBYTE + DEX ; {2C1B} DECREMENT COUNTER + BNE :FULL ; {3C2B} LOOP UNTIL PAGES DONE +:PART + LDX ]PROD ; {2C2B} LOAD PRODUCT LOBYTE TO X + BEQ :MFEXIT ; {3C2B} IF ZERO, THEN WE'RE DONE + LDY #0 ; {2C2B} RESET INDEX +:PARTLP + STA (]ADDR),Y ; {6C2B} STORE FILL BYTE + INY ; {2C1B} INCREASE INDEX + DEX ; {2C1B} DECREASE COUNTER + BNE :PARTLP ; {3C2B} LOOP UNTIL DONE +:MFEXIT + LDY #0 ; {2C2B} RESET INDEX + LDA ]AXBAK ; {2C2B} PUT X LENGTH INTO + STA (]ADDR),Y ; {6C2B} FIRST BYTE OF ARRAY + INY ; {2C1B} INCREMENT INDEX + LDA ]AYBAK ; {2C2B} PUT Y LENGTH INTO + STA (]ADDR),Y ; {6C2B} SECOND BYTE OF ARRAY + INY ; {2C1B} INCREMENT INDEX + LDA ]MCAND ; {2C2B} PUT ELEMENT SIZE + STA (]ADDR),Y ; {6C2B} INTO 3RD BYTE OF ARRAY + LDX ]ADDR ; {2C2B} RETURN ARRAY ADDR LOBYTE IN .X + LDY ]ADDR+1 ; {2C2B} RETURN ARRAY ADDR HIBYTE IN .Y + LDA ]PROD ; {2C2B} STORE PRODUCT LOBYTE IN RETURN + STA RETURN ; {3C2B} + LDA ]PROD+1 ; {2C2B} STORE NEXT BYTE + STA RETURN+1 ; {3C2B} + LDA ]PROD+2 ; {2C2B} NEXT BYTE + STA RETURN+2 ; {3C2B} + LDA ]PROD+3 ; {2C2B} STORE HIBYTE + STA RETURN+3 ; {3C2B} + LDA #4 ; {2C2B} SIZE OF RETURN + STA RETLEN ; {3C2B} SPECIFY RETURN LENGTH + LDA ]MCAND ; {2C2B} RETURN ELEMENT SIZE IN .A + RTS ; {6C3B} + +``` + + + +--- + + + +### THE GET82 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `GET82` | +| Type | Macro | +| File | `MAC.ARR8B2D.ASM` | +| Author | Nathan Riggs | +| Last Revision | 18-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Retrieve an element from an 8-bit, 2-dimensional array | +| Input | ]1 = Array Address
]2 = First Dimension Index
3] = Second Dimension Index | +| Output | none | +| Dependencies | `SUB.AGET82.ASM` | +| Flags Destroyed | NZCV | +| Cycles | 340+ | +| Bytes | 255 | +| Notes | None | +| See Also | `DIM82` `ADIM82` `AGET82` `PUT82` `APUT82` | + +--- + +*DETAILS* + +The `GET82` macro retrieves a value from a given element at the X,Y position (first dimension, second dimension) in an eight-bit, two-dimensional array. The value retrieved is stored in `RETURN`, with the corresponding byte-length of the value stored in `RETLEN`. The length of the value is also passed back via the **.A** register, and the physical address where the element is stored is returned via the **.X** and **.Y** registers (low byte and high byte, respectively). + + + +`LISTING 3.23: The GET82 Macro Source` + +```assembly +* +*``````````````````````````````* +* GET82 (NATHAN RIGGS) * +* * +* RETRIEVE VALUE FROM ELEMENT * +* OF 8-BIT, TWO DIMENSIONAL * +* ARRAY. * +* * +* PARAMETERS * +* * +* ]1 = ARRAY ADDRESS * +* ]2 = X INDEX * +* ]3 = Y INDEX * +* * +* CYCLES: 340+ * +* SIZE: 222 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +GET82 MAC + _MLIT ]1;WPAR1 ; {16C12B} + LDA ]2 ; {3C2B} X INDEX + STA BPAR1 ; {3C2B} + LDA ]3 ; {3C2B} Y INDEX + STA BPAR2 ; {3C2B} + JSR AGET82 ; {312C192B} + <<< +* + +``` + + + +--- + + + +### THE AGET82 SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `AGET82` | +| Type | Subroutine | +| File | `SUB.AGET82.ASM` | +| Author | Nathan Riggs | +| Last Revision | 18-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Retrieve element from an 8-bit, 2-dimensional array | +| Input | WPAR1 = Array Address
BPAR1 = First Dimension Index
BPAR2 = Second Dimension Index | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 306+ | +| Bytes | 189 | +| Notes | None | +| See Also | `DIM82` `ADIM82` `GET82` `PUT82` `APUT82` | + +--- + +*DETAILS* + +The `AGET82` subroutine retrieves an element value from a two-dimension, eight-bit array, returning the value in `RETURN` with its length byte in `RETLEN`. Note that the array in question must first have been initialized with the `DIM82` macro or the `ADIM82` subroutine in order to work properly, unless the exact same data structure is created by the programmer without the aid of the `DIM` functionality. Note also that `GET82`, like all array subroutines, does not do any error-checking: requesting an element that is out-of-bounds will result in trash being returned, if the system does not crash in the first place. A programmer should always be careful to manage array boundaries on her own, like memory management in general. + + + +`LISTING 3.24: The AGET82 Subroutine Source` + +```assembly +* +*``````````````````````````````* +* AGET82 (NATHAN RIGGS) * +* * +* RETRIEVE AN ELEMENT VALUE * +* FROM AN 8-BIT, 2D ARRAY AND * +* HOLD IT IN THE RETURN * +* ADDRESS, WITH ITS LENGTH IN * +* RETLEN. * +* * +*------------------------------* +* 8-BIT MULTIPLICATION CODE * +* ADAPTED FROM WHITE FLAME'S * +* WORK ON CODEBASE64. LICENSE * +* MAY VARY. * +*------------------------------* +* * +* INPUT: * +* * +* WPAR1 = ARRAY ADDRESS * +* BPAR1 = 1ST DIM INDEX * +* BPAR2 = 2ND DIM INDEX * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 306+ * +* SIZE: 189 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]ADDR EQU WPAR1 ; ARRAY ADDRESS +]XIDX EQU BPAR1 ; 1ST DIMENSION INDEX +]YIDX EQU BPAR2 ; 2ND DIMENSION INDEX +* +]XLEN EQU VARTAB+0 ; X DIMENSION LENGTH +]YLEN EQU VARTAB+2 ; Y DIMENSION LENGTH +]PROD EQU VARTAB+4 ; PRODUCT +]MLIER EQU VARTAB+8 ; MULTIPLIER +]MCAND EQU VARTAB+10 ; MULTIPLICAND +]ELEN EQU VARTAB+12 ; ELEMENT LENGTH +]PBAK EQU VARTAB+14 ; PRODUCT BACKUP +* +AGET82 + LDY #0 ; {2C2B} RESET INDEX + LDA (]ADDR),Y ; {6C2B} GET X-LENGTH FROM ARRAY + STA ]XLEN ; {3C2B} + LDY #1 ; {2C2B} INCREMENT INDEX + LDA (]ADDR),Y ; {6C2B} GET Y-LENGTH FROM ARRAY + STA ]YLEN ; {3C2B} + LDY #2 ; {2C2B} INCREMENT INDEX + LDA (]ADDR),Y ; {6C2B} GET ELEMENT LENGTH FROM ARRAY + STA ]ELEN ; {3C2B} +* +** MULTIPLY Y-INDEX BY Y-LENGTH +* + LDA #0 ; {2C2B} RESET LOBYTE + TAY ; {2C1B} RESET HIBYTE + STY SCRATCH ; {3C2B} SAVE HIBYTE IN SCRATCH + BEQ :ENTLP ; {3C2B} IF ZERO, SKIP TO LOOP +:DOADD + CLC ; {2C1B} CLEAR CARRY FLAG + ADC ]YIDX ; {4C3B} ADD Y-INDEX + TAX ; {2C1B} TEMPORARILY STORE IN .X + TYA ; {2C1B} LOAD HIBYTE TO .A + ADC SCRATCH ; {4C3B} ADD HIBYTE + TAY ; {2C1B} TRANSFER BACK INTO .Y + TXA ; {2C1B} RELOAD LOBYTE +:LP + ASL ]YIDX ; {6C3B} MULTIPLY Y-INDEX BY 2 + ROL SCRATCH ; {6C3B} DEAL WITH HIBYTE +:ENTLP + LSR ]YLEN ; {6C3B} DIVIDE Y-LENGTH BY 2 + BCS :DOADD ; {3C2B} IF >= LOBYTE IN .A, ADD AGAIN + BNE :LP ; {3C2B} ELSE, LOOP + STX ]PBAK ; {3C2B} STORE LOBYTE IN PRODUCT BACKUP + STY ]PBAK+1 ; {3C2B} STORE HIBYTE +* +** NOW MULTIPLY LENGTH OF ELEMENTS BY XIDX +* + LDA ]XIDX ; {3C2B} PUT X-INDEX INTO + STA ]MLIER ; {3C2B} MULTIPLIER + LDA ]ELEN ; {3C2B} ELEMENT LENGTH INTO + STA ]MCAND ; {3C2B} MULTIPLICAND + LDA #0 ; {2C2B} RESET PRODUCT LOBYTE + STA ]MLIER+1 ; {3C2B} RESET MULTIPLIER HIBYTE + STA ]MCAND+1 ; {3C2B} RESET MULTIPLICAND HIBYTE + STA ]PROD ; {3C2B} + STA ]PROD+1 ; {3C2B} RESET PRODUCT 2ND BYTE + STA ]PROD+2 ; {3C2B} RESET PRODUCT 3RD BYTE + STA ]PROD+3 ; {3C2B} RESET PRODUCT HIBYTE + LDX #$10 ; {2C2B} LOAD $10 INTO .X (#16) +:SHIFTR LSR ]MLIER+1 ; {6C3B} DIVIDE MULTIPLIER BY 2 + ROR ]MLIER ; {6C3B} ADJUST LOBYTE + BCC :ROTR ; {3C2B} IF < PRODUCT, ROTATE + LDA ]PROD+2 ; {3C2B} LOAD PRODUCT 3RD BYTE + CLC ; {2C1B} CLEAR CARRY FLAG + ADC ]MCAND ; {4C3B} ADD MULTIPLICAND + STA ]PROD+2 ; {3C2B} STORE BACK INTO 3RD + LDA ]PROD+3 ; {3C2B} LOAD HIBYTE + ADC ]MCAND+1 ; {4C3B} ADD MULTIPLICAND HIBYTE +:ROTR + ROR ; {6C3B} ROTATE .A RIGHT + STA ]PROD+3 ; {3C2B} STORE IN PRODUCT HIBYTE + ROR ]PROD+2 ; {6C3B} ROTATE PRODUCT 3RD BYTE + ROR ]PROD+1 ; {6C3B} ROTATE PRODUCT 2ND BYTE + ROR ]PROD ; {6C3B} ROTATE PRODUCT LOBYTE + DEX ; {2C1B} DECREMENT COUNTER + BNE :SHIFTR ; {3C2B} IF NOT 0, BACK TO SHIFTER + LDA ]PROD ; {3C2B} LOAD PRODUCT LOBYTE + CLC ; {2C1B} CLEAR CARRY FLAG + ADC #3 ; {2C2B} INCREASE BY 3 + STA ]PROD ; {3C2B} STORE BACK INTO LOBYTE + LDA ]PROD+1 ; {3C2B} ACCOUNT FOR CARRIES + ADC #0 ; {2C2B} + STA ]PROD+1 ; {3C2B} +* +** NOW ADD THAT TO EARLIER CALC +* + CLC ; {2C1B} CLEAR CARRY FLAG + LDA ]PROD ; {3C2B} LOAD PRODUCT LOBYTE + ADC ]PBAK ; {4C3B} ADD PREVIOUS PRODUCT + STA ]PROD ; {3C2B} STORE NEW PRODUCT LOBYTE + LDA ]PROD+1 ; {3C2B} LOAD PRODUCT HIBYTE + ADC ]PBAK+1 ; {4C3B} ADD PREV PRODUCT HIBYTE + STA ]PROD+1 ; {3C2B} STORE PRODUCT HIBYTE +* +** NOW ADD ARRAY ADDRESS TO GET INDEX ADDR +* + CLC ; {2C1B} CLEAR CARRY FLAG + LDA ]PROD ; {3C2B} LOAD PRODUCT LOBYTE + ADC ]ADDR ; {4C3B} ADD ARRAY ADDRESS LOBYTE + STA ]PROD ; {3C2B} STORE BACK IN PRODUCT LOBYTE + LDA ]PROD+1 ; {3C2B} LOAD HIBYTE + ADC ]ADDR+1 ; {4C3B} ADD ADDRESS HIBYTE + STA ]PROD+1 ; {3C2B} STORE IN PRODUCT HIBYTE +* + LDY ]PROD ; {3C2B} LOAD PRODUCT LOBYTE IN .Y + LDX ]PROD+1 ; {3C2B} LOAD HIBYTE IN .X FOR SOME REASON + STY ]ADDR ; {3C2B} TRANSFER TO ZERO PAGE + STX ]ADDR+1 ; {3C2B} + LDY #0 ; {2C2B} RESET INDEX +:RLP + LDA (]ADDR),Y ; {6C2B} LOAD BYTE + STA RETURN,Y ; {3C2B} STORE IN RETURN + INY ; {2C1B} INCREASE INDEX + CPY ]ELEN ; {4C3B} IF INDEX != ELEMENT LENGTH + BNE :RLP ; {3C2B} THEN KEEP COPYING + LDA ]ELEN ; {3C2B} OTHERWISE, STORE ELEMENT LENGTH + STA RETLEN ; {3C2B} INTO RETURN LENGTH + LDA RETLEN ; {3C2B} AND IN .A + LDX ]ADDR ; {3C2B} RETURN ARRAY ADDRESS LOBYTE IN .X + LDY ]ADDR+1 ; {3C2B} RETURN HIBYTE IN .Y + RTS ; {6C1B} + +``` + + + +--- + + + +### THE PUT82 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `PUT82` | +| Type | Macro | +| File | `MAC.ARR8B2D.ASM` | +| Author | Nathan Riggs | +| Last Revision | 18-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Write value to element in an 8-bit, 2-dimensional array | +| Input | ]1 = Source Address
]2 = Destination Array Address
]3 = First Dimension Index
]4 = Second Dimension Index | +| Output | none | +| Dependencies | `SUB.APUT82.ASM` | +| Flags Destroyed | NZCV | +| Cycles | 352+ | +| Bytes | 223 | +| Notes | None | +| See Also | `DIM82` `ADIM82` `GET82` `AGET82` `APUT82` | + +--- + +*DETAILS* + +The `PUT82` macro inserts the value given in a source memory address range into the specified X,Y element of the destination array's address. The number of bytes copied is determined by the array's element length attribute. As with other array routines, there is no error-checking for out-of-bounds requests; it is up to the programmer to keep track of an array's boundaries once they are set. Using `PUT82` with out-of-bounds dimensional indices will either overwrite important data stored already or will cause a fatal crash. Additionally note that this macro only accepts an address for the value to be copied, *not* a literal value; this too is in common with other array `PUT` macros and subroutines. + +After being called, the address of the element in question is returned via the **.X** and **.Y** registers (the low and high byte of the address, respectively), and the element length is returned in the **.A** register. + + + +`LISTING 3.25: The PUT82 Macro Source` + +```assembly +* +*``````````````````````````````* +* PUT82 (NATHAN RIGGS) * +* * +* SET VALUE OF AN ELEMENT IN * +* AN 8-BIT, TWO-DIMENSIONAL * +* ARRAY. * +* * +* PARAMETERS * +* * +* ]1 = SOURCE ADDRESS * +* ]2 = DEST ARRAY ADDRESS * +* ]3 = ELEMENT X INDEX * +* ]4 = Y INDEX * +* * +* CYCLES: 352+ * +* SIZE: 223 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +PUT82 MAC + _MLIT ]1;WPAR1 ; {16C12B} PARSE SOURCE ADDRESS + _MLIT ]2;WPAR2 ; {16C12B} PARSE DEST ADDRESS + LDA ]3 ; {3C2B} X INDEX + STA BPAR1 ; {3C2B} + LDA ]4 ; {3C2B} Y INDEX + STA BPAR2 ; {3C2B} + JSR APUT82 ; {308C191B} + <<< +* + +``` + + + +--- + + + +### THE APUT82 SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `APUT82` | +| Type | Subroutine | +| File | `SUB.APUT82.ASM` | +| Author | Nathan Riggs | +| Last Revision | 18-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Write a value into an 8-bit, 1-dimensional array's element | +| Input | WPAR1 = Source Address
WPAR2 = Destination Array Address
BPAR1 = First Dimension Index
BPAR2 = Second Dimension Index | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 302+ | +| Bytes | 188 | +| Notes | None | +| See Also | `DIM82` `ADIM82` `GET82` `AGET82` `PUT82` | + +--- + +*DETAILS* + +The `APUT82` subroutine takes a series of bytes found in a source address and places them into the given X,Y element of the specified eight-bit, two-dimensional array. As stated in the associated macro's entry (`PUT82`), note that 1) no error-checking is involved, thus the boundaries are not protected, and 2) a value can only be transferred from another memory location to the desired array element; that is, direct "putting" into the element cannot be accomplished with this subroutine. + +Once finished, the subroutine returns the beginning address of the element in the **.X** register (low byte) and **.Y** register (high byte), with the element length returned in the **.A** register. + + + +`LISTING 3.26: The APUT82 Subroutine Source` + +```assembly +* +*``````````````````````````````* +* APUT82 (NATHAN RIGGS) * +* * +* PUT DATA FROM SOURCE INTO * +* A 2D, 8BIT ARRAY ELEMENT. * +* * +* INPUT: * +* * +* WPAR1 = SOURCE ADDRESS * +* WPAR2 = ARRAY ADDRESS * +* BPAR1 = 1ST DIM INDEX * +* BPAR2 = 2ND DIM INDEX * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 302+ * +* SIZE: 188 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]ADDRS EQU WPAR1 ; SOURCE ADDRESS +]ADDRD EQU WPAR2 ; ARRAY ADDRESS +]XIDX EQU BPAR1 ; X INDEX +]YIDX EQU BPAR2 ; Y INDEX +* +]ESIZE EQU VARTAB ; ELEMENT LENGTH +]MCAND EQU VARTAB+1 ; MULTIPLICAND +]MLIER EQU VARTAB+3 ; MULTIPLIER +]PROD EQU VARTAB+5 ; PRODUCT +]XLEN EQU VARTAB+9 ; ARRAY X-LENGTH +]YLEN EQU VARTAB+13 ; ARRAY Y-LENGTH +]PBAK EQU VARTAB+15 ; PRODUCT BACKUP +* +APUT82 + LDY #0 ; {2C2B} RESET INDEX + LDA (]ADDRD),Y ; {6C2B} GET ARRAY X-LENGTH + STA ]XLEN ; {3C2B} + LDY #1 ; {2C2B} INCREMENT INDEX + LDA (]ADDRD),Y ; {6C2B} GET ARRAY Y-LENGTH + STA ]YLEN ; {3C2B} + LDY #2 ; {2C2B} INCREMENT INDEX + LDA (]ADDRD),Y ; {6C2B} GET ARRAY ELEMENT LENGTH + STA ]ESIZE ; {3C2B} +* +** MULTIPLY Y-INDEX BY Y-LENGTH +* + LDA #0 ; {2C2B} RESET LOBYTE + TAY ; {2C1B} RESET HIBYTE + STY SCRATCH ; {3C2B} SAVE HIBYTE IN SCRATCH + BEQ :ENTLP ; {3C2B} IF ZERO, SKIP TO LOOP +:DOADD + CLC ; {2C1B} CLEAR CARRY FLAG + ADC ]YIDX ; {4C3B} ADD Y-INDEX + TAX ; {2C1B} STORE IN .X + TYA ; {2C1B} LOAD HIBYTE + ADC SCRATCH ; {4C3B} ADD HIBYTE + TAY ; {2C1B} STORE IN .Y + TXA ; {2C1B} RELOAD LOBYTE +:LP + ASL ]YIDX ; {6C3B} MULTIPLY Y-INDEX BY 2 + ROL SCRATCH ; {6C3B} DEAL WITH HIBYTE +:ENTLP + LSR ]YLEN ; {6C3B} DIVIDE Y-LENGTH BY 2 + BCS :DOADD ; {3C2B} IF >= LOBYTE, ADD AGAIN + BNE :LP ; {3C2B} ELSE, LOOP + STX ]PBAK ; {3C2B} STORE LOBYTE IN PRODUCT BACKUP + STY ]PBAK+1 ; {3C2B} STORE HIBYTE + LDA ]XIDX ; {3C2B} PUT X-INDEX INTO MULTIPLIER + STA ]MLIER ; {3C2B} + LDA #0 ; {2C2B} RESET HIBYTE + STA ]MLIER+1 ; {3C2B} TRANSFER HIBYTE + LDA ]ESIZE ; {3C2B} PUT ELEMENT LENGTH + STA ]MCAND ; {3C2B} INTO MULTIPLICAND + LDA #0 ; {2C2B} RESET HIBYTE + STA ]MCAND+1 ; {3C2B} +* +** NOW MULTIPLY XIDX BY ELEMENT LENGTH +* + STA ]PROD ; {3C2B} RESET PRODUCT LOBYTE + STA ]PROD+1 ; {3C2B} RESET 2ND BYTE + STA ]PROD+2 ; {3C2B} RESET 3RD BYTE + STA ]PROD+3 ; {3C2B} RESET HIBYTE + LDX #$10 ; {2C2B} LOAD $10 INTO .X (#16) +:SHIFTR LSR ]MLIER+1 ; {6C3B} DIVIDE MULTIPLIER BY 2 + ROR ]MLIER ; {6C3B} DEAL WITH HIBYTE + BCC :ROTR ; {3C2B} IF < RODUCT, ROTATE + LDA ]PROD+2 ; {3C2B} LOAD PRODUCT 3RD BYTE + CLC ; {2C1B} CLEAR CARRY FLAG + ADC ]MCAND ; {4C3B} ADD MULTIPLICAND + STA ]PROD+2 ; {3C2B} STORE 3RD BYTE + LDA ]PROD+3 ; {3C2B} LOAD HIBYTE + ADC ]MCAND+1 ; {4C3B} ADD MULTIPLICAND HIBYTE +:ROTR + ROR ; {6C3B} ROTATE .A RIGHT + STA ]PROD+3 ; {3C2B} STORE IN PRODUCT HIBYTE + ROR ]PROD+2 ; {6C3B} ROTATE PRODUCT 3RD BYTE + ROR ]PROD+1 ; {6C3B} ROTATE RODUCT 2ND + ROR ]PROD ; {6C3B} ROTATE LOBYTE + DEX ; {2C1B} DECREMENT COUNTER + BNE :SHIFTR ; {3C2B} IF NOT 0, BACK TO SHIFTER +* +** NOW ADD PRODUCT TO REST +* + LDA ]PBAK ; {3C2B} LOAD FIRST PRODUCT LOBYTE + CLC ; {2C1B} CLEAR CARRY FLAG + ADC ]PROD ; {4C3B} ADD 2ND PRODUCT LOBYTE + STA ]PROD ; {3C2B} STORE NEW PRODUCT LOBYTE + LDA ]PBAK+1 ; {3C2B} LOAD FIRST PRODUCT HIBYTE + ADC ]PROD+1 ; {4C3B} ADD 2ND HIBYTE + STA ]PROD+1 ; {3C2B} STORE HIBYTE + LDA ]PROD ; {3C2B} LOAD NEW PRODUCT LOBYTE + CLC ; {2C1B} CLEAR CARRY FLAG + ADC #3 ; {4C3B} INCREASE BY 3 + STA ]PROD ; {3C2B} STORE IN LOBYTE + LDA ]PROD+1 ; {3C2B} APPLY CARRY TO HIBYTE + ADC #0 ; {2C2B} + STA ]PROD+1 ; {3C2B} +* +** ADD ARRAY ADDRESS TO GET INDEX +* + CLC ; {2C1B} CLEAR CARRY FLAG + LDA ]PROD ; {3C2B} LOAD PRODUCT LOBYTE + ADC ]ADDRD ; {4C3B} ADD ARRAY ADDRESS LOBYTE + STA ]PROD ; {3C2B} STORE IN PRODUCT + LDA ]PROD+1 ; {3C2B} LOAD PRODUCT HIBYTE + ADC ]ADDRD+1 ; {4C3B} ADD ARRAYH ADDRESS HIBYTE + STA ]PROD+1 ; {3C2B} STORE HIBYTE + LDX ]PROD ; {3C2B} PUT ELEMENT ADDRESS LOBYTE IN .X + LDY ]PROD+1 ; {3C2B} PUT HIBYTE IN Y + STX ADDR2 ; {3C2B} STORE IN ZERO PAGE + STY ADDR2+1 ; {3C2B} + LDY #0 ; {2C2B} RESET INDEX +* +** COPY FROM SRC ADDR TO DEST ADDR +* +:CLP + LDA (]ADDRS),Y ; {6C2B} GET BYTE FROM SOURCE + STA (ADDR2),Y ; {3C2B} STORE IN ELEMENT + INY ; {2C1B} INCREASE INDEX + CPY ]ESIZE ; {4C3B} IF < ELEMENT SIZE, + BNE :CLP ; {3C2B} CONTINUE COPYING + LDX ADDR2 ; {3C2B} PUT ELEMENT LOBYTE IN .X + LDY ADDR2+1 ; {3C2B} PUT HIBYTE IN .Y + LDA ]ESIZE ; {3C2B} PUT ELEMENT SIZE IN .A + RTS ; {6C1B} + +``` + + + +--- + + + +### 1-Dimensional, 16-Bit Arrays + +These macros and subroutines are dedicated to using arrays with a maximum of 65,025 elements per dimension (in this case, one dimension), with each element being up to 255 bytes in length. The macros for these arrays are located in the MAC.ARR16B1D.ASM file, named so after the types of macros it includes (**16** **B**ytes, **1 D**imensions). + +If it is not obvious: a single array with an element size of one byte each can easily take up more memory than most Apple II computers can contain. The macros and subroutines listed here are inadequate for implementations that require such large arrays, as they would also (usually) need to access extra memory provided by third-party hardware. Even in normal 128k systems like the Apple //c, these macros and subroutines cannot utilize memory to its fullest extent; while bank switching can be enacted by the programmer, an array created with this collection cannot fill part of one bank and continue to another. + +`LISTING 3.30: MAC.ARR16B1D.ASM Header Source` + +```assembly +* +*``````````````````````````````* +* MAC.ARR16B1D.ASM * +* * +* A MACRO LIBRARY FOR 16-BIT * +* 1-DIMENSIONAL ARRAYS. * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 18-MAR-2021 * +* ASSEMBLER: MERLIN 8 PRO * +* OS: DOS 3.3 * +* * +* SUBROUTINE FILES USED * +* * +* SUB.ADIM161 * +* SUB.AGET161 * +* SUB.APUT161 * +* * +* LIST OF MACROS * +* * +* DIM161: DIM 1D, 16BIT ARRAY * +* GET161: GET ELEMENT FROM 1D, * +* 16BIT ARRAY. * +* PUT161: PUT VALUE INTO A 1D, * +* 16BIT ARRAY INDEX. * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* + +``` + + + +--- + + + +### THE DIM161 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `DIM161` | +| Type | Macro | +| File | `MAC.ARR16B1D.ASM` | +| Author | Nathan Riggs | +| Last Revision | 18-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Initialize a 16-bit, 1-dimensional array | +| Input | ]1 = Array Address
]2 = Number of Elements
]3 = Element Byte Length
]4 = Default Fill Value | +| Output | none | +| Dependencies | `SUB.ADIM161.ASM` | +| Flags Destroyed | NZCV | +| Cycles | 271+ | +| Bytes | 165 | +| Notes | none | +| See Also | `ADIM161` `GET161` `AGET161` `PUT161` `APUT161` | + +--- + +*DETAILS* + +The `DIM161` macro initializes a one-dimensional array that can hold up to 65,025 elements, with each element being less than 255 bytes long. Note that not only can this macro easily fill the entire available memory on many Apple II machines, but it can also accidentally write over data or program statements that are important to the overall stability and functioning of the program and the computer. As such, the programmer should calculate the required amount of memory necessary for the `DIM161` array and make sure the space is clear prior to actually calling the macro. This can be done by multiplying the number of elements being declared by the given element length, then adding to account for the 3-byte header of the array. The header consists of three bytes that hold the number of elements and the size of the elements. The first two bytes indicate the number of elements as a 16-bit number (big-endian) and the third byte indicates the length of each element, which can vary from 1 to 255. + +After finishing initialization, the macro returns the total length of the array, in bytes, in `RETURN` (16-bit value). While this might be useful information after the fact, it does nothing to solve the problem of knowing the array's complete size before execution. + + + +`LISTING 3.31: The DIM161 Macro Source` + +```assembly +* +*``````````````````````````````* +* DIM161 (NATHAN RIGGS) * +* * +* INITIALIZE A 16-BIT ARRAY * +* WITH A SINGLE DIMENSION. * +* * +* PARAMETERS * +* * +* ]1 = ARRAY ADDRESS * +* ]2 = ARRAY BYTE LENGTH * +* ]3 = ELEMENT BYTE LENGTH * +* ]4 = ARRAY FILL VALUE * +* * +* CYCLES: 271+ * +* SIZE: 165 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +DIM161 MAC + _MLIT ]1;WPAR1 ; {16C12B} PARSE ARRAY ADDRESS + _MLIT ]2;WPAR2 ; {16C12B} PARSE BYTE LENGTH + LDA ]3 ; {3C2B} ELEMENT LENGTH + STA WPAR3 ; {3C2B} + LDA ]4 ; {3C2B} FILL VALUE + STA BPAR1 ; {3C2B} + JSR ADIM161 ; {227C133B} + <<< +* + +``` + + + +--- + + + +### THE ADIM161 SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `ADIM161` | +| Type | Subroutine | +| File | `SUB.ADIM161.ASM` | +| Author | Nathan Riggs | +| Last Revision | 18-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Initialize a single-dimension, 16-bit array | +| Input | WPAR1 = Array Address
WPAR2 = Number of Elements
WPAR3 = Element Byte-length
BPAR1 = Default Fill Value | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 221+ | +| Bytes | 130 | +| Notes | None | +| See Also | `DIM161` `GET161` `AGET161` `PUT161` `APUT161` | + +--- + +*DETAILS* + +The `ADIM161` subroutine creates the data structure for a 16-bit, one-dimensional array, which can hold up to 65,025 elements of a length between 1 and 255 bytes. Please see the listing for the `DIM161` macro for a short description of how the routine works as well as any necessary precautions to be considered. + + + +`LISTING 3.32: The ADIM161 Subroutine Source` + +```assembly +* +*``````````````````````````````* +* ADIM161 (NATHAN RIGGS) * +* * +* INITIALIZE A 16BIT, 1D ARRAY * +* * +*------------------------------* +* MULTIPLICATION CODE ADAPTED * +* FROM WHITE FLAME'S WORK ON * +* CODEBASE64. LICENSE MAY VARY * +*------------------------------* +* * +* INPUT: * +* * +* WPAR1 = ARRAY ADDRESS * +* WPAR2 = # OF ELEMENTS * +* WPAR3 = ELEMENT LENGTH * +* BPAR1 = FILL VALUE * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 221+ * +* SIZE: 130 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]ADDRD EQU WPAR1 ; ARRAY ADDRESS +]ASIZE EQU WPAR2 ; # OF ELEMENTS +]ESIZE EQU WPAR3 ; ELEMENT BYTE LENGTH +]FILL EQU BPAR1 ; FILL VALUE +* +]MSIZE EQU VARTAB ; TOTAL ARRAY BYTES +]ASZBAK EQU VARTAB+4 ; BACKUP OF ELEMENT # +]ESZBAK EQU VARTAB+7 ; BACKUP +* +ADIM161 + LDA ]ESIZE ; {3C2B} ELEMENT SIZE + STA ]ESZBAK ; {3C2B} ELEMENT LENGTH BACKUP + LDA ]ASIZE ; {3C2B} + STA ]ASZBAK ; {3C2B} ARRAY SIZE BACKUP + LDA ]ASIZE+1 ; {3C2B} + STA ]ASZBAK+1 ; {3C2B} BACKUP + STA SCRATCH ; {3C2B} HIBYTE FOR MULTIPLICATION + LDA ]ADDRD ; {3C2B} + STA ADDR2 ; {3C2B} + LDA ]ADDRD+1 ; {3C2B} + STA ADDR2+1 ; {3C2B} + LDY #0 ; {2C2B} CLEAR INDEX + LDA #0 ; {2C2B} CLEAR ACCUMULATOR + BEQ :ENTLP ; {3C2B} IF 0, SKIP TO LOOP +* +** MULTIPLY ARRAY SIZE BY ELEMENT SIZE +* +:DOADD + CLC ; {2C1B} CLEAR CARRY FLAG + ADC ]ASIZE ; {4C3B} ADD ARRAY SIZE + TAX ; {2C1B} HOLD IN .X + TYA ; {2C1B} LOAD HIBYTE + ADC SCRATCH ; {4C3B} ADD HIBYTE + TAY ; {2C1B} HOLD IN .Y + TXA ; {2C1B} RELOAD LOBYTE +:LP + ASL ]ASIZE ; {6C3B} MULTIPLY ARRAY SIZE BY 2 + ROL SCRATCH ; {6C3B} ADJUST HIBYTE +:ENTLP + LSR ]ESIZE ; {6C3B} DIVIDE ELEMENT SIZE BY 2 + BCS :DOADD ; {3C2B} IF >= LOBYTE IN .A, + BNE :LP ; {3C2B} ADD AGAIN--ELSE, LOOP + CLC ; {2C1B} CLEAR CARRY + TXA ; {2C1B} LOBYTE TO .A + ADC #3 ; {4C3B} ADD 2 FOR HEADER + STA ]MSIZE ; {3C2B} STORE IN TOTAL LOBYTE + TYA ; {2C1B} HIBYTE TO .A + ADC #0 ; {4C3B} DO CARRY + STA ]MSIZE+1 ; {3C2B} STORE IN TOTAL HIBYTE +* +** CLEAR MEMORY BLOCKS +* + LDA ]FILL ; {3C2B} GET FILL VALUE + LDX ]MSIZE+1 ; {3C2B} LOAD TOTAL SIZE LOBYTE + BEQ :PART ; {3C2B} IF NO WHOLE PAGES, JUST PART + LDY #0 ; {2C2B} RESET INDEX +:FULL + STA (]ADDRD),Y ; {6C2B} COPY BYTE TO ADDRESS + INY ; {2C1B} NEXT BYTE + BNE :FULL ; {3C2B} LOOP UNTIL PAGE DONE + INC ]ADDRD+1 ; {6C2B} GO TO NEXT PAGE + DEX ; {2C1B} DECREMENT COUNTER + BNE :FULL ; {3C2B} LOOP IF PAGES LEFT +:PART + LDX ]MSIZE ; {3C2B} PARTIAL PAGE BYTES + BEQ :MFEXIT ; {3C2B} EXIT IF = 0 + LDY #0 ; {2C2B} RESET INDEX +:PARTLP + STA (]ADDRD),Y ; {6C2B} STORE BYTE + INY ; {2C1B} INCREMENT INDEX + DEX ; {2C1B} DECREMENT COUNTER + BNE :PARTLP ; {3C2B} LOOP UNTIL DONE +:MFEXIT + LDY #0 ; {2C2B} RESET INDEX + LDA ]ASZBAK ; {3C2B} STORE ARRAY SIZE IN HEADER + STA (ADDR2),Y ; {6C2B} + INY ; {2C1B} INCREASE INDEX + LDA ]ASZBAK+1 ; {3C2B} STORE ARRAY SIZE HIBYTE + STA (ADDR2),Y ; {6C2B} + INY ; {2C1B} INCREMENT INDEX + LDA ]ESZBAK ; {3C2B} STORE ELEMENT SIZE + STA (ADDR2),Y ; {6C2B} IN HEADER + LDX ]ADDRD ; {3C2B} .X HOLDS ARRAY ADDRESS LOBYTE + LDY ]ADDRD+1 ; {3C2B} .Y HOLDS HIBYTE + LDA ]MSIZE ; {3C2B} STORE TOTAL ARRAY SIZE + STA RETURN ; {3C2B} IN RETURN + LDA ]MSIZE+1 ; {3C2B} + STA RETURN+1 ; {3C2B} + LDA #2 ; {2C2B} + STA RETLEN ; {3C2B} 2 BYTE LENGTH + LDA ]ASZBAK ; {3C2B} .A HOLDS # OF ELEMENTS + RTS ; {6C1B} + +``` + + + +--- + + + +### THE GET161 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------- | +| Name | `GET161` | +| Type | Macro | +| File | `MAC.ARR16B1D.ASM` | +| Author | Nathan Riggs | +| Last Revision | 18-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Get an element value from a 16-bit, 1-dimensional array | +| Input | ]1 = Array Address
]2 = Element Index | +| Output | none | +| Dependencies | `SUB.AGET161.ASM` | +| Flags Destroyed | NZCV | +| Cycles | 223+ | +| Bytes | 130 | +| Notes | none | +| See Also | `DIM161` `ADIM161` `AGET161` `PUT161` `APUT161` | + +--- + +*DETAILS* + +The `GET161` macro retrieves a value from a desired element in a one-dimensional, 16-bit array. After retrieval, the data is stored in `RETURN`, with the byte length of the data held in `RETLEN` (the length is determined by the array's element length attribute). Additionally, the element length is held in **.A** after execution, and the address of the given element is held in the **.X** and **.Y** registers (low byte and high byte of address, respectively). + +While requesting an out-of-bounds element is unlikely to freeze the system on its own (unless a soft switch happens to be in the area accessed), using the trash data collected from such an attempt may lead to crashes. As such, the programmer should take care to always know the bounds of the given array. + + + +`LISTING 3.33: The GET161 Macro` + +```assembly +* +*``````````````````````````````* +* GET161 (NATHAN RIGGS) * +* * +* GET THE VALUE STORED IN THE * +* ELEMENT OF A 16-BIT, ONE- * +* DIMENSIONAL ARRAY. * +* * +* PARAMETERS * +* * +* ]1 = SOURCE ADDRESS * +* ]2 = ARRAY ADDRESS * +* * +* CYCLES: 223+ * +* SIZE: 130 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +GET161 MAC + _MLIT ]1;WPAR1 ; {16C12B} PARSE SOURCE ADDRESS + _MLIT ]2;WPAR2 ; {16C12B} PARSE INDEX + JSR AGET161 ; {191C116B} + <<< +* + +``` + + + +--- + + + +### THE AGET161 SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | -------------------------------------------------------- | +| Name | `AGET161` | +| Type | Subroutine | +| File | `SUB.AGET161.ASM` | +| Author | Nathan Riggs | +| Last Revision | 18-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | read an element value in a 16-bit, one-dimensional array | +| Input | WPAR1 = Array Address
WPAR2 = Element Index | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 185+ | +| Bytes | 113 | +| Notes | None | +| See Also | `DIM161` `ADIM161` `GET161` `PUT161` `APUT161` | + +--- + +*DETAILS* + +The `AGET161` subroutine receives its parameters from `WPAR1` and `WPAR2`, where the Array Address and the Element Index is held (respectively), then retrieves the value in the given array's indexed element. This data is held in `RETURN`, with the length of the value (in bytes) held in both `RETLEN` and the **.A** register. Note that like all array `GET` macros and subroutines, this follows the TITO rule: trash in, trash out. If an element is requested that is out of bounds for the given array, then a trash value will be returned--an error will not interrupt the flow of execution. Thus, the programmer is responsible for keeping track of all array boundaries. + + + +`LISTING 3.34: The AGET161 Subroutine Source` + +```assembly +* +*``````````````````````````````* +* AGET161 (NATHAN RIGGS) * +* * +* GET DATA IN 16-BIT, 2D ARRAY * +* * +*------------------------------* +* MULTIPLICATION CODE ADAPTED * +* FROM WHITE FLAME'S WORK ON * +* CODEBASE64. LICENSE MAY VARY * +*------------------------------* +* * +* INPUT: * +* * +* WPAR1 = ARRAY ADDRESS * +* WPAR2 = ELEMENT INDEX * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 185+ * +* SIZE: 113 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]AIDX EQU WPAR2 ; ELEMENT INDEX +]ADDR EQU WPAR1 ; ARRAY ADDRESS +* +]ESIZE EQU VARTAB ; ELEMENT LENGTH +]ESIZEB EQU VARTAB+1 ; ^BACKUP +]ASIZE EQU VARTAB+2 ; NUMBER OF ELEMENTS +]IDX EQU VARTAB+6 ; INDEX BACKUP +* +AGET161 + LDA ]AIDX ; {3C2B} + STA ]IDX ; {3C2B} + LDA ]AIDX+1 ; {3C2B} GET INDEX HIBYTE + STA ]AIDX+1 ; {3C2B} + STA SCRATCH ; {3C2B} + LDY #0 ; {2C2B} RESET INDEX + LDA (]ADDR),Y ; {6C2B} GET NUMBER OF + STA ]ASIZE ; {3C2B} ARRAY ELEMENTS + LDY #1 ; {2C2B} GET HIBYTE OF + LDA (]ADDR),Y ; {6C2B} # OF ARRAY ELEMENTS + STA ]ASIZE+1 ; {3C2B} + INY ; {2C1B} INCREASE BYTE INDEX + LDA (]ADDR),Y ; {6C2B} GET ELEMENT LENGTH + STA ]ESIZE ; {3C2B} + STA ]ESIZEB ; {3C2B} +* +** MULTIPLY INDEX BY ELEMENT SIZE, ADD 3 +* + LDY #0 ; {2C2B} RESET .Y AND .A + LDA #0 ; {2C2B} + BEQ :ENTLPA ; {3C2B} IF ZERO, SKIP TO LOOP +:DOADD + CLC ; {2C1B} CLEAR CARRY + ADC ]AIDX ; {4C3B} ADD INDEX TO .A + TAX ; {2C1B} HOLD IN .X + TYA ; {2C1B} LOAD HIBYTE + ADC SCRATCH ; {4C3B} ADD HIBYTE + TAY ; {2C1B} HOLD IN .Y + TXA ; {2C1B} RELOAD LOBYTE +:LPA + ASL ]AIDX ; {6C3B} MULTIPLY INDEX BY 2 + ROL SCRATCH ; {6C3B} ADJUST HIBYTE +:ENTLPA + LSR ]ESIZE ; {6C3B} DIVIDE ELEMENT LENGTH BY 2 + BCS :DOADD ; {3C2B} IF BIT1 SHIFTED IN CARRY +MORE + BNE :LPA ; {3C2B} CONTINUE LOOPING IF Z FLAG UNSET + STX ]IDX ; {3C2B} STORE LOBYTE + STY ]IDX+1 ; {3C2B} STORE HIBYTE + LDA #3 ; {2C2B} ADD 3 TO INDEX LOBYTE + CLC ; {2C1B} CLEAR CARRY + ADC ]IDX ; {4C3B} + STA ADDR2 ; {3C2B} STORE ON ZERO PAGE + LDA ]IDX+1 ; {3C2B} ADJUST HIBYTE + ADC #0 ; {2C2B} + STA ADDR2+1 ; {3C2B} +* + LDA ADDR2 ; {3C2B} ADD ARRAY ADDRESS + CLC ; {2C1B} + ADC ]ADDR ; {4C3B} LOBYTE + STA ADDR2 ; {3C2B} + LDA ADDR2+1 ; {3C2B} HIBYTE + ADC ]ADDR+1 ; {4C3B} + STA ADDR2+1 ; {3C2B} + LDY #0 ; {2C2B} RESET BYTE INDEX +:LP + LDA (ADDR2),Y ; {6C2B} GET BYTE FROM ELEMENT + STA RETURN,Y ; {3C2B} PUT INTO RETURN + INY ; {2C1B} INCREASE BYTE INDEX + CPY ]ESIZEB ; {4C3B} IF INDEX != ELEMENT LENGTH + BNE :LP ; {3C2B} CONTINUE LOOP + LDA ]ESIZEB ; {3C2B} .A = ELEMENT SIZE + STA RETLEN ; {3C2B} STORE IN RETLEN + LDY ADDR2+1 ; {3C2B} .Y = ELEMENT ADDRESS HIBYTE + LDX ADDR2 ; {3C2B} .X = ELEMENT ADDRESS LOBYTE + RTS ; {6C1B} + +``` + + + +--- + + + +### THE PUT161 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `PUT161` | +| Type | Macro | +| File | `MAC.ARR16B1D.ASM` | +| Author | Nathan Riggs | +| Last Revision | 18-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Copy a value to an element in a 16-bit, 1-dimensional array | +| Input | ]1 = Source Address
]2 = Array Address
]3 = Element Index | +| Output | none | +| Dependencies | `SUB.APUT161.ASM` | +| Flags Destroyed | NZCV | +| Cycles | 238+ | +| Bytes | 148 | +| Notes | none | +| See Also | `DIM161` `ADIM161` `GET161` `AGET161` `APUT161` | + +--- + +*DETAILS* + +The `PUT161` macro copies a value from a memory address and places it in the specified element of the given 16-bit, one-dimensional array. It should be noted that this macro does not accept a literal value to be placed in the array--only the address of a value is accepted. Additionally, there is no error-checking; thus, if the programmer enters an out-of-bounds element index, it is possible that the subroutine will write over data or commands that should not be written over, possibly causing the computer to crash. + +After executing, the **.A** register holds the array's element byte length, as well as the physical memory location of the index in question in **.X** and **.Y** (low byte and high byte of address, respectively). + + + +`LISTING 3.35: The PUT161 Macro Source` + +```assembly +* +*``````````````````````````````* +* PUT161 (NATHAN RIGGS) * +* * +* SET THE VALUE OF AN INDEX * +* ELEMENT IN A 16-BIT, ONE- * +* DIMENSIONAL ARRAY. * +* * +* PARAMETERS * +* * +* ]1 = SOURCE ADDRESS * +* ]2 = ARRAY ADDRESS * +* ]3 = ELEMENT INDEX * +* * +* CYCLES: 238+ * +* SIZE: 148 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +PUT161 MAC + _MLIT ]1;WPAR1 ; {16C12B} PARSE SOURCE ADDRESS + _MLIT ]2;WPAR2 ; {16C12B} PARSE ARRAY ADDRESS + _MLIT ]3;WPAR3 ; {16C12B} PARSE INDEX + JSR APUT161 ; {190C112B} + <<< +* + +``` + + + +--- + + + +### THE APUT161 SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `APUT161` | +| Type | Subroutine | +| File | `SUB.APUT161.ASM` | +| Author | Nathan Riggs | +| Last Revision | 18-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Put a value in the specified element of a 16-bit, one-dimensional array | +| Input | WPAR1 = Source Address
WPAR2 = Destination Array Address
WPAR3 = Destination Element Index | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 184+ | +| Bytes | 109 | +| Notes | None | +| See Also | `DIM161` `ADIM161` `GET161` `AGET161``APUT161` | + +--- + +*DETAILS* + +The `APUT161` subroutine takes the value from a given source address range of bytes and puts that data into a specified element of a 16-bit, one-dimensional array. Afterward, the array's element byte-length is held in the .A register, with the element's physical address passed back via the **.X** and **.Y** registers (low byte and high byte, respectively). + + + +`LISTING 3.36: The APUT161 Subroutine Source` + +```assembly +* +*``````````````````````````````* +* APUT161 (NATHAN RIGGS) * +* * +*------------------------------* +* MULTIPLICATION CODE ADAPTED * +* FROM WHITE FLAME'S WORK ON * +* CODEBASE64. MICENSE MAY VARY * +*------------------------------* +* * +* INPUT: * +* * +* WPAR1 = SOURCE ADDRESS * +* WPAR2 = ARRAY ADDRESS * +* WPAR3 = ELEMENT INDEX * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 184+ * +* SIZE: 109 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]ADDRS EQU WPAR1 ; SOURCE ADDRESS +]ADDRD EQU WPAR2 ; ARRAY DESTINATION ADDRESS +]AIDX EQU WPAR3 ; ELEMENT INDEX +* +]ESIZE EQU VARTAB ; ELEMENT SIZE +]ESIZEB EQU VARTAB+1 ; ^BACKUP +]ASIZE EQU VARTAB+2 ; NUMBER OF ELEMENTS +]IDX EQU VARTAB+6 ; ANOTHER INDEX +* +APUT161 + LDA ]AIDX ; {3C2B} + STA ]IDX ; {3C2B} + LDA ]AIDX+1 ; {3C2B} + STA ]IDX+1 ; {3C2B} + STA SCRATCH ; {3C2B} + LDY #0 ; {2C2B} RESET BYTE COUNTER + LDA (]ADDRD),Y ; {6C2B} GET NUMBER OF ELEMENTS + STA ]ASIZE ; {3C2B} LOBYTE + LDY #1 ; {2C2B} INCREMENT INDEX + LDA (]ADDRD),Y ; {6C2B} GET NUMBER OF ELEMENTS + STA ]ASIZE+1 ; {3C2B} HIBYTE + INY ; {2C1B} INCREMENT INDEX + LDA (]ADDRD),Y ; {6C2B} GET ELEMENT LENGTH + STA ]ESIZE ; {3C2B} + STA ]ESIZEB ; {3C2B} BACKUP +* +** MULTIPLY INDEX BY ELEMENT SIZE, THEN ADD 3 +* + LDY #0 ; {2C2B} RESET LOBYTE + LDA #0 ; {2C2B} AND HIBYTE + BEQ :ENTLPA ; {3C2B} SKIP TO LOOP +:DOADD + CLC ; {2C1B} CLEAR CARRY + ADC ]AIDX ; {4C3B} ADD INDEX LOBYTE + TAX ; {2C1B} HOLD IN .X + TYA ; {2C1B} LOAD HIBYTE + ADC SCRATCH ; {4C3B} ADD HIBYTE + TAY ; {2C1B} HOLD BACK IN .Y + TXA ; {2C1B} RETURN LOBYTE TO .A +:LPA + ASL ]AIDX ; {6C2B} MULTIPLY INDEX BY 2 + ROL SCRATCH ; {6C2B} ADJUST HIBYTE +:ENTLPA + LSR ]ESIZE ; {6C2B} DIVIDE ELEM LENGTH BY 2 + BCS :DOADD ; {3C2B} IF 1 SHIFTED TO CARRY, ADD AGAIN + BNE :LPA ; {3C2B} CONTINUE LOOP IF ZERO UNSET + STX ]IDX ; {3C2B} LOBYTE IN .X + STY ]IDX+1 ; {3C2B} HIBYTE IN .Y + CLC ; {2C1B} + LDA #3 ; {2C2B} ADD 3 TO LOBYTE + ADC ]IDX ; {4C3B} + STA ADDR2 ; {3C2B} STORE ON ZERO PAGE + LDA ]IDX+1 ; {3C2B} ADJUST HIBYTE + ADC #0 ; {4C3B} + STA ADDR2+1 ; {3C2B} +* + CLC ; {2C1B} CLEAR CARRY + LDA ADDR2 ; {3C2B} ADD ARRAY ADDRESS + ADC ]ADDRD ; {4C3B} LOBYTE + STA ADDR2 ; {3C2B} ADD ARRAY ADDRESS + LDA ADDR2+1 ; {3C2B} HIBYTE + ADC ]ADDRD+1 ; {4C3B} + STA ADDR2+1 ; {3C2B} + LDY #0 ; {2C2B} +:LP +* +** OOPS; NEED TO CONVERT THIS TO 16 BITS +* + LDA (]ADDRS),Y ; {6C2B} GET BYTE FROM SOURCE + STA (ADDR2),Y ; {3C2B} STORE IN ELEMENT + INY ; {2C1B} INCREMENT BYTE INDEX + CPY ]ESIZEB ; {4C3B} IF INDEX != ELEMENT LENGTH + BNE :LP ; {3C2B} KEEP LOOPING + LDY ADDR2+1 ; {3C2B} HIBYTE OF ELEMENT ADDRESS + LDX ADDR2 ; {3C2B} LOBYTE + LDA ]ESIZEB ; {3C2B} .A = ELEMENT SIZE + RTS ; {6C1B} + +``` + + + +--- + + + +### 2-Dimensional, 16-Bit Arrays + +These macros and subroutines are dedicated to using arrays with a maximum of 65,025 elements per dimension (in this case, two dimensions), with each element being up to 255 bytes in length. The macros for these arrays are located in the MAC.ARR16B2D.ASM file, named so after the types of macros it includes (**16** **B**ytes, **2 D**imensions). + +**WARNING:** using 16-bit two-dimensional arrays can fill up memory *fast*. As an example, imagine have an array with 255 elements in the first dimension and 255 elements in the second dimension, with only two bytes per element. To calculate the amount of memory required for this, we multiply the elements on the first dimension by the elements in the second dimension, then multiply that by the number of bytes in each element--two. We then add four more bytes for the array header, and we have the total number that the array will take: 130,054 bytes! This is more memory than any Apple II computer has without special additional hardware (except the IIGS), and the limits of the subroutine are not even close to being met. Special care must be taken to control the size of an array *before* the array is even declared. + +`LISTING 3.40: MAC.ARR16B2D.ASM Header Source` + +```assembly +* +*``````````````````````````````* +* MAC.ARR16B2D.ASM * +* * +* A MACRO LIBRARY FOR 16-BIT, * +* 2-DIMENSIONAL ARRAYS. * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 18-MAR-2021 * +* ASSEMBLER: MERLIN 8 PRO * +* OS: DOS 3.3 * +* * +* SUBROUTINE FILES USED * +* * +* SUB.ADIM162 * +* SUB.AGET162 * +* SUB.APUT162 * +* * +* LIST OF MACROS * +* * +* DIM162: DIM 2D, 16BIT ARRAY * +* GET162: GET ELEMENT FROM 2D, * +* 16BIT ARRAY. * +* PUT162: PUT VALUE INTO A 2D, * +* 16BIT ARRAY INDEX. * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* + +``` + + + +--- + + + +### THE ADIM162 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `DIM162` | +| Type | Macro | +| File | `MAC.ARR16B2D.ASM` | +| Author | Nathan Riggs | +| Last Revision | 18-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Initialize a 16-bit, two-dimensional array | +| Input | ]1 = Array Address
]2 = First Dimension Element Count
]3 = Second Dimension Element Count
]4 = Byte size of Elements
]5 = Default Fill Value | +| Output | none | +| Dependencies | `SUB.APUT162.ASM` | +| Flags Destroyed | NZCV | +| Cycles | 464+ | +| Bytes | 373 | +| Notes | none | +| See Also | `ADIM162` `GET162` `AGET162` `PUT162` `APUT162` | + +--- + +*DETAILS* + +The `DIM162` macro initializes a 16-bit, two-dimensional array at the given memory address. As indicated by its 16-bit description, this type of array can hold up to 65,025 elements per dimension, with each element having a length of between one and 255. Such arrays are typically used for things like mapping coordinates on a two-dimensional plane. + +Whenever possible, the 8-bit version of two-dimensional arrays should be used instead of the 16-bit version due to the extra number of cycles needed to accommodate 16-bits. When arrays are used, they tend to be used often in a program, since they are such a staple "low level" data type; this means that even a few cycles here and there can add up quickly, slowing program execution. Additionally, as always, care should be taken with memory management when using arrays in general: they quickly gobble of space faster than a user may realize, and planning needs to be done before declaring and initializing the array. + + + +`LISTING 3.41: The DIM162 Macro Source` + +``` +* +*``````````````````````````````* +* DIM162 (NATHAN RIGGS) * +* * +* INITIALIZE A 16-BIT, TWO- * +* DIMENSIONAL ARRAY. * +* * +* PARAMETERS * +* * +* ]1 = ARRAY ADDRESS * +* ]2 = X DIMENSION * +* ]3 = Y DIMENSION * +* ]4 = ELEMENT SIZE * +* ]5 = FILL VALUE * +* * +* CYCLES: 464+ * +* SIZE: 373 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +DIM162 MAC + _MLIT ]1;WPAR3 ; {16C12B} PARSE ARRAY ADDRESS + _MLIT ]2;WPAR1 ; {16C12B} PARSE X DIMENSION + _MLIT ]3;WPAR2 ; {16C12B} PARSE Y DIMENSION + LDA ]4 ; {3C2B} ELEMENT LENGTH + STA BPAR1 ; {3C2B} + LDA ]5 ; {3C2B} FILL VAL + STA BPAR2 ; {3C2B} + JSR ADIM162 ; {404C229B} + <<< +* + +``` + + + +--- + + + +### THE ADIM162 SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `ADIM162` | +| Type | Subroutine | +| File | `SUB.ADIM162.ASM` | +| Author | Nathan Riggs | +| Last Revision | 18-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Initialize a two-dimensional, 16-bit array | +| Input | WPAR1 = First Dimension Length
WPAR2 = Second Dimension Length
WPAR3 = Array Address
BPAR1 = Element Byte Length
BPAR2 = Default Fill Value | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 398+ | +| Bytes | 226 | +| Notes | None | +| See Also | `DIM162` `GET162` `AGET162` `PUT162` `APUT162` | + +--- + +*DETAILS* + +The `ADIM162` subroutine declares and initializes a 16-bit, two-dimensional array. This is a fairly simple data type, though it is also usually a fundamental data type as well. At the starting two bytes of the array, the length of the first dimension is stored, with the lowest byte of the address coming first (big-endian); then the next two bytes are dedicated to the length of the second dimension. Finally, the fifth byte holds the length of each element in the array, and every byte afterwards holds the actual data placed in each element of the array. + +After executing, `ADIM162` holds the total array size in bytes in `RETURN`, with the length of the value in `RETLEN`. + + + +`LISTING 3.42: The ADIM162 Subroutine Source` + +```assembly +* +*``````````````````````````````* +* ADIM162 (NATHAN RIGGS) * +* * +* INITIALIZE A 1-DIMENSIONAL, * +* 16-BIT ARRAY. * +* * +*------------------------------* +* MULTIPLICATION ADAPTED FROM * +* WHITE FLAME'S WORK ON * +* CODEBASE64. LICENSE MAY VARY * +*------------------------------* +* * +* INPUT: * +* * +* WPAR1 = 1ST DIM LENGTH * +* WPAR2 = 2ND DIM LENGTH * +* WPAR3 = ARRAY ADDRESS * +* BPAR1 = ELEMENT LENGTH * +* BPAR2 = FILL VALUE * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 398+ * +* SIZE: 226 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]AXSIZE EQU WPAR1 ; FIRST DIMENSION LENGTH +]AYSIZE EQU WPAR2 ; SECOND DIMENSION LENGTH +]ELEN EQU BPAR1 ; ELEMENT BYTE LENGTH +]FILL EQU BPAR2 ; FILL VALUE +]ADDR EQU WPAR3 ; ARRAY ADDRESS +]ADDR2 EQU ADDR1 ; ZERO-PAGE WORKING SPACE +* +]PROD EQU VARTAB ; PRODUCT +]AXBAK EQU VARTAB+4 ; X SIZE BACKUP +]AYBAK EQU VARTAB+6 ; Y SIZE BACKUP +]MLIER EQU VARTAB+8 ; MULTIPLIER +]MCAND EQU VARTAB+10 ; MULTIPLICAND +* +ADIM162 + LDA ]AYSIZE ; {3C2B} + STA ]AYBAK ; {3C2B} + STA ]MCAND ; {3C2B} + LDA ]AYSIZE+1 ; {3C2B} + STA ]AYBAK+1 ; {3C2B} + STA ]MCAND+1 ; {3C2B} + LDA ]AXSIZE ; {3C2B} + STA ]AXBAK ; {3C2B} + STA ]MLIER ; {3C2B} + LDA ]AXSIZE+1 ; {3C2B} + STA ]AXBAK+1 ; {3C2B} + STA ]MLIER+1 ; {3C2B} + LDA ]ADDR ; {3C2B} GET ARRAY ADDRESS + STA ]ADDR2 ; {3C2B} LOBYTE; PUT IN ZERO PAGE + LDA ]ADDR+1 ; {3C2B} GET ARRAY ADDRESS HIBYTE + STA ]ADDR2+1 ; {3C2B} +* +** MULTIPLY X AND Y +* + LDA #0 ; {2C2B} RESET HIBYTE,LOBYTE + STA ]PROD+2 ; {3C2B} CLEAR PRODUCT BYTE 3 + STA ]PROD+3 ; {3C2B} CLEAR PRODUCT BYTE 4 + LDX #$10 ; {2C2B} (#16) +:SHIFT_R + LSR ]MLIER+1 ; {6C2B} DIVIDE MLIER BY TWO + ROR ]MLIER ; {6C2B} ADJUST LOBYTE + BCC :ROT_R ; {3C2B} IF 0 IN CARRY, ROTATE MORE + LDA ]PROD+2 ; {3C2B} GET 3RD BYTE OF PRODUCT + CLC ; {2C1B} + ADC ]MCAND ; {4C3B} ADD MULTIPLICAND + STA ]PROD+2 ; {3C2B} STORE 3RD BYTE + LDA ]PROD+3 ; {3C2B} LOAD 4TH BYTE + ADC ]MCAND+1 ; {4C3B} ADD MULTIPLICAND HIBYTE +:ROT_R + ROR ; {6C2B} ROTATE PARTIAL PRODUCT + STA ]PROD+3 ; {3C2B} STORE IN HIBYTE + ROR ]PROD+2 ; {6C2B} ROTATE THIRD BYTE + ROR ]PROD+1 ; {6C2B} ROTATE 2ND BYTE + ROR ]PROD ; {6C2B} ROTATE LOBYTE + DEX ; {2C1B} DECREASE COUNTER + BNE :SHIFT_R ; {3C2B} IF NOT ZERO, BACK TO SHIFTER +* + LDA ]ELEN ; {3C2B} PUT ELEMENT LENGTH + STA ]MCAND ; {3C2B} INTO MULTIPLICAND + LDA #0 ; {2C2B} CLEAR HIBYTE + STA ]MCAND+1 ; {3C2B} + LDA ]PROD ; {3C2B} LOAD EARLIER PRODUCT + STA ]MLIER ; {3C2B} STORE LOBYTE IN MULTIPLIER + LDA ]PROD+1 ; {3C2B} DO SAME FOR HIBYTE + STA ]MLIER+1 ; {3C2B} +* +** NOW MULTIPLY BY LENGTH OF ELEMENTS +* + LDA #0 ; {2C2B} CLEAR PRODUCT + STA ]PROD ; {3C2B} + STA ]PROD+1 ; {3C2B} + STA ]PROD+2 ; {3C2B} + STA ]PROD+3 ; {3C2B} + LDX #$10 ; {2C2B} +:SHIFTR LSR ]MLIER+1 ; {6C2B} SHIFT BYTES LEFT (/2) + ROR ]MLIER ; {6C2B} ADJUST LOBYTE + BCC :ROTR ; {3C2B} IF CARRY = 0, ROTATE + LDA ]PROD+2 ; {3C2B} LOAD 3RD BYTE OF PRODUCT + CLC ; {2C1B} + ADC ]MCAND ; {4C3B} ADD MULTIPLICAND + STA ]PROD+2 ; {3C2B} STORE IN 3RD BYTE + LDA ]PROD+3 ; {3C2B} LOAD HIBYTE + ADC ]MCAND+1 ; {4C3B} ADD MULTIPLICAND HIBYTE +:ROTR + ROR ; {6C2B} ROTATE .A RIGHT + STA ]PROD+3 ; {3C2B} ROTATE 4TH + ROR ]PROD+2 ; {6C2B} ROTATE 3RD + ROR ]PROD+1 ; {6C2B} ROTATE 2ND + ROR ]PROD ; {6C2B} ROTATE LOBYTE + DEX ; {2C1B} DECREMENT COUNTER + BNE :SHIFTR ; {3C2B}IF NOT 0, BACK TO SHIFTER +* + CLC ; {2C1B} CLEAR CARRY + LDA ]PROD ; {3C2B} INCREASE BY 5 + ADC #5 ; {2C2B} + STA ]PROD ; {3C2B} SAVE LOBYTE + LDA ]PROD+1 ; {3C2B} + ADC #0 ; {2C2B} + STA ]PROD+1 ; {3C2B} SAVE HIBYTE +* +** NOW CLEAR MEMORY BLOCKS, WHOLE PAGES FIRST +* + LDA ]FILL ; {3C2B} GET FILL VALUE + LDX ]PROD+1 ; {3C2B} LOAD PRODUCT 2ND BYTE + BEQ :PART ; {3C2B} IF 0, THEN PARTIAL PAGE + LDY #0 ; {2C2B} CLEAR INDEX +:FULL + STA (]ADDR),Y ; {6C2B} COPY FILL BYTE TO ADDRESS + INY ; {2C1B} INCREASE BYTE COUNTER + BNE :FULL ; {3C2B} LOOP UNTIL PAGES DONE + INC ]ADDR+1 ; {6C2B} INCREASE HIBYTE + DEX ; {2C1B} DECREASE COUNTER + BNE :FULL ; {3C2B} LOOP UNTIL PAGES DONE +* +** NOW DO REMAINING BYTES +* +:PART + LDX ]PROD ; {3C2B} LOAD PRODUCT LOBYTE IN X + BEQ :MFEXIT ; {3C2B} IF 0, THEN WE'RE DONE + LDY #0 ; {2C2B} CLEAR BYTE INDEX +:PARTLP + STA (]ADDR),Y ; {6C2B} STORE FILL BYTE + INY ; {2C1B} INCREASE BYTE INDEX + DEX ; {2C1B} DECREASE COUNTER + BNE :PARTLP ; {3C2B} LOOP UNTIL DONE +:MFEXIT + LDY #0 ; {2C2B} CLEAR BYTE INDEX + LDA ]AXBAK ; {3C2B} LOAD ORIGINAL X LENGTH + STA (]ADDR2),Y ; {6C2B} STORE IN ARRAY HEADER + INY ; {2C1B} INCREASE BYTE COUNTER + LDA ]AXBAK+1 ; {3C2B} STORE HIBYTE + STA (]ADDR2),Y ; {6C2B} + INY ; {2C1B} INCREASE BYTE INDEX + LDA ]AYBAK ; {3C2B} LOAD Y LENGTH LOBYTE + STA (]ADDR2),Y ; {6C2B} STORE IN ARRAY HEADER + INY ; {2C1B} INCREMENT BYTE INDEX + LDA ]AYBAK+1 ; {3C2B} STORE Y HIBYTE + STA (]ADDR2),Y ; {6C2B} + INY ; {2C1B} INCREMENT BYTE INDEX + LDA ]ELEN ; {3C2B} STORE ELEMENT LENGTH + STA (]ADDR2),Y ; {6C2B} +* + LDY ]ADDR2 ; {3C2B} LOBYTE OF ARRAY ADDRESS + LDX ]ADDR2+1 ; {3C2B} ARRAY ADDRESS HIBYTE + LDA ]PROD ; {3C2B} STORE TOTAL ARRAY SIZE + STA RETURN ; {3C2B} IN BYTES IN RETURN + LDA ]PROD+1 ; {3C2B} + STA RETURN+1 ; {3C2B} + LDA ]PROD+2 ; {3C2B} + STA RETURN+2 ; {3C2B} + LDA ]PROD+3 ; {3C2B} + STA RETURN+3 ; {3C2B} + LDA #4 ; {2C2B} SIZE OF RETURN + STA RETLEN ; {3C2B} + RTS ; {6C1B} + +``` + + + +--- + + + +### THE AGET162 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `GET162` | +| Type | Macro | +| File | `MAC.ARR16B2D.ASM` | +| Author | Nathan Riggs | +| Last Revision | 18-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Retrieve a value held in an element of a 16-bit, 2-dimensional array | +| Input | ]1 = Array Address
]2 = First Dimension Element Address
]3 = Second Dimension Element Address | +| Output | none | +| Dependencies | `SUB.AGET162.ASM` | +| Flags Destroyed | NZCV | +| Cycles | 436+ | +| Bytes | 263 | +| Notes | none | +| See Also | `DIM162` `ADIM162` `AGET162` `PUT162` `APUT162` | + +--- + +*DETAILS* + +The `GET162` macro retrieves a value from a specified element in a 16-bit, two-dimensional array and puts it in `RETURN`, with the element value length held in `RETLEN`. Note that if an out-of-bounds element index is given for either the first or second dimension, the value returned will be trash; there is no error-handling built into arrays, and thus it must be handled by the programmer. + + + +`LISTING 3.43: The GET162 Macro Source` + +```assembly +* +*``````````````````````````````* +* GET162 (NATHAN RIGGS) * +* * +* GET THE VALUE STORED AT AN * +* ELEMENT OF A 16-BIT, TWO- * +* DIMENSIONAL ARRAY. * +* * +* PARAMETERS * +* * +* ]1 = ARRAY ADDRESS * +* ]2 = ELEMENT X INDEX * +* ]3 = Y INDEX * +* * +* CYCLES: 436+ * +* SIZE: 263 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +GET162 MAC + _MLIT ]1;WPAR1 ; {16C12B} PARSE ARAY ADDRESS + _MLIT ]2;WPAR2 ; {16C12B} PARSE X INDEX + _MLIT ]3;WPAR3 ; {16C12B} PARSE Y INDEX + JSR AGET162 ; {388C227B} + <<< +* + +``` + + + +--- + + + +### THE AGET162 SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `AGET162` | +| Type | Subroutine | +| File | `SUB.AGET162.ASM` | +| Author | Nathan Riggs | +| Last Revision | 18-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Retrieve an indexed value in a two-dimensional, 16-bit array | +| Input | WPAR1 = Array Address
WPAR2 = First Dimension Index
WPAR3 = Second Dimension Index | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 382+ | +| Bytes | 224 | +| Notes | None | +| See Also | | + +--- + +*DETAILS* + +The `AGET162` subroutine retrieves the value stored in an element of a two-dimensional, 16-bit array, holding the value in `RETURN` with its length in `RETLEN`. Additionally, the value length is held in the **.A** register, and the starting address of the element being read is held in the **.X** register (low byte of address) and the **.Y** register (high byte of address). + +Parameters are passed to `AGET162` via the zero page to save cycles. + + + +`LISTING 3.44: The AGET162 Subroutine Source` + +```assembly +* +*``````````````````````````````* +* AGET162 (NATHAN RIGGS) * +* * +* GET A VALUE FROM AN ELEMENT * +* IN A 2-DIMENSIONAL, 16-BIT * +* ARRAY. * +* * +*------------------------------* +* MULTIPLICATION CODE ADAPTED * +* FROM WHITE FLAME'S WORK ON * +* CODEBASE64. LICENSE MAY VARY * +*------------------------------* +* * +* INPUT: * +* * +* WPAR1 = ARRAY ADDRESS * +* WPAR2 = 1ST DIM INDEX * +* WPAR3 = 2ND DIM INDEX * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 382+ * +* SIZE: 224 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]ADDR EQU WPAR1 ; ARRAY ADDRESS +]XIDX EQU WPAR2 ; FIRST DIMENSION LENGTH +]YIDX EQU WPAR3 ; SECOND DIMENSION LENGTH +* +]ESIZE EQU VARTAB ; ELEMENT LENGTH +]MCAND EQU VARTAB+2 ; MULTIPLICAND +]MLIER EQU VARTAB+4 ; MULTIPLIER +]PROD EQU VARTAB+6 ; PRODUCT +]PBAK EQU VARTAB+10 ; ^BACKUP +]XLEN EQU VARTAB+12 ; X-DIM LENGTH +]YLEN EQU VARTAB+14 ; Y-DIM LENGTH +* +AGET162 + LDY #4 ; {2C2B} READ BYTE 4 FROM HEADER + LDA (]ADDR),Y ; {6C2B} TO GET ELEMENT SIZE + STA ]ESIZE ; {3C2B} + LDY #0 ; {2C2B} READ BYTE 0 FROM HEADER + LDA (]ADDR),Y ; {6C2B} TO GET X-DIM LENGTH LOBYTE + STA ]XLEN ; {3C2B} + LDY #1 ; {2C2B} READ BYTE 1 FROM HEADER + LDA (]ADDR),Y ; {6C2B} TO GET X-DIM LENGTH HIBYTE + STA ]XLEN+1 ; {3C2B} + LDY #2 ; {2C2B} READ BYTE 2 FROM HEADER + LDA (]ADDR),Y ; {6C2B} TO GET Y-DIM LENGTH LOBYTE + STA ]YLEN ; {3C2B} + LDY #3 ; {2C2B} READ BYTE 3 OF HEADER + LDA (]ADDR),Y ; {6C2B} TO GET Y-DIM LENGTH HIBYTE + STA ]YLEN+1 ; {3C2B} + LDY #0 ; {2C2B} RESET BYTE INDEX +* +** MULTIPLY Y-INDEX BY Y-LENGTH +* + LDA ]YIDX ; {3C2B} PUT Y-INDEX INTO + STA ]MLIER ; {3C2B} MULTIPLIER + LDA ]YIDX+1 ; {3C2B} ALSO HIBYTE + STA ]MLIER+1 ; {3C2B} + LDA ]YLEN ; {3C2B} PUT Y-DIM LENGTH LOBYTE + STA ]MCAND ; {3C2B} INTO MULTIPLICAND + LDA ]YLEN+1 ; {3C2B} ALSO HIBYTE + STA ]MCAND+1 ; {3C2B} + LDA #00 ; {2C2B} RESET + STA ]PROD ; {3C2B} PRODUCT BYTES + STA ]PROD+1 ; {3C2B} + STA ]PROD+2 ; {3C2B} + STA ]PROD+3 ; {3C2B} + LDX #$10 ; {2C2B} LOAD #16 INTO X REGISTER +:SHIFT_R + LSR ]MLIER+1 ; {6C2B} DIVIDE MULTIPLIER BY 2 + ROR ]MLIER ; {6C2B} ADJUST HIBYTE + BCC :ROT_R ; {3C2B} IF 0 PUT IN CARRY, ROTATE MORE + LDA ]PROD+2 ; {3C2B} LOAD PRODUCT 3RD BYTE + CLC ; {2C1B} CLEAR CARRY + ADC ]MCAND ; {4C3B} ADD MULTIPLICAND + STA ]PROD+2 ; {3C2B} STORE IN PRODUCT 3RD + LDA ]PROD+3 ; {3C2B} LOAD PRODUCT HIBYTE + ADC ]MCAND+1 ; {4C3B} ADD MULTIPLICAN HIBYTE +:ROT_R + ROR ; {6C2B} ROTATE .A RIGHT + STA ]PROD+3 ; {3C2B} STORE IN PRODUCT HIBYTE + ROR ]PROD+2 ; {6C2B} ROTATE 3RD BYTE + ROR ]PROD+1 ; {6C2B} ROTATE 2ND BYTE + ROR ]PROD ; {6C2B} ROTATE LOBYTE + DEX ; {2C1B} DECREASE X COUNTER + BNE :SHIFT_R ; {3C2B} IF NOT ZERO, SHIFT AGAIN +* +** NOW MULTIPLY XIDX BY ELEMENT SIZE +* + LDA ]PROD ; {3C2B} BACKUP PREVIOUS PRODUCT + STA ]PBAK ; {3C2B} 1ST AND 2ND BYTES; THE + LDA ]PROD+1 ; {3C2B} 3RD AND 4TH ARE NOT USED + STA ]PBAK+1 ; {3C2B} + LDA ]XIDX ; {3C2B} LOAD X-INDEX LOBYTE + STA ]MLIER ; {3C2B} AND STORE IN MULTIPLIER + LDA ]XIDX+1 ; {3C2B} LOAD HIBYTE AND STORE + STA ]MLIER+1 ; {3C2B} + LDA ]ESIZE ; {3C2B} LOAD ELEMENT SIZE AND + STA ]MCAND ; {3C2B} STORE LOBYTE IN MULTIPLICAND + LDA #0 ; {2C2B} CLEAR MULTIPLICAND HIBYTE + STA ]MCAND+1 ; {3C2B} +* + STA ]PROD ; {3C2B} CLEAR ALL PRODUCT BYTES + STA ]PROD+1 ; {3C2B} + STA ]PROD+2 ; {3C2B} + STA ]PROD+3; ; {3C2B} + LDX #$10 ; {2C2B} LOAD #16 IN COUNTER +:SHIFTR LSR ]MLIER+1 ; {6C2B} DIVIDE MULTIPLIER HIBYTE BY 2 + ROR ]MLIER ; {6C2B} ADJUST LOBYTE + BCC :ROTR ; {3C2B} IF 0 PUT IN CARRY, ROTATE + LDA ]PROD+2 ; {3C2B} LOAD PRODUCT 3RD BYTE + CLC ; {2C1B} CLEAR CARRY + ADC ]MCAND ; {4C3B} ADD MULTIPLICAND LOBYTE + STA ]PROD+2 ; {3C2B} STORE PRODUCT 3RD BYTE + LDA ]PROD+3 ; {3C2B} LOAD PRODUCT HIBYTE + ADC ]MCAND+1 ; {4C3B} ADD MULTIPLICAND HIBYTE +:ROTR + ROR ; {6C2B} ROTATE .A RIGHT + STA ]PROD+3 ; {3C2B} STORE IN PRODUCT HIBYTE + ROR ]PROD+2 ; {6C2B} ROTATE PRODUCT 3RD BYTE + ROR ]PROD+1 ; {6C2B} ROTATE 2ND BYTE + ROR ]PROD ; {6C2B} ROTATE LOBYTE + DEX ; {2C1B} DECREMENT X COUNTER + BNE :SHIFTR ; {3C2B} IF != 0, SHIFT AGAIN +* +** NOW ADD X * ESIZE TO RUNNING PRODUCT +* + CLC ; {2C1B} CLEAR CARRY + LDA ]PROD ; {3C2B} ADD PREVIOUS PRODUCT + ADC ]PBAK ; {4C3B} LOBYTE TO CURRENT + STA ]PROD ; {3C2B} AND STORE IN PRODUCT + LDA ]PROD+1 ; {3C2B} DO THE SAME WITH HIBYTES + ADC ]PBAK+1 ; {4C3B} + STA ]PROD+1 ; {3C2B} + CLC ; {2C1B} CLEAR CARRY + LDA ]PROD ; {3C2B} ADD 5 BYTES TO PRODUCT + ADC #5 ; {4C3B} TO ACCOUNT FOR ARRAY HEADER + STA ]PROD ; {3C2B} + LDA ]PROD+1 ; {3C2B} + ADC #0 ; {2C2B} ADJUST HIBYTE + STA ]PROD+1 ; {3C2B} +* +** NOW ADD BASE ADDRESS OF ARRAY TO GET +** THE ADDRESS OF THE INDEX VALUE +* + CLC ; {2C1B} CLEAR CARRY + LDA ]PROD ; {3C2B} ADD PRODUCT TO ARRAY + ADC ]ADDR ; {4C3B} ADDRESS, LOBYTES + STA ADDR2 ; {3C2B} STORE IN ZERO PAGE + LDA ]PROD+1 ; {3C2B} DO THE SAME WITH HIBYTES + ADC ]ADDR+1 ; {4C3B} + STA ADDR2+1 ; {3C2B} + LDY #0 ; {2C2B} RESET BYTE INDEX +* +** COPY FROM SRC ADDR TO DEST ADDR +* +:CLP + LDA (ADDR2),Y ; {6C2B} LOAD BYTE FROM ELEMENT + STA RETURN,Y ; {5C3B} AND STORE IN RETURN + INY ; {2C1B} INCREMENT BYTE COUNTER + CPY ]ESIZE ; {4C3B} IF != ELEMENT LENGTH, + BNE :CLP ; {3C2B} CONTINUE LOOPING + LDA ]ESIZE ; {3C2B} .A = ELEMENT SIZE + STA RETLEN ; {3C2B} ALSO IN RETLEN + LDY ADDR2+1 ; {3C2B} .Y = ELEMENT ADDRESS HIBYTE + LDX ADDR2 ; {3C2B} .X = ELEMENT ADDRESS LOBYTE + RTS ; {6C1B} + +``` + + + +--- + + + +### THE PUT162 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `PUT162` | +| Type | Macro | +| File | `MAC.ARR16B2D.ASM` | +| Author | Nathan Riggs | +| Last Revision | 18-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Place a value in an element of a 16-bit, 2-dimensional array | +| Input | ]1 = Source Address
]2 = Destination Array Address
]3 = First Dimension Element Address
]4 = Second Dimension Element Address | +| Output | none | +| Dependencies | `SUB.APUT162.ASM` | +| Flags Destroyed | NZCV | +| Cycles | 352+ | +| Bytes | 223 | +| Notes | none | +| See Also | `DIM162` `ADIM162` `GET162` `AGET162` `APUT162` | + +--- + +*DETAILS* + +The `PUT162` macro uses the `APUT162` subroutine to place a value in an element of a 16-bit, two-dimensional array. Like other array `PUT` macros and subroutines, `APUT162` only accepts an address that either 1) points to the address of the value to be written, or 2) is the address of the value to be written. This is determined by either sending a literal value as the address (preceded by a # sign), which indicates that the value is held at the given address, or simply by passing the address as it is, which indicates it is an indirect reference. This is how addresses are treated throughout the library as a whole; the difference is that one might expect to send a value in itself as a parameter, when this is simply not the case. In the future, extra functionality may be added to allow for direct and literal placement of a value as a parameter, but this is not currently a pressing issue. + + + +`LISTING 3.45: The PUT162 Macro Source` + +```assembly +* +*``````````````````````````````* +* PUT82 (NATHAN RIGGS) * +* * +* SET VALUE OF AN ELEMENT IN * +* AN 8-BIT, TWO-DIMENSIONAL * +* ARRAY. * +* * +* PARAMETERS * +* * +* ]1 = SOURCE ADDRESS * +* ]2 = DEST ARRAY ADDRESS * +* ]3 = ELEMENT X INDEX * +* ]4 = Y INDEX * +* * +* CYCLES: 352+ * +* SIZE: 223 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +PUT82 MAC + _MLIT ]1;WPAR1 ; {16C12B} PARSE SOURCE ADDRESS + _MLIT ]2;WPAR2 ; {16C12B} PARSE DEST ADDRESS + LDA ]3 ; {3C2B} X INDEX + STA BPAR1 ; {3C2B} + LDA ]4 ; {3C2B} Y INDEX + STA BPAR2 ; {3C2B} + JSR APUT82 ; {308C191B} + <<< +* +``` + + + +--- + + + +### THE APUT162 SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `APUT162` | +| Type | Subroutine | +| File | `SUB.APUT162.ASM` | +| Author | Nathan Riggs | +| Last Revision | 18-MAR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Initialize a two-dimensional, 16-bit array | +| Input | WPAR1 = Source Address
WPAR2 = Array Address
WPA32 = First Dimension Index
ADDR1 = Second Dimension Index | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 378+ | +| Bytes | 220 | +| Notes | None | +| See Also | `DIM162` `ADIM162` `GET162` `AGET162` `PUT162` | + +--- + +*DETAILS* + +The `APUT162` subroutine places a value found at one address into the location of an element in a 16-bit, two-dimensional array. The length to be copied is determined by the array's element length. After executing, the **.A** register holds the length of the value, and the **.X** and **.Y** registers hold the starting address of the element in question (**.X** holds the low byte of the address, while **.Y** holds the high byte). Note that providing an out-of-bounds value for either dimensional index will result in overwriting either data or code that should not be written to, and this will likely crash the system. As such, care should be taken by the programmer to keep all array requests within their given boundaries. + + + +`LISTING 3.46: The APUT182 Subroutine Source` + +```assembly +* +*``````````````````````````````* +* APUT162 (NATHAN RIGGS) * +* * +* PLACE A VALUE HELD IN ONE * +* ADDRESS INTO THE SPECIFIED * +* ELEMENT IN A 16-BIT, TWO- * +* DIMENSIONAL ARRAY. * +* * +*------------------------------* +* MULTIPLICATION ADAPTED FROM * +* WHITE FLAME'S WORK ON * +* CODEBASE64. LICENSE MAY VARY * +*------------------------------* +* * +* INPUT: * +* * +* WPAR1 = SOURCE ADDRESS * +* WPAR2 = ARRAY ADDRESS * +* WPAR3 = 1ST DIM INDEX * +* ADDR1 = 2ND DIM INDEX * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 378+ * +* SIZE: 220 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]ADDRS EQU WPAR1 ; VALUE SOURCE ADDRESS +]ADDRD EQU WPAR2 ; ARRAY ADDRESS +]XIDX EQU WPAR3 ; FIRST DIMENSION INDEX +]YIDX EQU ADDR1 ; SECOND DIMENSION INDEX +* +]ESIZE EQU VARTAB ; ELEMENT LENGTH +]MCAND EQU VARTAB+6 ; MULTIPLICAND +]MLIER EQU VARTAB+8 ; MULTIPLIER +]PBAK EQU VARTAB+10 ; PRODUCT BACKUP +]XLEN EQU VARTAB+12 ; X-DIMENSION LENGTH +]YLEN EQU VARTAB+14 ; Y-DIMENSION LENGTH +]PROD EQU VARTAB+16 ; PRODUCT OF MULTIPLICATION +* +APUT162 + LDY #4 ; {2C2B} LOAD BYTE 4 OF ARRAY + LDA (]ADDRD),Y ; {6C2B} HEADER TO GET ELEM LENGTH + STA ]ESIZE ; {3C2B} + LDY #0 ; {2C2B} LOAD BYTE 0 TO GET + LDA (]ADDRD),Y ; {6C2B} X-DIMENSION LENGTH LOBYTE + STA ]XLEN ; {3C2B} + LDY #1 ; {2C2B} LOAD BYTE 1 TO GET + LDA (]ADDRD),Y ; {6C2B} X-DIMENSION LENGTH HIBYTE + STA ]XLEN+1 ; {3C2B} + LDY #2 ; {2C2B} LOAD BYTE 2 TO GET THE + LDA (]ADDRD),Y ; {6C2B} Y-DIMENSION LENGTH LOBYTE + STA ]YLEN ; {3C2B} + LDY #3 ; {2C2B} LOAD BYTE 3 TO GET THE + LDA (]ADDRD),Y ; {6C2B} Y-DIMENSION LENGTH HIBYTE + STA ]YLEN+1 ; {3C2B} + LDY #0 ; {2C2B} RESET BYTE INDEX +* +** MULTIPLY Y-INDEX BY Y-LENGTH +* + LDA ]YIDX ; {3C2B} LOAD Y-INDEX LOBYTE + STA ]MLIER ; {3C2B} PUT IN MULTIPLIER LOBYTE + LDA ]YIDX+1 ; {3C2B} DO SAME FOR HIBYTES + STA ]MLIER+1 ; {3C2B} + LDA ]YLEN ; {3C2B} PUT Y-DIM LENGTH LOBYTE + STA ]MCAND ; {3C2B} INTO MULTIPLICAND + LDA ]YLEN+1 ; {3C2B} DO SAME FOR HIBYTE + STA ]MCAND+1 ; {3C2B} + LDA #00 ; {2C2B} CLEAR PRODUCT BYTES + STA ]PROD ; {3C2B} + STA ]PROD+1 ; {3C2B} + STA ]PROD+2 ; {3C2B} + STA ]PROD+3 ; {3C2B} + LDX #$10 ; {2C2B} INIT COUNTER TO #16 +:SHIFT_R + LSR ]MLIER+1 ; {6C2B} DIVIDE MULTIPLIER HIBYTE BY 2 + ROR ]MLIER ; {6C2B} ADJUST LOBYTE + BCC :ROT_R ; {3C2B} IF 0 PUT IN CARRY, ROTATE PRODUCT + LDA ]PROD+2 ; {3C2B} LOAD PRODUCT 3RD BYTE + CLC ; {2C1B} CLEAR CARRY + ADC ]MCAND ; {4C3B} ADD MULTIPLICAND + STA ]PROD+2 ; {3C2B} STORE 3RD BYTE + LDA ]PROD+3 ; {3C2B} LOAD PRODUCT HIBYTE + ADC ]MCAND+1 ; {4C3B} ADD MULTIPLICAND HIBYTE +:ROT_R + ROR ; {6C2B} ROTATE .A RIGHT + STA ]PROD+3 ; {3C2B} STORE IN PRODUCT HIBYTE + ROR ]PROD+2 ; {6C2B} ROTATE 3RD BYTE + ROR ]PROD+1 ; {6C2B} ROTATE 2ND + ROR ]PROD ; {6C2B} ROTATE LOBYTE + DEX ; {2C1B} DECREASE X COUNTER + BNE :SHIFT_R ; {3C2B} IF NOT ZERO, LOOP AGAIN +* +** NOW MULTIPLY XIDX BY ELEMENT SIZE +* + LDA ]PROD ; {3C2B} BACKUP PREVIOUS + STA ]PBAK ; {3C2B} PRODUCT FOR USE LATER + LDA ]PROD+1 ; {3C2B} DO SAME FOR HIBYTE + STA ]PBAK+1 ; {3C2B} + LDA ]XIDX ; {3C2B} PUT X-INDEX LOBYTE + STA ]MLIER ; {3C2B} INTO MULTIPLIER + LDA ]XIDX+1 ; {3C2B} DO SAME FOR HIBYTE + STA ]MLIER+1 ; {3C2B} + LDA ]ESIZE ; {3C2B} PUT ELEMENT SIZE + STA ]MCAND ; {3C2B} INTO MULTIPLICAND + LDA #0 ; {2C2B} CLEAR MULTIPLICAND HIBYTE + STA ]MCAND+1 ; {3C2B} +* + STA ]PROD ; {3C2B} CLEAR PRODUCT + STA ]PROD+1 ; {3C2B} + STA ]PROD+2 ; {3C2B} + STA ]PROD+3 ; {3C2B} + LDX #$10 ; {2C2B} INIT X COUNTER TO #16 +:SHIFTR LSR ]MLIER+1 ; {6C2B} DIVIDE MULTIPLIER BY 2 + ROR ]MLIER ; {6C2B} ADJUST LOBYTE + BCC :ROTR ; {3C2B} IF 0 PUT INTO CARRY, ROTATE PROD + LDA ]PROD+2 ; {3C2B} LOAD PRODUCT 3RD BYTE + CLC ; {2C1B} CLEAR CARRY + ADC ]MCAND ; {4C3B} ADD MULTIPLICAND LOBYTE + STA ]PROD+2 ; {3C2B} + LDA ]PROD+3 ; {3C2B} LOAD PRODUCT HIBYTE + ADC ]MCAND+1 ; {4C3B} HAD MULTIPLICAND HIBYTE +:ROTR + ROR ; {6C2B} ROTATE .A RIGHT + STA ]PROD+3 ; {3C2B} STORE PRODUCT HIBYTE + ROR ]PROD+2 ; {6C2B} ROTATE 3RD BYTE + ROR ]PROD+1 ; {6C2B} ROTATE 2ND BYTE + ROR ]PROD ; {6C2B} ROTATE LOBYTE + DEX ; {2C1B} DECREASE X COUNTER + BNE :SHIFTR ; {3C2B} IF NOT 0, KEEP LOOPING +* +** NOW ADD X * ESIZE TO RUNNING PRODUCT +* + CLC ; {2C1B} CLEAR CARRY + LDA ]PROD ; {3C2B} ADD CURRENT PRODUCT + ADC ]PBAK ; {4C3B} TO PREVIOUS PRODUCT + STA ]PROD ; {3C2B} AND STORE BACK IN PRODUCT + LDA ]PROD+1 ; {3C2B} + ADC ]PBAK+1 ; {4C3B} + STA ]PROD+1 ; {3C2B} + CLC ; {2C1B} CLEAR CARRY + LDA ]PROD ; {3C2B} INCREASE LOBYTE BY 5 + ADC #5 ; {2C2B} TO ACCOUNT FOR ARRAY + STA ]PROD ; {3C2B} HEADER + LDA ]PROD+1 ; {3C2B} + ADC #0 ; {2C2B} ADJUST HIBYTE + STA ]PROD+1 ; {3C2B} +* +** ADD ARRAY ADDRESS TO GET INDEX +* + CLC ; {2C1B} CLEAR CARRY + LDA ]PROD ; {3C2B} ADD ARRAY ADDRESS + ADC ]ADDRD ; {4C3B} TO PRODUCT TO GET + STA ADDR2 ; {3C2B} ELEMENT ADDRESS; STORE + LDA ]PROD+1 ; {3C2B} ADDRESS ON ZERO PAGE + ADC ]ADDRD+1 ; {4C3B} + STA ADDR2+1 ; {3C2B} + LDY #0 ; {2C2B} RESET BYTE INDEX +:CLP + LDA (]ADDRS),Y ; {6C2B} LOAD BYTE FROM SOURCE + STA (ADDR2),Y ; {6C2B} STORE AT ELEMENT ADDRESS + INY ; {2C1B} INCREASE BYTE INDEX + CPY ]ESIZE ; {4C3B} IF != ELEMENT LENGTH, LOOP + BNE :CLP ; {3C2B} + LDY ADDR2+1 ; {3C2B} .Y = ELEMENT ADDRESS HIBYTE + LDX ADDR2 ; {3C2B} .X = ELEMENT ADDRESS LOBYTE + LDA ]ESIZE ; {3C2B} .A = ELEMENT LENGTH + RTS ; {6C1B} + +``` + + + +--- + + + +# PART II: THE ARRAYS COLLECTION DEMO FILE + +The Arrays Collection Demo File includes basic methods for calling each macro (and thus subroutine) in the collection, along with substantial commentary that explains how the macros are being used. This demo is not meant to be exhaustive; rather, it simply shows the most common ways to use the collection, and serves as a proof-of-concept rather than a rigorous test of each macro and subroutine. Ultimately, this demo--as well as others in the library--is provided for pedagogical reasons rather than practical reasons, and should serve a beginner well in trying to learn how to use the library, or how to use 6502 Assembly with macros in general. + + + +`LISTING 3.5: The DEMO.ARRAYS.ASM File Source` + +```assembly +* +*``````````````````````````````* +* DEMO.ARRAYS * +* * +* A DECIDEDLY NON-EXHAUSTIVE * +* DEMO OF ARRAY FUNCTIONALITY * +* IN THE APPLEIIASM LIBRARY. * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 18-MAR-2021 * +* ASSEMBLER: MERLIN 8 PRO * +* OS: DOS 3.3 * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** ASSEMBLER DIRECTIVES +* + CYC AVE + EXP OFF + TR ON + DSK DEMO.ARRAYS + OBJ $BFE0 + ORG $6000 +* +*``````````````````````````````* +* TOP INCLUDES (HOOKS,MACROS) * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* + PUT MIN.HEAD.REQUIRED.ASM + USE MIN.MAC.REQUIRED.ASM + USE MIN.MAC.ARR8B1D.ASM + USE MIN.MAC.ARR8B2D.ASM + USE MIN.MAC.ARR16B1D.ASM + USE MIN.MAC.ARR16B2D.ASM + PUT MIN.HEAD.ARRAYS.ASM +* +]HOME2 EQU $FC58 +* +*``````````````````````````````* +* PROGRAM MAIN BODY * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE ARRAY COLLECTION OF THE APPLEIIASM LIBRARY +** CONTAINS THE CORE FUNCTIONALITY NECESSARY FOR +** USING ARRAYS. CURRENTLY, THERE ARE 4 TYPES OF +** ARRAYS SUPPORTED: 8-BIT, 1 DIMENSIONAL ARRAYS, +** WHICH CAN HOLD UP TO 255 KEYS, 8-BIT, 2-DIMENSIONAL +** ARRAYS THAT CAN HOLD UP TO 255 X 255 ELEMENTS +** ACCESSED VIA AN X,Y COORDINATE; 16-BIT, +** 1-DIMENSION ARRAYS THAT CAN HOLD JUST OVER 65K +** ELEMENTS (A MAXIMUM THAT WOULD TAKE UP MOST OF THE +** MEMORY ON A TYPICAL APPLE II SYSTEM), AND 16-BIT, +** 2-DIMENSIONAL ARRAYS THAT CAN HOLD A MAXIMUM OF +** 65K X 65K ELEMENTS ACCESSED VIA X,Y COORDINATES. +* +** AS IT IS, EACH TYPE OF ARRAY HAS THREE MACROS DEDICATED +** TO IT: AN ARRAY INITIALIZATION MACRO (DIM), A MACRO +** THAT GETS AN ELEMENT VALUE, AND A MACRO THAT ENTERS +** A VALUE AT THE SPECIFIED INDEX ELEMENT. SUPPORT FOR +** SPECIFIC MACROS AND SUBROUTINES THAT ACT ON ARRAYS, SUCH +** AS SORTING ALGORITHMS, MERGING, SEARCHING AND SO ON, +** ARE LIKELY TO BE ADDED IN THE FUTURE, BUT THIS WILL +** REQUIRE A SECOND DISK DEDICATED TO ARRAYS, GIVEN SPACE +** CONSTRAINTS. SUPPORT FOR 8-BIT 3-DIMENSIONAL ARRAYS +** MAY ALSO BE SUPPORTED, BUT THAT DEPENDS ON NECESSITY OR +** DEMAND. +* +** THIS DEMO FILE SHOWS THE USAGE AND PERFORMANCE OF THE +** FOLLOWING MACROS (AND THE SUBROUTINES BEHIND THEM): +* +** DIM81 : INITIALIZE AN 8-BIT, 1 DIMENSIONAL ARRAY +** GET81 : RETRIEVE ELEMENT VALUE FROM 8-BIT, 1-D ARRAY +** PUT81 : PLACE VALUE IN ELEMENT OF 8-BIT, 1-D ARRAY +** DIM82 : INITIALIZE AN 8-BIT, 2 DIMENSIONAL ARRAY +** GET82 : RETRIEVE ELEMENT VALUE FROM 8-BIT, 2-D ARRAY +** PUT82 : PLACE VALUE IN ELEMENT OF 8-BIT, 2-D ARRAY +** DIM161 : INITIALIZE A 16-BIT, 1 DIMENSIONAL ARRAY +** GET161 : RETRIEVE ELEMENT VALUE FROM 16-BIT, 1-D ARRAY +** PUT161 : PLACE VALUE IN ELEMENT OF 16-BIT, 1-D ARRAY +** DIM162 : INITIALIZE A 16-BIT, 2 DIMENSIONAL ARRAY +** GET162 : RETRIEVE ELEMENT VALUE FROM 16-BIT, 2-D ARRAY +** PUT162 : PLACE VALUE IN ELEMENT OF 16-BIT, 2-D ARRAY +* +*``````````````````````````````* +* 8-BIT ARRAY MACRO EXAMPLES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** SINCE THESE ROUTINES DO NOT NORMALLY OUTPUT ANYTHING +** ON THEIR OWN, WE WILL BE USING THE DUMP MACRO FROM THE +** REQUIRED LIBRARY COLLECTION IN ORDER TO PEEK AT THE +** ARRAY CONTENTS. +* +** FIRST, LET'S TACKLE ONE-DIMENSIONAL 8-BIT ARRAYS. WE START +** BY INITIALIZING THE ARRAY, WHICH ALWAYS MUST BE DONE BEFORE +** ANY OTHER USE OF AN ARRAY. WE DO THIS WITH THE DIM81 MACRO, +** AS SUCH: +* + DIM81 #$300;#10;#2;#$EE +* +** HERE WE ARE INITIALIZING AN ARRAY AT THE ADDRESS #$300, +** WITH 10 ELEMENTS THAT ARE EACH 2 BYTES LONG. EACH ELEMENT +** IS FILLED WITH THE VALUE #$EE TO BEGIN WITH. NOW WE CAN +** USE DUMP TO VIEW THE ARRAY: +* + JSR ]HOME2 + _PRN "8-BIT, 1-DIMENSIONAL ARRAYS",8D + _PRN "===========================",8D8D8D + DUMP #$300;#2 + DUMP #$302;#10 + DUMP #$30C;#10 + _PRN " ",8D8D + _WAIT +* +** IT IS IMPORTANT TO NOTE HOW WE ARE VIEWING THE ARRAY HERE. +** FIRST, WE ARE DUMPING THE FIRST TWO BYTES OF THE ARRAY; +** THESE HOLD THE NUMBER OF ELEMENTS IN THE ARRAY AND THE +** LENGTH OF EACH ELEMENT, RESPECTIVELY. AFTERWARDS, WE +** DUMP THE FOLLOWING 20 BYTES, DETERMINED BY THE ELEMENT +** SIZE TIMES THE NUMBER OF ELEMENTS, WHICH HOLDS THE DATA +** OF THE ARRAY. ALL FOUR TYPES OF ARRAYS ARE BUILT IN A +** SIMILAR FASHION, BUT VARY BASED ON THE NEEDS OF EACH TYPE. +** THE DIFFERENCES WILL BE EASY TO DISCERN ONCE WE DUMP +** A 2-DIMENSIONAL ARRAY. +* +** NOW WE CAN PUT A VALUE IN AN ELEMENT. THIS IS EASILY DONE VIA +** THE PUT81 MACRO: +* + PUT81 #]WORD1;#$300;#5 + DUMP #$300;#2 + DUMP #$302;#10 + DUMP #$30C;#10 + _PRN " ",8D8D + _WAIT +* +** TWO THINGS SHOULD BECOME APPARENT AFTER VIEWING THE +** CONTENTS OF THIS ARRAY, AND THESE EXTEND TO ALL TYPES +** OF ARRAYS: FIRST, AN ITEM IS COPIED TO THE ARRAY FROM +** ANOTHER MEMORY ADDRESS, AND THE NUMBER OF BYTES COPIED +** IS DETERMINED BY THE ELEMENT LENGTH OF THE ARRAY; +** SECOND, IT SEEMS AT FIRST AS THOUGH THE ELEMENT ALTERED +** IS AT INDEX 6 RATHER THAN 5, BUT THIS IS BECAUSE ARRAYS +** START AT ELEMENT 0. NOT ELEMENT 1. WHILE THERE WOULD NOT +** BE MUCH OF A PROBLEM TREATING ARRAYS AS STARTING AT +** ELEMENT 1, PROVIDED YOU ARE CAREFUL NOT TO OVERFLOW THE +** ARRAY, INDEXING MIGHT GET CONFUSING. +* +** NOW WE CAN GET BACK THE VALUE WE PLACED IN THE ARRAY +** WITH THE GET81 MACRO, AS SUCH: +* + GET81 #$300;#5 + DUMP #RETURN;RETLEN + _WAIT +* +** NOTE HERE THAT THE VALUE IS PLACED IN #RETURN, AND ITS +** LENGTH IS PLACED IN RETLEN. THIS IS HOW ALL ARRAY GET +** MACROS WORK. +* +** NOW THAT WE HAVE ONE TYPE OF ARRAY DOWN, THE REST OF THEM +** ARE SIMPLE TO UNDERSTAND BECAUSE THEY ALL WORK THE SAME +** WAY. LET'S LOOK AT HOW TWO-DIMENSION ARRAYS ARE HANDLED AT +** THE 8-BIT LEVEL TO HAVE A MORE COMPLETE UNDERSTANDING BEFORE +** MOVING TO 16 BITS, HOWEVER. FIRST, WE INITIALIZE THE ARRAY: +* + JSR ]HOME2 + _PRN "8-BIT, 2-DIMENSIONAL ARRAYS",8D + _PRN "---------------------------",8D8D8D + DIM82 #$300;#5;#5;#1;#$AA + DUMP #$300;#3 + DUMP #$303;#5 + DUMP #$308;#5 + DUMP #$30D;#5 + DUMP #$312;#5 + DUMP #$317;#5 + _WAIT + _PRN " ",8D8D +* +** AND NOW THAT WE HAVE DECLARED THE 2D, 8BIT ARRAY, +** ADDING A VALUE AND RETRIVING IT IS EASY: +* + PUT82 #]WORD1;#$300;#3;#3 + GET82 #$300;#3;#3 + DUMP #RETURN;RETLEN + _WAIT +* +* +** IN THE ABOVE CODE, YOU CAN SEE THAT WE INITIALIZE +** THE 2-DIMENSIONAL ARRAY AT #$300, WHICH WRITES OVER +** THE PREVIOUS 1-DIMENSIONAL ARRAY. THERE ARE NO +** PROTECTIONS AGAINST THIS; YOU ARE EXPECTED TO KEEP +** TRACK OF THE LOCATIONS YOURSELF. AFTER THE ADDRESS, +** THE LENGTHS OF EACH DIMENSION ARE DECLARED (IN THIS +** CASE, 5 EACH), THEN THE LENGTH OF EACH ELEMENT IN +** THE ARRAY. FINALLY, THE DEFAULT VALUE, #$AA, IS +** PROVIDED. THE ARRAY IS THEN LISTED WITH THE DUMP +** COMMAND, 5 ELEMENTS AT A TIME. +* +** NOW ARMED WITH EXAMPLES OF 1 DIMENSIONAL AND TWO +** DIMENSIONAL ARRAYS, IT CAN BE EASILY INFERRED HOW +** THE REST OF THE MACROS WORK. THE ONLY REAL DIFFERENCE +** IS THAT 16-BIT ARRAYS USE TWO BYTES FOR THE NUMBER OF +** ELEMENTS IN EACH DIMENSION, RATHER THAN A SINGLE BYTE +** (THUS THE 16-BIT DESCRIPTION). THUS, WE CAN DECLARE +** A 16-BIT, 1-DIMENSIONAL ARRAY AS WELL AS PUT AND GET +** TO AND FROM IT AS SUCH: +* + JSR ]HOME2 + DIM161 #$300;#10;#1;#$55 + PUT161 #]WORD1;#$300;#3 + GET161 #$300;#3 + _PRN "16-BIT, 1-DIMENSIONAL ARRAYS",8D + _PRN "============================",8D8D8D + DUMP #$300;#3 + DUMP #$303;#10 + _PRN " ",8D8D + DUMP #RETURN;RETLEN + _WAIT +* +** AND AS YOU WOULD EXPECT, 16-BIT 2 DIMENSIONAL ARRAYS +** WORK IN MUCH THE SAME WAY: +* + JSR ]HOME2 + DIM162 #$300;#3;#3;#1;#$66 + PUT162 #]WORD1;#$300;#0;#0 + GET162 #$300;#0;#0 + _PRN "16-BIT, 2-DIMENSIONAL ARRAYS",8D + _PRN "============================",8D8D8D + DUMP #$300;#5 + DUMP #$305;#3 + DUMP #$308;#3 + DUMP #$30B;#3 + _PRN " ",8D8D + DUMP #RETURN;RETLEN + _WAIT + JSR ]HOME2 + _PRN " ",8D8D + _PRN "FIN.",8D8D +* + JMP REENTRY +* +*``````````````````````````````* +* BOTTOM INCLUDES (ROUTINES) * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* + PUT MIN.LIB.REQUIRED.ASM +* +** INDIVIDUAL SUBROUTINE INCLUDES +* +** 8-BIT 1-DIMENSIONAL ARRAY SUBROUTINES +* + PUT MIN.SUB.ADIM81.ASM + PUT MIN.SUB.AGET81.ASM + PUT MIN.SUB.APUT81.ASM +* +** 8-BIT 2-DIMENSIONAL ARRAY SUBROUTINES +* + PUT MIN.SUB.ADIM82.ASM + PUT MIN.SUB.AGET82.ASM + PUT MIN.SUB.APUT82.ASM +* +** 16-BIT 1-DIMENSIONAL ARRAYS +* + PUT MIN.SUB.ADIM161.ASM + PUT MIN.SUB.APUT161.ASM + PUT MIN.SUB.AGET161.ASM +* +** 16-BIT 2-DIMENSIONAL ARRAYS +* + PUT MIN.SUB.ADIM162.ASM + PUT MIN.SUB.APUT162.ASM + PUT MIN.SUB.AGET162.ASM +* +** VARIABLES +* +]WORD1 HEX FFFF + +``` + + + diff --git a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/33.0 Detailed_Reference_D4_MATH.md b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/33.0 Detailed_Reference_D4_MATH.md new file mode 100644 index 0000000..a77c79f --- /dev/null +++ b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/33.0 Detailed_Reference_D4_MATH.md @@ -0,0 +1,5473 @@ +# Disk 4 : Basic Integer Math and Pseudorandom Numbers + + + +- [Part I: The Math Collection](#part-i-the-math-collection) + + - [Math Components](#math-components) + - [Math Header File](#math-header-file) + - [Math Macros and Subroutines](#math-macros-and-subroutines) + - [Math By Constants: 8-bit Division](#math-by-constants-macros-8-bit-division) + - [The D8BY2 Macro](#the-d8by2-macro) + - [The D8BY3 Macro](#the-d8by3-macro) + - [The D8BY4 Macro](#the-d8by4-macro) + - [The D8BY5 Macro](#the-d8by5-macro) + - [The D8BY6 Macro](#the-d8by6-macro) + - [The D8BY7 Macro](#the-d8by7-macro) + - [The D8BY8 Macro](#the-d8by8-macro) + - [The D8BY9 Macro](#the-d8by9-macro) + - [The D8BY10 Macro](#the-d8by10-macro) + - [Math By Constants: 8-bit Multiplication](#math-by-constants-macros-8-bit-multiplication) + - [The M8BY2 Macro](#the-m8by2-macro) + - [The M8BY3 Macro](#the-m8by3-macro) + - [The M8BY4 Macro](#the-m8by4-macro) + - [The M8BY5 Macro](#the-m8by5-macro) + - [The M8BY6 Macro](#the-m8by6-macro) + - [The M8BY7 Macro](#the-m8by7-macro) + - [The M8BY8 Macro](#the-m8by8-macro) + - [The M8BY9 Macro](#the-m8by9-macro) + - [The M8BY10 Macro](#the-m8by10-macro) + - [Math By Constants: 16-bit Multiplication](#math-by-constants-macros-16-bit-multiplication) + - [The M16BY2 Macro](#the-m16by2-macro) + - [The M16BY3 Macro](#the-m16by3-macro) + - [The M16BY4 Macro](#the-m16by4-macro) + - [The M16BY5 Macro](#the-m16by5-macro) + - [The M16BY6 Macro](#the-m16by6-macro) + - [The M16BY7 Macro](#the-m16by7-macro) + - [The M16BY8 Macro](#the-m16by8-macro) + - [The M16BY9 Macro](#the-m16by9-macro) + - [The M16BY10 Macro](#the-m16by10-macro) + - [The M16BY1H Macro](#the-m16by1h-macro) + - [General Purpose Math: 8-bit Macros and Subroutines](#general-purpose-math-8-bit-procedures) + - [The ADD8 Macro](#the-add8-macro) + - [The SUB8 Macro](#the-sub8-macro) + - [The MUL8 Macro](#the-mul8-macro) + - [The MULTU8 Subroutine](#the-multu8-subroutine) + - [The DIV8 Macro](#the-div8-macro) + - [The DIVDU8 Subroutine](#the-divdu8-subroutine) + - [General Purpose Math: 16-bit Macros and Subroutines](#general-purpose-math-16-bit-procedures) + - [The ADD16 Macro](#the-add16-macro) + - [The ADDIT16 Subroutine](#the-addit16-subroutine) + - [The SUB16 Macro](#the-sib16-macro) + - [The SUBT16 Subroutine](#the-subt16-subroutine) + - [The MUL16 Macro](#the-mul16-macro) + - [The MULTU16 Subroutine](#the-multu16-subroutine) + - [The DIV16 Macro](#the-div16-macro) + - [The DIVDU16 Subroutine](#the-divdu16-subroutine) + - [The CMP16 Macro](#the-cmp16-macro) + - [The COMP16 Subroutine](#the-comp16-subroutine) + - [Pseudorandom Number Generation Macros and Subroutines](#pseudorandom-number-generation-macros-and-subroutines) + - [The RNDEOR Macro](#the-rndeor-macro) + - [The RNDMZ Macro](#the-rndmz-macro) + - [The RND8 Macro](#the-rnd8-macro) + - [The RAND8 Subroutine](#the-rand8-subroutine) + - [The RAND Macro](#the-rand-macro) + - [The RANDB Subroutine](#the-randb-subroutine) + - [The RND16 Macro](#the-rnd16-macro) + - [The RAND16 Subroutine](#the-rand16-subroutine) + +- [Part II: Math Collection Demonstrations](@part-ii-math-collection-demonstrations) + + + + + +--- + + + + +## Part I: The Math Collection + + + +The fourth disk in the library is dedicated to Integer Math and Pseudorandom Number Generation. For the most part, this collection is limited to basic math routines only due to disk size constraints; the routines and macros for all of the current implementations already occupy most of the disk space available. This includes, somewhat meagerly, macros and subroutines for: + +- 8-bit Addition +- 8-bit Subtraction +- 8-bit Multiplication +- 8-bit Division +- 16-bit Addition +- 16-bit Subtraction +- 16-bit Multiplication +- 16-bit Division +- 8-bit Pseudorandom Number Generation +- 16-bit Pseudorandom Number Generation +- Ranged Pseudorandom Number Generation +- Two Randomization Techniques + +Currently, all operations are for unsigned integers, though this will likely be the last addition to the collection in the next revision (accounting for negative numbers requires using wrappers that predetermine the sign value before multiplying or dividing, making the added number of bytes somewhat trivial). While a math routine and macro disk is planned for the Extended AppleIIAsm Library disks, including routines for finding percentages, floating point math, greatest common factors and least common multiples and more, the appearance of the Extended Library disks will not be any time soon. + + + +--- + + + +## Math Components + + + +The Math Collection contains the following components: + +- A header file that contains the various hooks, vectors and memory space used by the entire Math Collection. +- Macros and subroutines for basic integer math as well as pseudorandom numbers. The basic math macros and subroutines are further separated into "math-by" items that calculate by a given integer constant and general purpose items that calculate by arbitrary numbers. +- Demonstrations of all macros and subroutines in the collection, ordered by type: "math-by", general purpose basic math, and pseudorandom numbers. + + + +--- + + + +## Header File + +The mathematics header file includes a number of vectors and spans of address space for use by the rest of the collection. The header is especially important for pseudorandom number generation, as it holds the location for the seed (both low byte and high byte) as well as a table used to determine the value to `EOR` the seed by in the process of number generation. This table is 64 bytes long, and contains "magic numbers" that allow for the generation of a complete set of 65,536 different numbers before being repeated, guaranteeing that 1) with the same number and `EOR` value, the number generated can be reproduced, which allows for stepping back and forth between the generated numbers if needed, and 2) maximum variability is achieved, although this may come at the cost of statistical unlikeliness. The table is used by the `RNDMZ` macro for randomization of the generator, but the `EOR` value can also be directly altered by the `RNDEOR` macro when a specific `EOR` value is desired. + + + +| Condition | Value | +| ------------- | ------------------------------------------------------------ | +| Name | File: HEAD.MATH.ASM | +| Type | Header File | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin 8 Pro | +| OS | Apple DOS 3.3 | +| Purpose | Provide appropriate hooks and routines for the Math collection | +| Dependencies | none | +| Bytes | 0 | +| Notes | Repeatedly used subroutines by the library may be placed here in the future | +| See Also | none | + +--- + +*DETAILS* + +`Listing 4.0` provides the complete source of the header file. + + + +`Listing 4.0: HEAD.MATH.ASM Source` + +```assembly +* +*``````````````````````````````* +* HEAD.MATH.ASM * +* * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 22-MAR-2021 * +* ASSEMBLER: MERLIN 8 PRO * +* OS: DOS 3.3 * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +GETNUM EQU $FFA7 ; ASCII TO HEX IN 3E & 3F +RNDL EQU $4E ; RANDOM NUMBER LOW +RNDH EQU $4F ; RANDOM NUMBER HIGH +* +FAC EQU $9D ; FLOATING POINT ACCUM +FSUB EQU $E7A7 ; FLOATING POINT SUBTRACT +FADD EQU $E7BE +FMULT EQU $E97F ; FP MULTIPLY +FDIV EQU $EA66 ; FP DIVIDE +FMULTT EQU $E982 +FDIVT EQU $EA69 +FADDT EQU $E7C1 +FSUBT EQU $E7AA +* +MOVFM EQU $EAF9 ; MOVE FAC > MEM +MOVMF EQU $EB2B ; MOVE MEM > FAC +NORM EQU $E82E +CONUPK EQU $E9E3 +* +FLOG EQU $E941 ; LOGARITHM +FSQR EQU $EE8D ; SQUARE ROOT +FCOS EQU $EFEA ; FP COSINE +FSIN EQU $EFF1 ; SINE +FTAN EQU $F03A ; TANGENT +FATN EQU $F09E ; ATANGENT +* +** THE FOLLOWING IS A SERIES OF "MAGIC NUMBERS" +** TO USE AS SEEDS FOR PSEUDO-RANDOM NUMBER GENERATION. +** THESE CAN BE FOUND ON CODEBASE64, BUT ARE KNOWN +** WELL ENOUGH TO BE CONSIDERED COMMON KNOWLEDGE. +* + JMP ]MATH_GO +]MAGEOR HEX 002D +]MAGNUM HEX 002D0039003F005300BD00D7012F013D + HEX 014F015D019701A101AD01BF01C70215 + HEX 02190225022F025D026D0285029102A1 + HEX 02E5031D034B036903710387038D039F + HEX 03A303DD03F904290457046704830489 + HEX 049104BF04C1053305470569058705C3 + HEX 05DD05EB0641064B0653068B06C3076B + HEX 076D0779078307F1080D086108BF08D5 +]MATH_GO +* + +``` + + + +--- + + + +## Macros and Subroutines + + + +The Basic Math Collection is divided into six different macro categories: 8-bit basic math, 16-bit basic math, 8-bit constant operations, 16-bit constant operations, and pseudorandom number generation. As always, 8-bit operations should be used whenever possible, as significantly fewer bytes and cycles are used in comparison to 16-bit operations. + +Additionally, math by constants should be used whenever one of the arguments is already known; that is, the programmer knows that something will be multiplied by three, or ten, or thirty. While macros do not exist for numbers above ten (except for 100), the answers can be easily derived from using two or more of the math-by macros. For instance, to get multiplication by thirty, first multiply by 10, then multiply that by three. Although this seems like extra work, the number of cycles saved here can be substantial, as compared to using the general purpose macros. If speed is a concern, then math-by-constants is the way to go. + + + +--- + + + +### Math By Constants Macros: 8-bit Division + +These macros are dedicated to dividing a given 8-bit number by a known constant. Since the constant is known, we are able to precisely measure the number of cycles and bytes needed for the operation, and it takes significantly fewer of both to return a result than would using a general purpose division implementation. + +It should be noted that these macros do not take sign into consideration, and there are no plans to make wrappers for the math-by macros; only unsigned numbers are accepted. For signed operations, either use the general purpose division macros or create wrappers specific to each divide-by-constant macro. + +`LISTING 4.10: MAC.D8BY.ASM Header Source` + +```assembly +* +*``````````````````````````````* +* MAC.M8BY.ASM * +* * +* THESE MACROS CONSIST OF 8BIT * +* DIVISION BY A CONSTANT * +* ROUTINES. WHILE SOME OF * +* THESE ARE TRIVIAL, MOST HAVE * +* BEEN TAKEN FROM THE WORK OF * +* OMEGAMATRIX AS SHARED ON * +* THE NESDEV FORUMS. AS SUCH, * +* LICENSING MAY VARY; HOWEVER, * +* SINCE THEY WERE FREELY * +* SHARED, IT SHOULD BE SAFELY * +* ASSUMED THAT THEY ARE PUBLIC * +* DOMAIN. * +* * +* NOTE: THESE ARE ^HIGHLY^ * +* OPTIMIZED BY BRUTE FORCE ON * +* A MODERN SYSTEM, AND SOME OF * +* THESE MAY EXPLOIT QUIRKS OF * +* THE 6502. TO SUM THAT UP: * +* THESE ARE NOT ROUTINES THAT * +* ARE EASILY CUT THROUGH BY A * +* NOVICE, EVEN THOUGH THEY * +* LOOK SIMPLE ENOUGH. * +* * +* AUTHOR: NATHAN RIGGS * +* (COPIED FROM OMEGAMATRIX) * +* * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 29-MAR-2021 * +* ASSEMBLER: MERLIN 8 PRO * +* OS: DOS 3.3 * +* * +* LIST OF MACROS * +* * +* D8BY2 : DIVIDE BY TWO * +* D8BY3 : DIVIDE BY THREE * +* D8BY4 : DIVIDE BY FOUR * +* D8BY5 : DIVIDE BY FIVE * +* D8BY6 : DIVIDE BY SIX * +* D8BY7 : DIVIDE BY SEVEN * +* D8BY8 : DIVIDE BY EIGHT * +* D8BY9 : DIVIDE BY NINE * +* D8BY10 : DIVIDE BY TEN * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* + +``` + + + +--- + + + +### THE D8BY2 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------- | +| Name | `D8BY2` | +| Type | Macro | +| File | `MAC.D8BY.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Divide an 8-bit value by two | +| Input | ]1 = Number to divide | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 6 | +| Bytes | 4 | +| Notes | None | +| See Also | `M8BY2` `M16BY2` | + +--- + +*DETAILS* + +The `D8BY2` macro divides a given number by two using a left shift (`LSR`). The result is floored, and no remainder is provided; it is passed back via the **.A** register. + + + +`LISTING 4.11: The D8BY2 Macro Source` + +```assembly +* +*``````````````````````````````* +* D8BY2 (NATHAN RIGGS) * +* * +* DIVIDE 8-BIT NUMBER BY TWO * +* * +* PARAMETERS * +* * +* ]1 = NUMBER TO DIVIDE * +* * +* CYCLES: 6 * +* SIZE: 4 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +D8BY2 MAC + LDA ]1 ; {4C3B} + LSR ; {2C1B} SHIFT BITS RIGHT IN .A + ; TO DIVIDE BY TWO. ANSWER REMAINS + ; IN .A REGISTER + <<< + +``` + + + +--- + + + +### THE D8BY3 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------ | +| Name | `D8BY3` | +| Type | Macro | +| File | `MAC.D8BY.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Divide an 8-bit value by three | +| Input | ]1 = Number to divide | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 29 | +| Bytes | 19 | +| Notes | None | +| See Also | `M8BY3` `M16BY3` | + +--- + +*DETAILS* + +The `DIV8BY3` macro divides a given 8-bit number by three. + + + +`LISTING 4.12: D8BY3 Macro Source` + +```assembly +* +*``````````````````````````````* +* D8BY3 (NATHAN RIGGS) * +* * +* DIVIDE 8-BIT NUMBER BY THREE * +* * +* PARAMETERS * +* * +* ]1 = NUMBER TO DIVIDE * +* * +* CYCLES: 29 * +* SIZE: 19 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +D8BY3 MAC + LDA ]1 ; {4C3B} + CLC ; {2C2B} CLEAR THE CARRY + STA BPAR1 ; {3C2B} STORE ORIG NUM IN Z PAGE + LSR ; {2C1B} DIVIDE .A BY TWO + ADC #21 ; {2C2B} ADD 21 AS A CORRECTION + LSR ; {2C1B} DIVIDE .A BY TWO + ADC BPAR1 ; {3C2B} ADD ORIGINAL VALUE TO NEW + ROR ; {2C1B} ROTATE A TO NEXT BIT + LSR ; {2C1B} SHIFT RIGHT TO DIVIDE BY 2 + ADC BPAR1 ; {3C2B} ADD ORIGINAL VALUE + ROR ; {2C1B} ROTATE .A TO NEXT BIT + LSR ; {2C1B} DIVIDE BY 2 + <<< +* + +``` + + + +--- + + + +### THE D8BY4 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ----------------------------- | +| Name | `D8BY4` | +| Type | Macro | +| File | `MAC.D8BY.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Divide an 8-bit value by four | +| Input | ]1 = Number to divide | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 8 | +| Bytes | 5 | +| Notes | None | +| See Also | `M8BY4` `M16BY4` | + +--- + +*DETAILS* + +The `DIV8BY4` macro divides a given 8-bit number by four. + + + +`LISTING 4.13: D8BY4 Macro Source` + +```assembly +* +*``````````````````````````````* +* D8BY4 (NATHAN RIGGS) * +* * +* DIVIDE 8-BIT NUMBER BY FOUR * +* * +* PARAMETERS * +* * +* ]1 = NUMBER TO DIVIDE * +* * +* CYCLES: 8 * +* SIZE: 5 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +D8BY4 MAC + LDA ]1 ; {4C3B} + LSR ; {2C1B} DIVIDE .A BY TWO + LSR ; {2C1B} DIVIDE .A BY TWO + <<< +* + +``` + + + +--- + + + +### THE D8BY5 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ----------------------------- | +| Name | `D8BY5` | +| Type | Macro | +| File | `MAC.D8BY.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Divide an 8-bit value by five | +| Input | ]1 = Number to divide | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 34 | +| Bytes | 21 | +| Notes | None | +| See Also | `M8BY5` `M16BY5` | + +--- + +*DETAILS* + +The `DIV8BY4` macro divides a given 8-bit number by five. + + + +`LISTING 4.14: D8BY5 Macro Source` + +```assembly +* +*``````````````````````````````* +* D8BY5 (NATHAN RIGGS) * +* * +* DIVIDE 8-BIT NUMBER BY FIVE * +* * +* PARAMETERS * +* * +* ]1 = NUMBER TO DIVIDE * +* * +* CYCLES: 34 * +* SIZE: 21 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +D8BY5 MAC + LDA ]1 ; {4C3B} + STA BPAR1 ; {3C2B} STORE ORIG ON ZERO PAGE + LSR ; {2C1B} DIVIDE BY TWO + ADC #13 ; {2C2B} ADD 13 AS A CORRECTIVE + ADC BPAR1 ; {3C2B} ADD ORIGINAL + ROR ; {2C1B} ROTATE BITS RIGHT + LSR ; {2C1B} DIVIDE BY TWO + LSR ; {2C1B} DIVIDE BY TWO + ADC BPAR1 ; {3C2B} ADD ORIGINAL + ROR ; {2C1B} ROTATE BITS RIGHT + ADC BPAR1 ; {3C2B} ADD ORIGINAL VALUE + ROR ; {2C1B} ROTATE BITS RIGHT + LSR ; {2C1B} DIVIDE BY TWO + LSR ; {2C1B} DIVIDE BY TWO + <<< +* + +``` + + + +--- + + + +### THE D8BY6 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------- | +| Name | `D8BY6` | +| Type | Macro | +| File | `MAC.D8BY.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Divide an 8-bit value by six | +| Input | ]1 = Number to divide | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 36 | +| Bytes | 21 | +| Notes | None | +| See Also | `M8BY6` `M16BY6` | + +--- + +*DETAILS* + +The `DIV8BY6` macro divides a given 8-bit number by six. + + + +`LISTING 4.15: D8BY5 Macro Source` + +```assembly +* +*``````````````````````````````* +* D8BY6 (NATHAN RIGGS) * +* * +* DIVIDE 8-BIT NUMBER BY SIX * +* * +* PARAMETERS * +* * +* ]1 = NUMBER TO DIVIDE * +* * +* CYCLES: 36 * +* SIZE: 21 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +D8BY6 MAC + LDA ]1 ; {4C3B} + CLC ; {2C1B} + LSR ; {2C1B} DIVIDE .A BY TWO + STA BPAR1 ; {3C2B} STORE IN ZERO PAGE + LSR ; {2C1B} DIVIDE AGAIN + LSR ; {2C1B} DIVIDE AGAIN + ADC BPAR1 ; {3C2B} ADD VALUE/2 + ROR ; {2C1B} ROTATE BITS RIGHT + LSR ; {2C1B} DIVIDE BY TWO + ADC BPAR1 ; {3C2B} ADD VALUE/2 + ROR ; {2C1B} ROTATE RIGHT + LSR ; {2C1B} DIVIDE BY TWO + ADC BPAR1 ; {3C2B} ADD VALUE/2 + ROR ; {2C1B} ROTATE BITS RIGHT + LSR ; {2C1B} DIVIDE BY TWO + <<< +* + +``` + + + +--- + + + +### THE D8BY7 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------ | +| Name | `D8BY7` | +| Type | Macro | +| File | `MAC.D8BY.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Divide an 8-bit value by seven | +| Input | ]1 = Number to divide | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 33 | +| Bytes | 19 | +| Notes | None | +| See Also | `M8BY7` `M16BY7` | + +--- + +*DETAILS* + +The `DIV8BY7` macro divides a given 8-bit number by seven. + + + +`LISTING 4.16: D8BY7 Macro Source` + +```assembly +* +*``````````````````````````````* +* D8BY7 (NATHAN RIGGS) * +* * +* DIVIDE 8-BIT NUMBER BY SEVEN * +* * +* PARAMETERS * +* * +* ]1 = NUMBER TO DIVIDE * +* * +* CYCLES: 33 * +* SIZE: 19 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +D8BY7 MAC + LDA ]1 ; {4C3B} + CLC ; {2C1B} CLEAR CARRY + STA BPAR1 ; {3C2B} STORE NUMBER IN ZERO PAGE + LSR ; {2C1B} DIVIDE .A BY TWO + LSR ; {2C1B} AGAIN + LSR ; {2C1B} AGAIN + ADC BPAR1 ; {3C2B} ADD ORIGINAL NUMBER + ROR ; {2C1B} ROTATE BITS RIGHT + LSR ; {2C1B} DIVIDE BY TWO + LSR ; {2C1B} DIVIDE BY TWO + ADC BPAR1 ; {3C2B} ADD ORIGINAL NUMBER + ROR ; {2C1B} ROTATE BITS RIGHT + LSR ; {2C1B} DIVIDE BY TWO + LSR ; {2C1B} DIVIDE BY TWO + <<< +* + +``` + + + +--- + + + +### THE D8BY8 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------ | +| Name | `D8BY8` | +| Type | Macro | +| File | `MAC.D8BY.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Divide an 8-bit value by eight | +| Input | ]1 = Number to divide | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 10 | +| Bytes | 6 | +| Notes | None | +| See Also | `M8BY8` `M16BY8` | + +--- + +*DETAILS* + +The `DIV8BY8` macro divides a given 8-bit number by eight. + + + +`LISTING 4.17: D8BY8 Macro Source` + +```assembly +* +*``````````````````````````````* +* D8BY8 (NATHAN RIGGS) * +* * +* DIVIDE 8-BIT NUMBER BY EIGHT * +* * +* PARAMETERS * +* * +* ]1 = NUMBER TO DIVIDE * +* * +* CYCLES: 10 * +* SIZE: 6 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +D8BY8 MAC + LDA ]1 ; {4C3B} + LSR ; {2C1B} DIVIDE .A BY TWO (2) + LSR ; {2C1B} DIVIDE BY TWO (4) + LSR ; {2C1B} DIVIDE BY TWO (8) + <<< +* + +``` + + + +--- + + + +### THE D8BY9 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ----------------------------- | +| Name | `D8BY9` | +| Type | Macro | +| File | `MAC.D8BY.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Divide an 8-bit value by nine | +| Input | ]1 = Number to divide | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 36 | +| Bytes | 21 | +| Notes | None | +| See Also | `M8BY9` `M16BY9` | + +--- + +*DETAILS* + +The `DIV8BY9` macro divides a given 8-bit number by nine. + + + +`LISTING 4.18: D8BY9 Macro Source` + +```assembly +* +*``````````````````````````````* +* D8BY9 (NATHAN RIGGS) * +* * +* DIVIDE 8-BIT NUMBER BY NINE * +* * +* PARAMETERS * +* * +* ]1 = NUMBER TO DIVIDE * +* * +* CYCLES: 36 * +* SIZE: 21 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +D8BY9 MAC + LDA ]1 ; {4C3B} + CLC ; {2C1B} CLEAR CARRY + STA BPAR1 ; {3C2B} STORE .A IN ZERO PAGE + LSR ; {2C1B} DIVIDE .A BY TWO + LSR ; {2C1B} AGAIN + LSR ; {2C1B} AGAIN + ADC BPAR1 ; {3C2B} ADD ORIGINAL NUMBER + ROR ; {2C1B} ROTATE BITS RIGHT + ADC BPAR1 ; {3C2B} ADD ORIGINAL NUMBER + ROR ; {2C1B} ROTATE BITS RIGHT + ADC BPAR1 ; {3C2B} ADD ORIGINAL NUMBER + ROR ; {2C1B} ROTATE BITS RIGHT + LSR ; {2C1B} DIVIDE BY TWO + LSR ; {2C1B} DIVIDE BY TWO + LSR ; {2C1B} DIVIDE BY TWO + <<< +* + +``` + + + +--- + + + +### THE D8BY10 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------- | +| Name | `D8BY10` | +| Type | Macro | +| File | `MAC.D8BY.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Divide an 8-bit value by ten | +| Input | ]1 = Number to divide | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 36 | +| Bytes | 21 | +| Notes | None | +| See Also | `M8BY10` `M16BY10` | + +--- + +*DETAILS* + +The `DIV8BY10` macro divides a given 8-bit number by ten. + + + +`LISTING 4.19: D8BY10 Macro Source` + +```assembly +* +*``````````````````````````````* +* D8BY10 (NATHAN RIGGS) * +* * +* DIVIDE 8-BIT NUMBER BY TEN * +* * +* PARAMETERS * +* * +* ]1 = NUMBER TO DIVIDE * +* * +* CYCLES: 36 * +* SIZE: 21 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +D8BY10 MAC + CLC ; {2C1B} CLEAR CARRY + LDA ]1 ; {4C3B} + LSR ; {2C1B} DIVIDE .A BY TWO + STA BPAR1 ; {3C2B} STORE IN ZERO PAGE + LSR ; {2C1B} DIVIDE .A BY TWO + ADC BPAR1 ; {3C2B} ADD ORIGINAL/2 + ROR ; {2C1B} ROTATE BITS RIGHT + LSR ; {2C1B} DIVIDE BY TWO + LSR ; {2C1B} DIVIDE BY TWO + ADC BPAR1 ; {3C2B} ADD ORIGINAL/2 + ROR ; {2C1B} ROTATE BITS RIGHT + ADC BPAR1 ; {3C2B} ADD ORIGINAL/2 + ROR ; {2C1B} ROTATE BITS RIGHT + LSR ; {2C1B} DIVIDE BY TWO + LSR ; {2C1B} DIVIDE BY TWO + <<< + +``` + + + +--- + + + +### Math By Constants Macros: 8-bit Multiplication + +These macros are dedicated to multiplying a given 8-bit number by a known constant. Since the constant is known, we are able to precisely measure the number of cycles and bytes needed for the operation, and it takes significantly fewer of both to return a result than would using a general purpose multiplication implementation. It should be noted that these macros do not take sign into consideration, and there are no plans to make wrappers for the math-by macros; only unsigned numbers are accepted. For signed operations, either use the general purpose division macros or create wrappers specific to each divide-by-constant macro. + +In the comments of each macro, a formula is provided that shows, mathematically, how the algorithm works. In simple terms: since the 6502 runs on binary, it can divide and multiply only by two on its own. Thus, any division or multiplication ultimately has to reduce an equation to factors of two, plus or minus the original value passed at times. + + + +`LISTING 4.20: MAC.D8BY.ASM Header Source` + +```assembly +* +*``````````````````````````````* +* MAC.M8BY.ASM * +* * +* THESE MACROS ARE FOR QUICKLY * +* CALCULATING A NUMBER BY A * +* CONSTANT VALUE. THIS IS * +* OFTEN FASTER THAN THE ALL- * +* PURPOSE IMPLEMENTATIONS DUE * +* TO KNOWING EXACTLY HOW MANY * +* SHIFTS AND ROTATES ARE * +* NEEDED, ADDITIONS, ETC. * +* * +* NOTE THAT THIS IS FOR 8-BIT * +* VALUES ONLY, AND ALL ANSWERS * +* ARE RETURNED IN THE .A * +* REGISTER. .X IS SOMETIMES * +* DESTROYED AS WELL. * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 29-MAR-2021 * +* ASSEMBLER: MERLIN 8 PRO * +* OS: DOS 3.3 * +* * +* LIST OF MACROS * +* * +* M8BY2 : MULTIPLY BY TWO * +* M8BY3 : MULTIPLY BY THREE * +* M8BY4 : MULTIPLY BY FOUR * +* M8BY5 : MULTIPLY BY FIVE * +* M8BY6 : MULTIPLY BY SIX * +* M8BY7 : MULTIPLY BY SEVEN * +* M8BY8 : MULTIPLY BY EIGHT * +* M8BY9 : MULTIPLY BY NINE * +* M8BY10 : MULTIPLY BY TEN * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* + +``` + + + +--- + + + +### THE M8BY2 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------ | +| Name | `M8BY2` | +| Type | Macro | +| File | `MAC.M8BY.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Multiply an 8-bit value by two | +| Input | ]1 = Number to multiply | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 6 | +| Bytes | 4 | +| Notes | None | +| See Also | `D8BY2` `M16BY2` | + +--- + +*DETAILS* + +The `MUL8BY2` macro multiplies a given 8-bit number by two. + + + +`LISTING 4.21: M8BY2 Macro Source` + +```assembly +* +*``````````````````````````````* +* M8BY2 (NATHAN RIGGS) * +* * +* MULTIPLY AN 8-BIT VALUE BY * +* TWO. ONLY RETURNS 8-BIT * +* PRODUCTS. * +* * +* PARAMETERS * +* * +* ]1 = NUMBER TO MULTIPLY * +* * +* CYCLES: 6 * +* SIZE: 4 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +M8BY2 MAC + LDA ]1 ; {4C3B} LOAD NUMBER + ASL ; {2C1B} SHIFT LEFT TO MUL BY 2 + ; ANSWER HELD IN .A REGISTER + <<< +* + +``` + + + +--- + + + +### THE M8BY3 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | -------------------------------- | +| Name | `M8BY3` | +| Type | Macro | +| File | `MAC.M8BY.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Multiply an 8-bit value by three | +| Input | ]1 = Number to multiply | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 6 | +| Bytes | 4 | +| Notes | None | +| See Also | `D8BY3` `M16BY3` | + +--- + +*DETAILS* + +The `MUL8BY3` macro multiplies a given 8-bit number by three. + + + +`LISTING 4.22: M8BY3 Macro Source` + +```assembly +* +*``````````````````````````````* +* M8BY3 (NATHAN RIGGS) * +* * +* MULTIPLY AN 8-BIT VALUE BY * +* THREE. ONLY RETURNS 8-BIT * +* PRODUCTS. * +* * +* PARAMETERS * +* * +* ]1 = NUMBER TO MULTIPLY * +* * +* CYCLES: 13 * +* SIZE: 9 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +M8BY3 MAC + LDA ]1 ; {4C3B} LOAD NUMBER + STA BPAR1 ; {3C2B} BACKUP IN BPAR1 + ASL ; {2C1B} MUL 1ST ARGUMENT BY 2 + CLC ; {2C1B} CLEAR CARRY + ADC BPAR1 ; {2C2B} ADD ORIGINAL NUMBER + ; ANSWER HELD IN .A REGISTER + <<< +* + +``` + + + +--- + + + +### THE M8BY4 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------- | +| Name | `M8BY4` | +| Type | Macro | +| File | `MAC.M8BY.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Multiply an 8-bit value by four | +| Input | ]1 = Number to multiply | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 8 | +| Bytes | 5 | +| Notes | None | +| See Also | `D8BY4` `M16BY4` | + +--- + +*DETAILS* + +The `MUL8BY4` macro multiplies a given 8-bit number by four. + + + +`LISTING 4.23: M8BY4 Macro Source` + +```assembly +* +*``````````````````````````````* +* M8BY4 (NATHAN RIGGS) * +* * +* MULTIPLY AN 8-BIT VALUE BY * +* FOUR. ONLY RETURNS 8-BIT * +* PRODUCTS. * +* * +* PARAMETERS * +* * +* ]1 = NUMBER TO MULTIPLY * +* * +* CYCLES: 8 * +* SIZE: 5 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +M8BY4 MAC + LDA ]1 ; {4C3B} LOAD NUMBER INTO .A + ASL ; {2C1B} MULTIPLY BY TWO + ASL ; {2C1B} MUL AGAIN BY TWO (SO, FOUR) + ; ANSWER HELD IN .A + <<< +* + +``` + + + +--- + + + +### THE M8BY5 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------- | +| Name | `M8BY5` | +| Type | Macro | +| File | `MAC.M8BY.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Multiply an 8-bit value by five | +| Input | ]1 = Number to multiply | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 15 | +| Bytes | 10 | +| Notes | None | +| See Also | `D8BY5` `M16BY5` | + +--- + +*DETAILS* + +The `MUL8BY5` macro multiplies a given 8-bit number by five. + + + +`LISTING 4.25: M8BY5 Macro Source` + +```assembly +* +*``````````````````````````````* +* M8BY5 (NATHAN RIGGS) * +* * +* MULTIPLY AN 8-BIT VALUE BY * +* FIVE. ONLY RETURNS 8-BIT * +* PRODUCTS. * +* * +* PARAMETERS * +* * +* ]1 = NUMBER TO MULTIPLY * +* * +* CYCLES: 15 * +* SIZE: 10 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +M8BY5 MAC + LDA ]1 ; {4C3B} LOAD NUMBER INTO .A + STA BPAR1 ; {3C2B} STORE ORIG VAL IN BPAR1 + ASL ; {2C1B} MULTIPLY BY TWO + ASL ; {2C1B} MUL BY TWO (SO, FOUR) + CLC ; {2C1B} CLEAR CARRY + ADC BPAR1 ; {2C2B} ADD ORIGINAL VALUE TO NEW + ; ANSWER HELD IN .A + <<< +* + +``` + + + +--- + + + +### THE M8BY6 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------ | +| Name | `M8BY6` | +| Type | Macro | +| File | `MAC.M8BY.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Multiply an 8-bit value by six | +| Input | ]1 = Number to multiply | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 15 | +| Bytes | 10 | +| Notes | None | +| See Also | `D8BY6` `M16BY6` | + +--- + +*DETAILS* + +The `MUL8BY6` macro multiplies a given 8-bit number by six. + + + +`LISTING 4.25: M8BY6 Macro Source` + +```assembly +* +*``````````````````````````````* +* M8BY6 (NATHAN RIGGS) * +* * +* MULTIPLY AN 8-BIT VALUE BY * +* SIX. ONLY RETURNS 8-BIT * +* PRODUCTS. * +* * +* PARAMETERS * +* * +* ]1 = NUMBER TO MULTIPLY * +* * +* CYCLES: 27 * +* SIZE: 18 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +M8BY6 MAC + LDA ]1 ; {4C3B} LOAD NUMBER INTO .A + STA BPAR1 ; {3C2B} STORE ORIG IN BPAR1 + ASL ; {2C1B} MULTIPLY .A BY TWO + ASL ; {2C1B} DO AGAIN TO MUL BY FOUR + TAX ; {2C1B} TRANSFER ANSWER INTO .X + LDA BPAR1 ; {3C2B} RELOAD ORIGINAL IN .A + ASL ; {2C1B} MULTIPLY BY TWO + STA BPAR1 ; {3C2B} STORE BACK INTO BPAR1 + TXA ; {2C1B} TRANSFER .X BACK TO .A + CLC ; {2C1B} CLEAR THE CARRY + ADC BPAR1 ; {2C3B} ADD SECOND ARGUMENT ANSWER + <<< +* + +``` + + + +--- + + + +### THE M8BY7 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | -------------------------------- | +| Name | `M8BY7` | +| Type | Macro | +| File | `MAC.M8BY.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Multiply an 8-bit value by seven | +| Input | ]1 = Number to multiply | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 133 | +| Bytes | 20 | +| Notes | None | +| See Also | `D8BY7` `M16BY7` | + +--- + +*DETAILS* + +The `MUL8BY7` macro multiplies a given 8-bit number by seven. + + + +`LISTING 4.28: M8BY7 Macro Source` + +```assembly +* +*``````````````````````````````* +* M8BY7 (NATHAN RIGGS) * +* * +* MULTIPLY AN 8-BIT VALUE BY * +* SEVEN. ONLY RETURNS 8-BIT * +* PRODUCTS. * +* * +* PARAMETERS * +* * +* ]1 = NUMBER TO MULTIPLY * +* * +* CYCLES: 33 * +* SIZE: 20 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +M8BY7 MAC + LDA ]1 ; {4C3B} LOAD NUMBER INTO .A + STA BPAR1 ; {3C2B} STORE IN ZERO PAGE + ASL ; {2C1B} MULTIPLY NUM BY TWO + ASL ; {2C1B} AND AGAIN, SO BY FOUR + CLC ; {2C1B} CLEAR CARRY + ADC BPAR1 ; {3C2B} ADD ORIGINAL NUMBER TO VALUE + TAX ; {2C1B} TEMPORARY TRANSFER TO .X + LDA BPAR1 ; {3C2B} LOAD ORIGINAL NUMBER IN .A + ASL ; {2C1B} MULTIPLY BY TWO + STA BPAR1 ; {3C2B} STORE IN ZERO PAGE + TXA ; {2C1B} TRANSFER .X BACK INTO .A + CLC ; {2C1B} CLEAR CARRY + ADC BPAR1 ; {3C2B} ADD OTHER ARGUMENT SAVED IN MEM + ; ANSWER IS HELD IN .A REGISTER + <<< +* + +``` + + + +--- + + + +### THE M8BY8 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | -------------------------------- | +| Name | `M8BY8` | +| Type | Macro | +| File | `MAC.M8BY.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Multiply an 8-bit value by eight | +| Input | ]1 = Number to multiply | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 10 | +| Bytes | 6 | +| Notes | None | +| See Also | `D8BY8` `M16BY8` | + +--- + +*DETAILS* + +The `MUL8BY8` macro multiplies a given 8-bit number by eight. + + + +`LISTING 4.29: M8BY8 Macro Source` + +```assembly +* +*``````````````````````````````* +* M8BY8 (NATHAN RIGGS) * +* * +* MULTIPLY AN 8-BIT VALUE BY * +* EIGHT. ONLY RETURNS 8-BIT * +* PRODUCTS. * +* * +* PARAMETERS * +* * +* ]1 = NUMBER TO MULTIPLY * +* * +* CYCLES: 10 * +* SIZE: 6 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +M8BY8 MAC + LDA ]1 ; {4C3B} LOAD NUMBER INTO .A + ASL ; {2C1B} MULTIPLY BY TWO + ASL ; {2C1B} MULTIPLY BY 2 (SO FOUR) + ASL ; {2C1B} MULTIPLY BY 2 (SO EIGHT) + ; ANSWER IS HELD IN THE .A REGISTER + <<< +* + +``` + + + +--- + + + +### THE M8BY9 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------- | +| Name | `M8BY9` | +| Type | Macro | +| File | `MAC.M8BY.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Multiply an 8-bit value by nine | +| Input | ]1 = Number to multiply | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 18 | +| Bytes | 11 | +| Notes | None | +| See Also | `D8BY9` `M16BY9` | + +--- + +*DETAILS* + +The `MUL8BY9` macro multiplies a given 8-bit number by nine. + + + +`LISTING 4.30: M8BY9 Macro Source` + +```assembly +* +*``````````````````````````````* +* M8BY9 (NATHAN RIGGS) * +* * +* MULTIPLY AN 8-BIT VALUE BY * +* NINE. ONLY RETURNS 8-BIT * +* PRODUCTS. * +* * +* PARAMETERS * +* * +* ]1 = NUMBER TO MULTIPLY * +* * +* CYCLES: 18 * +* SIZE: 11 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +M8BY9 MAC + LDA ]1 ; {4C3B} LOAD NUMBER INTO .A + STA BPAR1 ; {3C2B} TEMPORARY HOLD IN ZERO PAGE + ASL ; {2C1B} MULTIPLY BY 2 (2) + ASL ; {2C1B} MULTIPLY BY 2 (4) + ASL ; {2C1B} MULTIPLY BY 2 (8) + CLC ; {2C1B} CLEAR CARRY + ADC BPAR1 ; {3C2B} ADD ORIGINAL VALUE + ; ANSWER IS HELD IN .A REGISTER + <<< +* + +``` + + + +--- + + + +### THE M8BY10 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------ | +| Name | `M8BY10` | +| Type | Macro | +| File | `MAC.M8BY.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Multiply an 8-bit value by ten | +| Input | ]1 = Number to multiply | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 18 | +| Bytes | 11 | +| Notes | None | +| See Also | `D8BY10` `M16BY10` | + +--- + +*DETAILS* + +The `MUL8BY10` macro multiplies a given 8-bit number by ten. + + + +`LISTING 4.30: M8BY10 Macro Source` + +```assembly +* +*``````````````````````````````* +* M8BY10 (NATHAN RIGGS) * +* * +* MULTIPLY AN 8-BIT VALUE BY * +* TEN. ONLY RETURNS 8-BIT * +* PRODUCTS. * +* * +* PARAMETERS * +* * +* ]1 = NUMBER TO MULTIPLY * +* * +* CYCLES: 18 * +* SIZE: 11 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +M8BY10 MAC + LDA ]1 ; {4C3B} LOAD NUMBER INTO .A + STA BPAR1 ; {3C2B} STORE IT IN ZERO PAGE + ASL ; {2C1B} MULTIPLY BY TWO (2) + ASL ; {2C1B} MULTIPLY BY TWO (4) + CLC ; {2C1B} CLEAR CARRY + ADC BPAR1 ; {3C2B} ADD ORIGINAL NUMBER TO VALUE + ASL ; {2C1B} AND MULTIPLY TOTAL BY TWO + ; ANSWER IS HELD IN .A REGISTER + <<< +* +``` + + + +--- + + + +### Math By Constants Macros: 16-bit Multiplication + +These macros are dedicated to multiplying a given 16-bit number by a known constant. Since the constant is known, we are able to precisely measure the number of cycles and bytes needed for the operation, and it takes significantly fewer of both to return a result than would using a general purpose multiplication implementation. It should be noted that these macros do not take sign into consideration, and there are no plans to make wrappers for the math-by macros; only unsigned numbers are accepted. For signed operations, either use the general purpose division macros or create wrappers specific to each divide-by-constant macro. + +In the comments of each macro, a formula is provided that shows, mathematically, how the algorithm works. In simple terms: since the 6502 runs on binary, it can divide and multiply only by two on its own. Thus, any division or multiplication ultimately has to reduce an equation to factors of two, plus or minus the original value passed at times. + + + +`LISTING 4.40: MAC.D8BY.ASM Header Source` + +```assembly +* +*``````````````````````````````* +* MAC.M16BY.ASM * +* * +* THESE MACROS ARE FOR FASTER * +* MULTIPLICATION THROUGH USE * +* OF KNOWN CONSTANTS. THIS WAY * +* IS FASTER THAN USUAL ALL- * +* PURPOSE IMPLEMENTATIONS DUE * +* TO CYCLES SAVED FROM NO * +* LOOPS, TRACKING, ETC., AND * +* ARE MEANT AS A POSSIBLE * +* ALTERNATIVE TO THE REGULAR * +* MULTIPLICATION ROUTINE IF * +* NECESSARY. * +* * +* NOTE THAT THIS IS FOR 16-BIT * +* NUMBERS. ADDITIONALLY, THE * +* FINAL ANSWER IS PLACED IN * +* THE .X REGISTER (HIGH BYTE) * +* AND .Y REGISTER (LOW BYTE). * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 25-MAR-2021 * +* ASSEMBLER: MERLIN 8 PRO * +* OS: DOS 3.3 * +* * +* LIST OF MACROS * +* * +* MBY2 : MULTIPLY BY TWO * +* MBY3 : MULTIPLY BY THREE * +* MBY4 : MULTIPLY BY FOUR * +* MBY5 : MULTIPLY BY FIVE * +* MBY6 : MULTIPLY BY SIX * +* MBY7 : MULTIPLY BY SEVEN * +* MBY8 : MULTIPLY BY EIGHT * +* MBY9 : MULTIPLY BY NINE * +* MBY10 : MULTIPLY BY TEN * +* MBY1H : MULTIPLY BY 100 * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* + +``` + + + +--- + + + +### THE M16BY2 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------- | +| Name | `M16BY2` | +| Type | Macro | +| File | `MAC.M8BY.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Multiply a 16-bit value by 2 | +| Input | ]1 = Number to multiply | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 20 | +| Bytes | 13 | +| Notes | None | +| See Also | `D8BY2` `M8BY2` | + +--- + +*DETAILS* + +The `MUL16BY2` macro multiplies a given 16-bit number by two. + + + +`LISTING 4.41: M16BY2 Macro Source` + +```assembly +* +*``````````````````````````````* +* MBY2 (NATHAN RIGGS) * +* * +* MULTIPLY A 16-BIT NUMBER BY * +* TWO. * +* * +* PARAMETERS * +* * +* ]1 = NUMBER ADDRESS * +* * +* CYCLES: 20 * +* SIZE: 13 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +MBY2 MAC + LDA ]1 ; {4C3B} LOAD LOW BYTE IN .A + ASL ; {2C1B} MULTIPLY IT BY TWO + STA WPAR1 ; {3C2B} STORE ON ZERO PAGE + LDA ]1+1 ; {4C3B} LOAD NUMBER HIGH BYTE + ROL ; {2C1B} ADJUST FOR CARRY AND MULTIPLY + TAX ; {2C1B} TRANSFER HIGH BYTE TO .X REGISTER + LDA WPAR1 ; {3C2B} HOLD LOW BYTE IN .A + <<< +* + +``` + + + +--- + + + +### THE M16BY3 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------- | +| Name | `M16BY3` | +| Type | Macro | +| File | `MAC.M8BY.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Multiply a 16-bit value by 3 | +| Input | ]1 = Number to multiply | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 47 | +| Bytes | 32 | +| Notes | None | +| See Also | `D8BY3` `M8BY3` | + +--- + +*DETAILS* + +The `MUL16BY2` macro multiplies a given 16-bit number by three. + + + +`LISTING 4.42: M16BY3 Macro Source` + +```assembly +* +*``````````````````````````````* +* MBY3 (NATHAN RIGGS) * +* * +* MULTIPLY A 16-BIT NUMBER BY * +* THREE. * +* * +* FORMULA: 3X = 2X +X * +* * +* PARAMETERS * +* * +* ]1 = NUMBER ADDRESS * +* * +* CYCLES: 47 * +* SIZE: 32 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +MBY3 MAC + LDA ]1 ; {4C3B} LOAD NUMBER LOW BYTE + STA WPAR1 ; {3C2B} STORE IT ON ZERO PAGE + ASL ; {2C1B} NOW MULTIPLY BY TWO + STA WPAR2 ; {3C2B} STORE IN 2ND ZERO PAGE LOC + LDA ]1+1 ; {4C3B} GET NUMBER HIGH BYTE + STA WPAR1+1 ; {3C2B} STORE HIGH BYTE ON ZERO PAGE + ROL ; {2C1B} ADJUST FOR CARRY AND MULTIPLY + STA WPAR2+1 ; {3C2B} STORE HIGH BYTE IN 2ND LOCATION + LDA WPAR1 ; {4C3B} LOAD ORIGINAL NUMBER IN .A + CLC ; {2C2B} CLEAR CARRY + ADC WPAR2 ; {3C2B} ADD SECOND NUMBER TO ORIGINAL + STA WPAR1 ; {3C2B} STORE LOW BYTE IN ZERO PAGE + LDA WPAR1+1 ; {3C2B} LOAD NUMBER HIGH BYTE IN .A + ADC WPAR2+1 ; {3C2B} ADD 2ND VALUE HIGH BYTE TO IT + TAX ; {2C1B} TRANSFER NEW HIGH BYTE TO .X + LDA WPAR1 ; {3C2B} RELOAD LOW BYTE IN .A + <<< +* + +``` + + + +--- + + + +### THE M16BY4 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------- | +| Name | `M16BY4` | +| Type | Macro | +| File | `MAC.M8BY.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Multiply a 16-bit value by 4 | +| Input | ]1 = Number to multiply | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 37 | +| Bytes | 24 | +| Notes | None | +| See Also | `D8BY4` `M8BY4` | + +--- + +*DETAILS* + +The `MUL16BY4` macro multiplies a given 16-bit number by four. + + + +`LISTING 4.43: M16BY4 Macro Source` + +```assembly +* +*``````````````````````````````* +* MBY4 (NATHAN RIGGS) * +* * +* MULTIPLY A 16-BIT NUMBER BY * +* FOUR. * +* * +* PARAMETERS * +* * +* ]1 = NUMBER ADDRESS * +* * +* CYCLES: 37 * +* SIZE: 24 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +MBY4 MAC + LDA ]1 ; {4C3B} LOAD NUMBER LOW BYTE TO .A + ASL ; {2C1B} MULTIPLY BY TWO + STA BPAR1 ; {3C2B} STORE LOW BYTE TO ZERO PAGE + LDA ]1+1 ; {4C3B} LOAD NUMBER HIGH BYTE + ROL ; {2C1B} ADJUST FOR CARRY AND MUL + TAX ; {2C1B} TRANSFER HIGH BYTE TO .X + LDA BPAR1 ; {4C3B} RELOAD LOW BYTE + ASL ; {2C1B} MULTIPLY AGAIN + STA BPAR1 ; {4C3B} STORE BACK ON ZERO PAGE + TXA ; {2C1B} TRANSFER HIGH BYTE BACK TO .A + ROL ; {2C1B} ADJUST FOR CARRY AND MULTIPLY + TAX ; {2C1B} TRANSFER HIGH BYTE BACK TO .X + LDA BPAR1 ; {4C3B} RELOAD LOW BYTE IN .A + <<< +* + +``` + + + +--- + + + +### THE M16BY5 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------- | +| Name | `M16BY5` | +| Type | Macro | +| File | `MAC.M8BY.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Multiply a 16-bit value by 5 | +| Input | ]1 = Number to multiply | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 46 | +| Bytes | 30 | +| Notes | None | +| See Also | `D8BY5` `M8BY5` | + +--- + +*DETAILS* + +The `MUL16BY5` macro multiplies a given 16-bit number by five. + + + +`LISTING 4.44: M16BY5 Macro Source` + +```assembly +* +*``````````````````````````````* +* MBY5 (NATHAN RIGGS) * +* * +* MULTIPLY A 16-BIT NUMBER BY * +* FIVE. * +* * +* FORMULA: 5X = 4X + X * +* * +* PARAMETERS * +* * +* ]1 = NUMBER ADDRESS * +* * +* CYCLES: 46 * +* SIZE: 30 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +MBY5 MAC + LDA ]1 ; {4C3B} LOAD NUMBER LOW BYTE + ASL ; {2C1B} MULTIPLY BY TWO + STA WPAR1 ; {3C2B} STORE ON ZERO PAGE + LDA ]1+1 ; {4C3B} LOAD NUMBER HIGH BYTE + ROL ; {2C1B} APPLY CARRY AND MULTIPLY + STA WPAR1+1 ; {3C2B} STORE ON ZERO PAGE + ASL WPAR1 ; {2C1B} MULTIPLY LOW BYTE BY TWO AGAIN + ROL WPAR1+1 ; {2C1B} AND HIGH BYTE AGAIN + LDA WPAR1 ; {4C3B} LOAD LOW BYTE INTO .A + CLC ; {2C1B} CLEAR THE CARRY + ADC ]1 ; {3C2B} ADD ORIGINAL VALUE + STA WPAR1 ; {3C2B} STORE ON ZERO PAGE + LDA WPAR1+1 ; {3C2B} LOAD HIGH BYTE IN .A + ADC ]1+1 ; {4C3B} ADD ORIGINAL HIGH BYTE + TAX ; {2C1B} TRANSFER HIGH BYTE TO .X + LDA WPAR1 ; {3C2B} LOAD LOW BYTE BACK IN .A + <<< +* + +``` + + + +--- + + + +### THE M16BY6 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------- | +| Name | `M16BY6` | +| Type | Macro | +| File | `MAC.M8BY.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Multiply a 16-bit value by 6 | +| Input | ]1 = Number to multiply | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 58 | +| Bytes | 38 | +| Notes | None | +| See Also | `D8BY6` `M8BY6` | + +--- + +*DETAILS* + +The `MUL16BY6` macro multiplies a given 16-bit number by six. + + + +`LISTING 4.45: M16BY6 Macro Source` + +```assembly +* +*``````````````````````````````* +* MBY6 (NATHAN RIGGS) * +* * +* MULTIPLY A 16-BIT NUMBER BY * +* SIX. * +* * +* FORMULA: 6X = 2(2X + X) * +* * +* PARAMETERS * +* * +* ]1 = NUMBER ADDRESS * +* * +* CYCLES: 58 * +* SIZE: 38 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +MBY6 MAC + LDA ]1 ; {4C3B} LOAD NUMBER LOW BYTE + ASL ; {2C1B} MULTIPLY BY TWO + STA WPAR1 ; {3C2B} STORE ON ZERO PAGE + LDA ]1+1 ; {4C3B} LOAD HIGH BYTE + ROL ; {2C1B} ADJUST CARRY AND MULTIPLY + STA WPAR1+1 ; {3C2B} STORE ON ZERO PAGE + LDA WPAR1 ; {3C2B} RELOAD LOW BYTE IN .A + CLC ; {2C1B} CLEAR THE CARRY + ADC ]1 ; {4C3B} ADD ORIGINAL LOW BYTE + STA WPAR1 ; {3C2B} STORE BACK ON ZERO PAGE + LDA WPAR1+1 ; {3C2B} LOAD HIGH BYTE AGAIN + ADC ]1+1 ; {4C3B} ADD ORIGINAL HIGH BYTE + STA WPAR1+1 ; {3C2B} STORE ON ZERO PAGE + LDA WPAR1 ; {3C2B} LOAD LOW BYTE AGAIN + ASL ; {2C1B} MULTIPLY BY TWO + STA WPAR1 ; {3C2B} STORE ON ZERO PAGE + LDA WPAR1+1 ; {3C2B} LOAD HIGH BYTE + ROL ; {2C1B} APPLY CARRY AND MULTIPLY + TAX ; {2C1B} HOLD HIGH BYTE IN .X + LDA WPAR1 ; {3C2B} RELOAD LOW BYTE IN .A + <<< +* + +``` + + + +--- + + + +### THE M16BY7 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------- | +| Name | `M16BY7` | +| Type | Macro | +| File | `MAC.M8BY.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Multiply a 16-bit value by 7 | +| Input | ]1 = Number to multiply | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 72 | +| Bytes | 47 | +| Notes | None | +| See Also | `D8BY7` `M8BY7` | + +--- + +*DETAILS* + +The `MUL16BY7` macro multiplies a given 16-bit number by seven. + + + +`LISTING 4.46: M16BY7 Macro Source` + +```assembly +* +*``````````````````````````````* +* MBY7 (NATHAN RIGGS) * +* * +* MULTIPLY A 16-BIT NUMBER BY * +* SEVEN. * +* * +* FORMULA: 7X = 4X + 2X + X * +* * +* PARAMETERS * +* * +* ]1 = NUMBER ADDRESS * +* * +* CYCLES: 72 * +* SIZE: 47 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +MBY7 MAC + LDA ]1 ; {4C3B} LOAD NUMBER LOW BYTE IN .A + ASL ; {2C1B} MULTIPLY BY TWO + STA WPAR1 ; {3C2B} STORE IN ZERO PAGE + STA WPAR2 ; {3C2B} STORE IN 2ND ZP LOCATION + LDA ]1+1 ; {4C3B} LOAD NUMBER HIGH BYTE + ROL ; {2C1B} APPLY CARRY AND MULTIPLY + STA WPAR1+1 ; {3C2B} STORE IN 1ST ZP HIGH BYTE LOC + STA WPAR2+1 ; {3C2B} STORE IN 2ND ZP HIGH BYTE LOC + ASL WPAR1 ; {2C1B} MULTIPLY 1ST LOC BY TWO + ROL WPAR1+1 ; {2C1B} ALSO HIGH BYTE + LDA ]1 ; {4C3B} LOAD ORIGINAL NUMBER + CLC ; {2C1B} CLEAR CARRY + ADC WPAR1 ; {3C2B} ADD FIRST LOC TO NUMBER LOW BYTE + STA WPAR1 ; {3C2B} STORE IN 1ST LOC + LDA ]1+1 ; {4C3B} LOAD NUMBER HIGH BYTE + ADC WPAR1+1 ; {3C2B} ADD 1ST LOC HIGH BYTE + STA WPAR1+1 ; {3C2B} AND STORE IN 1ST LOC + LDA WPAR1 ; {3C2B} RELOAD NEW LOW BYTE + CLC ; {2C1B} CLEAR CARRY + ADC WPAR2 ; {3C2B} ADD SECOND LOC LOW BYTE + STA WPAR1 ; {3C2B} STORE IN 1ST LOC LOW BYTE + LDA WPAR1+1 ; {3C2B} LOAD 1ST LOC HIGH BYTE + ADC WPAR2+1 ; {3C2B} ADD 2ND LOC HIGH BYTE + TAX ; {2C1B} HOLD FINAL HIGH BYTE IN .X + LDA WPAR1 ; {3C2B} HOLD FINAL LOW BYTE IN .A + <<< +* + +``` + + + +--- + + + +### THE M16BY8 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------- | +| Name | `M16BY8` | +| Type | Macro | +| File | `MAC.M8BY.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Multiply a 16-bit value by 8 | +| Input | ]1 = Number to multiply | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 34 | +| Bytes | 21 | +| Notes | None | +| See Also | `D8BY8` `M8BY8` | + +--- + +*DETAILS* + +The `MUL16BY8` macro multiplies a given 16-bit number by eight. + + + +`LISTING 4.47: M16BY8 Macro Source` + +```assembly +* +*``````````````````````````````* +* MBY8 (NATHAN RIGGS) * +* * +* MULTIPLY A 16-BIT NUMBER BY * +* EIGHT. * +* * +* PARAMETERS * +* * +* ]1 = NUMBER ADDRESS * +* * +* CYCLES: 34 * +* SIZE: 21 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +MBY8 MAC + LDA ]1 ; {4C3B} LOAD NUMBER LOW BYTE + ASL ; {2C1B} MULTIPLY BY TWO + STA WPAR1 ; {3C2B} STORE IN ZP LOC + LDA ]1+1 ; {4C3B} LOAD NUMBER HIGH BYTE + ROL ; {2C1B} APPLY CARRY AND MUL BY 2 + STA WPAR1+1 ; {3C2B} STORE IN ZP LOC + ASL WPAR1 ; {2C1B} MULTIPLY LOW BYTE BY 2 + ROL WPAR1+1 ; {2C1B} APPLY CARRY, MUL HIGH BY 2 + ASL WPAR1 ; {2C1B} MUL LOW BYTE BY 2 + LDA WPAR1+1 ; {3C2B} LOAD HIGH BYTE IN .A + ROL ; {2C1B} APPLY CARRY AND MUL BY 2 + TAX ; {2C1B} HOLD HIGH BYTE IN .X + LDA WPAR1 ; {3C2B} HOLD LOW BYTE IN .A + <<< +* + +``` + + + +--- + + + +### THE M16BY9 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------- | +| Name | `M16BY9` | +| Type | Macro | +| File | `MAC.M8BY.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Multiply a 16-bit value by 9 | +| Input | ]1 = Number to multiply | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 62 | +| Bytes | 36 | +| Notes | None | +| See Also | `D8BY9` `M8BY9` | + +--- + +*DETAILS* + +The `MUL16BY9` macro multiplies a given 16-bit number by nine. + + + +`LISTING 4.48: M16BY9 Macro Source` + +```assembly +* +*``````````````````````````````* +* MBY9 (NATHAN RIGGS) * +* * +* MULTIPLY A 16-BIT NUMBER BY * +* NINE. * +* * +* FORMULA: 9X = 8X + X * +* * +* PARAMETERS * +* * +* ]1 = NUMBER ADDRESS * +* * +* CYCLES: 62 * +* SIZE: 36 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +MBY9 MAC + LDA ]1 ; {4C3B} LOAD NUMBER LOW BYTE + ASL ; {2C1B} MULTIPLY BY TWO + STA WPAR1 ; {3C2B} STORE ON ZERO PAGE + LDA ]1+1 ; {4C3B} LOAD NUMBER HIGH BYTE + ROL ; {2C1B} APPLY CARRY AND MUL BY 2 + STA WPAR1+1 ; {3C2B} STORE ON ZP + ASL WPAR1 ; {5C2B} MUL LOW BYTE BY 2 + ROL WPAR1+1 ; {5C2B} CARRY AND MUL HIGH BY 2 + ASL WPAR1 ; {5C2B} MUL LOW BY 2 + ROL WPAR1+1 ; {5C2B} CARRY AND MUL HIGH BY 2 + LDA WPAR1 ; {3C2B} LOAD LOW BYTE + CLC ; {2C1B} CLEAR CARRY + ADC ]1 ; {4C3B} ADD ORIGINAL VALUE + STA WPAR1 ; {3C2B} STORE ON ZP + LDA WPAR1+1 ; {3C2B} LOAD HIGH BYTE + ADC ]1+1 ; {4C3B} ADD ORIGINAL HIGH BYTE + TAX ; {2C1B} HOLD FINAL HIGH BYTE IN .X + LDA WPAR1 ; {3C2B} HOLD FINAL LOW BYTE IN .A + <<< +* + +``` + + + +--- + + + +### THE M16BY10 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ----------------------------- | +| Name | `M16BY10` | +| Type | Macro | +| File | `MAC.M8BY.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Multiply a 16-bit value by 10 | +| Input | ]1 = Number to multiply | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 66 | +| Bytes | 39 | +| Notes | None | +| See Also | `D8BY10` `M8BY10` | + +--- + +*DETAILS* + +The `MUL16BY10` macro multiplies a given 16-bit number by ten. + + + +`LISTING 4.49: M16BY10 Macro Source` + +```assembly +* +*``````````````````````````````* +* MBY10 (NATHAN RIGGS) * +* * +* MULTIPLY A 16-BIT NUMBER BY * +* TEN. * +* * +* FORMULA: 10X = 2(4X + X) * +* * +* PARAMETERS * +* * +* ]1 = NUMBER ADDRESS * +* * +* CYCLES: 65 * +* SIZE: 39 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +MBY10 MAC + LDA ]1 ; {4C3B} LOAD NUMBER LOW BYTE + ASL ; {2C1B} MUL LOW BYTE BY 2 + STA WPAR1 ; {3C2B} STORE ON ZERO PAGE + LDA ]1+1 ; {4C3B} LOAD NUMBER HIGH BYTE + ROL ; {2C1B} APPLY CARRY AND MUL BY 2 + STA WPAR1+1 ; {3C2B} STORE ON ZERO PAGE + ASL WPAR1 ; {5C2B} MUL LOW BY 2 + ROL WPAR1+1 ; {5C2B} CARRY AND MUL HIGH BY 2 + LDA ]1 ; {4C3B} LOAD ORIGINAL LOW BYTE + CLC ; {2C1B} CLEAR CARRY + ADC WPAR1 ; {3C2B} ADD ZP LOW BYTE + STA WPAR1 ; {3C2B} STORE BACK ON ZP + LDA ]1+1 ; {4C3B} LOAD ORIGINAL HIGH BYTE + ADC WPAR1+1 ; {3C2B} ADD ZP HIGH BYTE + STA WPAR1+1 ; {3C2B} STORE BACK ON ZP + ASL WPAR1 ; {5C2B} MULTIPLY NEW VAL LOW BY 2 + LDA WPAR1+1 ; {3C2B} LOAD HIGH BYTE IN .A + ROL ; {2C1B} CARRY AND MUL HIGH BY 2 + TAX ; {2C1B} HOLD FINAL HIGH BYTE IN .X + LDA WPAR1 ; {3C2B} HOLD FINAL LOW BYTE IN .A + <<< +* + +``` + + + +--- + + + +### THE M16BY1H MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------ | +| Name | `M16BY100` | +| Type | Macro | +| File | `MAC.M8BY.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Multiply a 16-bit value by 100 | +| Input | ]1 = Number to multiply | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 124 | +| Bytes | 70 | +| Notes | None | +| See Also | none | + +--- + +*DETAILS* + +The `MUL16BY1H` macro multiplies a given 16-bit number by one hundred. + + + +`LISTING 4.50: M16BY1H Macro Source` + +```assembly +* +*``````````````````````````````* +* MBY1H (NATHAN RIGGS) * +* * +* MULTIPLY A 16-BIT NUMBER BY * +* ONE HUNDRED. * +* * +* FORMULA: 100X = 4(16X+8X+X) * +* * +* PARAMETERS * +* * +* ]1 = NUMBER ADDRESS * +* * +* CYCLES: 124 * +* SIZE: 70 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +MBY1H MAC + LDA ]1 ; {4C3B} LOAD NUMBER INTO .A + ASL ; {2C1B} MUL LOW BYTE BY 2 + STA WPAR2 ; {3C2B} STORE IN ZP LOC 1 + LDA ]1+1 ; {4C3B} LOAD NUMBER HIGH BYTE + ROL ; {2C1B} CARRY AND MUL BY 2 + STA WPAR2+1 ; {3C2B} STORE IN ZP LOC 1 + ASL WPAR2 ; {5C2B} MUL ZP LOW BYTE BY 2 + ROL WPAR2+1 ; {5C2B} CARRY AND MUL ZP HIGH BY 2 + ASL WPAR2 ; {5C2B} MUL LOW BY 2 + ROL WPAR2+1 ; {5C2B} MUL HIGH BY 2 + LDA WPAR2 ; {3C2B} LOAD LOW BYTE IN .A + ASL ; {2C1B} MULTIPLY BY TWO + STA WPAR1 ; {3C2B} STORE IN 2ND ZP LOC + LDA WPAR2+1 ; {3C2B} LOAD HIGH BYTE IN .A + ROL ; {2C1B} CARRY AND MUL BY 2 + STA WPAR1+1 ; {3C2B} STORE IN 2ND ZP LOC + LDA ]1 ; {4C3B} LOAD ORIGINAL VALUE + CLC ; {2C1B} CLEAR CARRY + ADC WPAR1 ; {3C2B} ADD LOW BYTE ZP LOC 1 + STA WPAR1 ; {3C2B} STORE AT ZP LOC 1 + LDA ]1+1 ; {4C3B} LOAD ORIGINAL HIGH BYTE + ADC WPAR1+1 ; {3C2B} ADD ZP LOC HIGH + STA WPAR1+1 ; {3C2B} STORE BACK AT ZP LOC 1 HIGH + LDA WPAR1 ; {3C2B} LOAD LOC 1 LOW BYTE IN .A + CLC ; {2C1B} CLEAR CARRY + ADC WPAR2 ; {3C2B} ADD 2ND ZP LOC LOW BYTE + STA WPAR1 ; {3C2B} STORE IN LOC 1 LOW + LDA WPAR1+1 ; {3C2B} LOAD ZP LOC 1 HIGH BYTE + ADC WPAR2+1 ; {3C2B} ADD 2ND LOC HIGH BYTE + STA WPAR1+1 ; {3C2B} STORE BACK IN ZP LOC 1 HIGH + ASL WPAR1 ; {5C2B} MULTIPLY THAT VALUE LOW BY 2 + ROL WPAR1+1 ; {5C2B} CARRY AND MUL HIGH BY 2 + LDA WPAR1 ; {3C2B} LOAD LOW BYTE IN .A + ASL ; {2C1B} MULTIPLY AGAIN BY TWO + STA WPAR1 ; {3C2B} STORE IN ZP LOC 1 LOW + LDA WPAR1+1 ; {3C2B} LOAD HIGH BYTE IN .A + ROL ; {2C1B} CARRY AND MUL HIGH BY 2 + TAX ; {2C1B} HOLD FINAL HIGH BYTE IN .X + LDA WPAR1 ; {3C2B} HOLD FINAL LOW BYTE IN .A + <<< +* +``` + + + +--- + + + +### General Purpose Math: 8-bit Procedures + +These macros are dedicated to adding, subtracting, multiplying or dividing a given 8-bit number by another arbitrary 8-bit number. All values are **unsigned**; wrappers for signed numbers will be provided in a future revision, along with a modulus macro that wraps around `DIV8`. + + + +`LISTING 4.60: MAC.MATHBAS.ASM Header Source` + +```assembly +* +*``````````````````````````````* +* MAC.MATH8.ASM * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 01-APR-2021 * +* ASSEMBLER: MERLIN 8 PRO * +* OS: DOS 3.3 * +* * +* SUBROUTINE FILES USED * +* * +* SUB.DIVDU8 * +* SUB.MULTU8 * +* * +* LIST OF MACROS * +* * +* ADD8 : ADD 8BIT NUMBERS * +* SUB8 : SUBTRACT 8BIT NUMS * +* MUL8 : MULTIPLY 8BIT NUMS * +* DIV8 : DIVIDE 8BIT NUMS * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* + +``` + + + +--- + + + +### THE ADD8 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | -------------------------------------------------------- | +| Name | `ADD8` | +| Type | Macro | +| File | `MAC.MATH8.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Add one 8-bit number to another | +| Input | ]1 = first number to add
]2 = second number to add | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 21 | +| Bytes | 15 | +| Notes | None | +| See Also | `SUB8` `ADD16` | + +--- + +*DETAILS* + +The `ADD8` macro adds two arbitrary 8-bit numbers and passes back the sum in the **.A** register and in `RETURN`. Note that this macro, along with `SUB8`, is included as a courtesy to beginners, and they should not be relied on for fast and compact code (bytes and cycles are wasted to keep within the standards of the library). + + + +`LISTING 4.61: ADD8 Macro Source` + +```assembly +* +*``````````````````````````````* +* ADD8 (NATHAN RIGGS) * +* * +* DIRTY MACRO TO ADD TWO BYTES * +* * +* PARAMETERS * +* * +* ]1 = ADDEND 1 * +* ]2 = ADDEND 2 * +* * +* CYCLES: 21 * +* SIZE: 15 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +ADD8 MAC + LDA #1 ; {3C2B} + STA RETLEN ; {4C3B} + LDA ]1 ; {4C3B} + CLC ; {2C1B} + ADC ]2 ; {4C3B} + STA RETURN ; {4C3B} + <<< +* + +``` + + + +--- + + + +### THE SUB8 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------------- | +| Name | `SUB8` | +| Type | Macro | +| File | `MAC.MATH8.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Add one 8-bit number to another | +| Input | ]1 = Minuend
]2 = Subtrahend | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 21 | +| Bytes | 15 | +| Notes | None | +| See Also | `ADD8` `SUB16` | + +--- + +*DETAILS* + +The `SUB8` macro subtracts one arbitrary 8-bit number from another and passes back the sum in the **.A** register and in `RETURN`. Note that this macro, like with `ADD8`, is included as a courtesy to beginners, and should not be relied on for fast and compact code (bytes and cycles are wasted to keep within the standards of the library). + + + +`LISTING 4.62: SUB8 Macro Source` + +```assembly +* +*``````````````````````````````* +* SUB8 (NATHAN RIGGS) * +* * +* MACRO TO SUBTRACT TWO BYTES * +* * +* PARAMETERS * +* * +* ]1 = MINUEND * +* ]2 = SUBTRAHEND * +* * +* CYCLES: 21 * +* SIZE: 15 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +SUB8 MAC + LDA #1 ; {3C2B} + STA RETLEN ; {4C3B} + LDA ]1 ; {4C3B} + SEC ; {2C1B} + SBC ]2 ; {4C3B} + STA RETURN ; {4C3B} + <<< +* + +``` + + + +--- + + + +### THE MUL8 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | -------------------------------------- | +| Name | `MUL8` | +| Type | Macro | +| File | `MAC.MATH8.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Multiply one 8-bit number by another | +| Input | ]1 = Multiplicand
]2 = Multiplier | +| Output | none | +| Dependencies | `SUB.MULTU8` | +| Flags Destroyed | NZCV | +| Cycles | 97+ | +| Bytes | 56 | +| Notes | None | +| See Also | `DIV8` `MUL16` | + +--- + +*DETAILS* + +The `MUL8` macro multiplies one 8-bit number by another, passing back the 16-bit product in `RETURN` and in the .A (low byte) and .X (high byte) registers. + + + +`LISTING 4.63: MUL8 Macro Source` + +```assembly +* +*``````````````````````````````* +* MUL8 (NATHAN RIGGS) * +* * +* MULTIPLIES TWO 8BIT VALUES * +* AND RETURNS A 16BIT RESULT * +* IN .A,.X (LOW, HIGH). * +* * +* PARAMETERS * +* * +* ]1 = MULTIPLICAND * +* ]2 = MULTIPLIER * +* * +* CYCLES: 97+ * +* BYTES: 56 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +MUL8 MAC + LDA ]1 ; {4C3B} + LDX ]2 ; {4C3B} + JSR MULTU8 ; {89C50B} + <<< +* + +``` + + + +--- + + + +### THE MULTU8 SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | -------------------------------------------- | +| Name | `MULTU8` | +| Type | Subroutine | +| File | `SUB.MULTU8.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Multiply one 8-bit number by another | +| Input | WPAR1 = Multiplicand
WPAR2 = Multiplier | +| Output | none | +| Dependencies | `SUB.MULTU8` | +| Flags Destroyed | NZCV | +| Cycles | 83+ | +| Bytes | 47 | +| Notes | None | +| See Also | `MUL16` | + +--- + +*DETAILS* + +The `MUL8` macro multiplies one 8-bit number by another, passing back the 16-bit product in `RETURN` and in the **.A** (low byte) and **.X** (high byte) registers (`RETLEN` holds 2, the length in bytes of the return value). + + + +`LISTING 4.63: MULTU8 Subroutine Source` + +```assembly +* +*``````````````````````````````* +* MULTU8 (NATHAN RIGGS) * +* * +* MULTIPLY TWO 8-BIT NUMBERS. * +* * +* INPUT: * +* * +* WPAR1 = MULTIPLIER * +* WPAR2 = MULTIPLICAND * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 83+ * +* SIZE: 47 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]MUL1 EQU WPAR1 ; MULTIPLIER +]MUL2 EQU WPAR2 ; MULTIPLICAND +* +MULTU8 + STA ]MUL1 ; {3C2B} + STX ]MUL2 ; {3C2B} + LDA #0 ; {3C2B} CLEAR REGISTERS + TAY ; {2C1B} + TAX ; {2C1B} + STA ]MUL1+1 ; {3C2B} CLEAR HIBYTE + BEQ :GOLOOP ; {3C2B} +:DOADD + CLC ; {2C1B} CLEAR CARRY + ADC ]MUL1 ; {3C2B} ADD MULTIPLIER + TAX ; {2C1B} HOLD IN .Y + TYA ; {2C1B} XFER .X TO .A + ADC ]MUL1+1 ; {3C2B} ADD MULTIPLIER HIBYTE + TAY ; {2C1B} HOLD BACK IN .X + TXA ; {2C1B} MOVE LOBYTE INTO .A +:LP + ASL ]MUL1 ; {5C2B} SHIFT LEFT + ROL ]MUL1+1 ; {5C2B} ROLL HIBYTE +:GOLOOP + LSR ]MUL2 ; {5C2B} SHIFT MULTIPLIER + BCS :DOADD ; {3C2B} IF SHIFTED IN CARRY ADD AGAIN + BNE :LP ; {3C2B} OTHERWISE, LOOP + LDA #2 ; {3C2B} 16-BIT LENGTH, 2 BYTES + STA RETLEN ; {4C3B} FOR RETURN LENGTH + STX RETURN ; {4C3B} STORE LOBYTE + STY RETURN+1 ; {4C3B} STORE HIBYTE + TXA ; {2C1B} LOBYTE TO .A + LDX RETURN+1 ; {4C3B} HIBYTE TO .X + RTS ; {6C1B} + +``` + + + +--- + + + +### THE DIV8 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------------- | +| Name | `DIV8` | +| Type | Macro | +| File | `MAC.MATH8.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Divide one 8-bit number by another | +| Input | ]1 = Dividend
]2 = Divisor | +| Output | none | +| Dependencies | `SUB.DIVDU8` | +| Flags Destroyed | NZCV | +| Cycles | 75+ | +| Bytes | 43 | +| Notes | None | +| See Also | `DIV8` `DIVDU8` | + +--- + +*DETAILS* + +The `DIV8` macro divides one 8-bit number by another, passing back the 8-bit result in `RETURN` and in the .A register. + + + +`LISTING 4.65: DIV8 Macro Source` + +```assembly +* +*``````````````````````````````* +* DIV8 (NATHAN RIGGS) * +* * +* DIVIDES ONE 8BIT NUMBER BY * +* ANOTHER AND STORES THE * +* QUOTIENT IN .A WITH THE * +* REMAINDER IN .X. * +* * +* PARAMETERS * +* * +* ]1 = DIVIDEND * +* ]2 = DIVISOR * +* * +* CYCLES: 75+ * +* SIZE: 43 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +DIV8 MAC + LDA ]1 ; {4C3B} + LDX ]2 ; {4C3B} + JSR DIVDU8 ; {67C37B} + <<< +* + +``` + + + +--- + + + +### THE DIVDU8 SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------- | +| Name | `DIVDU8` | +| Type | Subroutine | +| File | `SUB.DIVDU8.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Divide one 8-bit number by another | +| Input | WPAR1 = Dividend
WPAR2 = Divisor | +| Output | none | +| Dependencies | `SUB.DIVDU8` | +| Flags Destroyed | NZCV | +| Cycles | 61+ | +| Bytes | 34 | +| Notes | None | +| See Also | `DIV16` | + +--- + +*DETAILS* + +The `DIVDU8` subroutine divides an 8-bit dividend by an 8-bit divisor, passing back an 8-bit result in the .A register and `RETURN`. Additionally, any remainder is returned in the **.X** register. + + + +`LISTING 4.63: SUB8 Macro Source` + +```assembly +* +*``````````````````````````````* +* DIVDU8 (NATHAN RIGGS) * +* * +* DIVIDE WITH TWO 8-BIT VALUES * +* * +* INPUT: * +* * +* WPAR1 = DIVIDEND * +* WPAR2 = DIVISOR * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 61+ * +* SIZE: 34 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]DVEND EQU WPAR1 ; DIVIDEND +]DVSOR EQU WPAR2 ; DIVISOR +* +DIVDU8 + STX ]DVEND ; {3C2B} .X HOLDS DIVIDEND + STA ]DVSOR ; {3C2B} .A HOLDS DIVISOR + LDA #$00 ; {3C2B} CLEAR ACCUMULATOR + LDX #8 ; {3C2B} COUNTER + ASL ]DVSOR ; {5C2B} SHIFT LEFT DIVISOR +:L1 ROL ; {2C1B} ROTATE LEFT .A + CMP ]DVEND ; {3C2B} COMPARE TO DIVIDEND + BCC :L2 ; {3C2B} IF NEXT BIT = 0, BRANCH :L2 + SBC ]DVEND ; {3C2B} OTHERWISE, SUBTRACT DIVIDEND +:L2 ROL ]DVSOR ; {5C2B} LEFT DIVISOR + DEX ; {2C1B} DECREMENT COUNTER + BNE :L1 ; {3C2B} IF > 0, LOOP + TAX ; {2C1B} REMAINDER IN .X + LDA #1 ; {3C2B} + STA RETLEN ; {4C3B} + LDA ]DVSOR ; {3C2B} RESULT IN .A + STA RETURN ; {4C3B} + RTS ; {6C1B} + +``` + + + +--- + + + +### General Purpose Math: 16-bit Procedures + +These macros are dedicated to adding, subtracting, multiplying or dividing with two 8-bit or 16-bit numbers, as well as a macro for 16-bit comparisons. All values are **unsigned**; wrappers for signed numbers will be provided in a future revision, along with a modulus macro that wraps around `DIV16`. + +While these macros and subroutines are adequate for most general purposes, if speed or size is a major concern then it is advised that at least addition and subtraction be done manually, as some bytes and cycles are "wasted" keeping the library's operating principles intact. + + + +`LISTING 4.70: MAC.MATHBAS.ASM Header Source` + +```assembly +* +*``````````````````````````````* +* MAC.MATH16.ASM * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 01-APR-2021 * +* ASSEMBLER: MERLIN 8 PRO * +* OS: DOS 3.3 * +* * +* SUBROUTINE FILES USED * +* * +* SUB.ADDIT16 * +* SUB.COMP16 * +* SUB.DIVDU16 * +* SUB.MULTU16 * +* SUB.SUBT16 * +* * +* LIST OF MACROS * +* * +* ADD16 : ADD 16BIT NUMBERS * +* SUB16 : SUBTRACT 16BIT NUMS * +* MUL16 : MULTIPLY 16BIT NUMS * +* DIV16 : DIVIDE 16BIT NUMS * +* CMP16 : COMPARE 16BIT NUMS * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* + +``` + + + +--- + + + +### THE ADD16 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ----------------------------------------- | +| Name | `ADD16` | +| Type | Macro | +| File | `MAC.MATH16.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Add two 16-bit numbers | +| Input | ]1 = First Addend
]2 = Second Addend | +| Output | none | +| Dependencies | `SUB.ADDIT16` | +| Flags Destroyed | NZCV | +| Cycles | 78+ | +| Bytes | 51 | +| Notes | None | +| See Also | `ADD8` `ADDIT16` | + +--- + +*DETAILS* + +The `ADD16` macro adds two 16-bit numbers, passing back the 16-bit result in `RETURN` and in the **.A** (low byte) and **.X** (high byte) registers. + + + +`LISTING 4.71: ADD16 Macro Source` + +```assembly +* +*``````````````````````````````* +* ADD16 (NATHAN RIGGS) * +* * +* ADD TWO 16BIT VALUES, STORE * +* RESULT IN .A, .X (LOW, HIGH) * +* * +* PARAMETERS * +* * +* ]1 = ADDEND 1 * +* ]2 = ADDEND 2 * +* * +* CYCLES: 78+ * +* SIZE: 51 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +ADD16 MAC + _MLIT ]1;WPAR1 ; {16C12B} + _MLIT ]2;WPAR2 ; {16C12B} + JSR ADDIT16 ; {46C27B} + <<< +* + +``` + + + +--- + + + +### THE ADDIT16 SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------------- | +| Name | `ADDIT16` | +| Type | Subroutine | +| File | `SUB.ADDIT16.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Add two 16-bit numbers | +| Input | WPAR1 = Augend
WPAR2 = Addend | +| Output | none | +| Dependencies | `SUB.ADDIT16` | +| Flags Destroyed | NZCV | +| Cycles | 40+ | +| Bytes | 24 | +| Notes | None | +| See Also | `ADD16` | + +--- + +*DETAILS* + +The `ADDIT16` subroutine adds two 16-bit numbers and passes back a 16-bit sum via `RETURN` and the .A (low byte) and .X (high byte) registers. `RETLEN` holds #2, the byte-length of the return value. + + + +`LISTING 4.72: ADDIT16 Macro Source` + +```assembly +* +*``````````````````````````````* +* ADDIT16 (NATHAN RIGGS) * +* * +* ADD TWO 16-BIT VALUES. * +* * +* INPUT: * +* * +* WPAR1 = AUGEND * +* WPAR2 = ADDEND * +* * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 40 * +* SIZE: 24 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]ADD1 EQU WPAR1 ; FIRST ARGUMENT +]ADD2 EQU WPAR2 ; SECOND ARGUMENT +* +ADDIT16 + LDA #2 ; {2C2B} PUT SUMMAND LENGTH + STA RETLEN ; {4C3B} IN RETLEN + LDA ]ADD1 ; {3C2B} ADD LOBYTES + CLC ; {2C1B} CLEAR CARRY + ADC ]ADD2 ; {3C2B} + TAY ; {2C1B} TEMPORARY STORE IN .Y + LDA ]ADD1+1 ; {3C2B} ADD HIBYTES + ADC ]ADD2+1 ; {3C2B} + TAX ; {2C1B} STORE IN .X + TYA ; {2C1B} XFER LOBYTE TO .A + STA RETURN ; {4C3B} ALSO PASS BACK IN RETURN + STX RETURN+1 ; {4C3B} TO KEEP CONSISTENT + RTS ; {6C1B} + +``` + + + +--- + + + +### THE SUB16 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------- | +| Name | `SUB16` | +| Type | Macro | +| File | `MAC.MATH16.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Subtract a 16-bit number from another | +| Input | ]1 = Minuend
]2 = Subtrahend | +| Output | none | +| Dependencies | `SUB.SUBT16` | +| Flags Destroyed | NZCV | +| Cycles | 79+ | +| Bytes | 51 | +| Notes | None | +| See Also | `SUB8` `SUBT16` | + +--- + +*DETAILS* + +The `SUB16` macro subtracts one 16-bit number from another, storing the difference in `RETURN` an in the **.A** (low byte) and **.X** (high byte) registers. The byte length of the difference, two, is stored in `RETLEN`. + + + +`LISTING 4.73: SUB16 Macro Source` + +```assembly +* +*``````````````````````````````* +* SUB16 (NATHAN RIGGS) * +* * +* SUBTRACTS ONE 16BIT INTEGER * +* FROM ANOTHER, STORING THE * +* RESULT IN .A, .X (LOW, HIGH) * +* * +* PARAMETERS * +* * +* ]1 = MINUEND * +* ]2 = SUBTRAHEND * +* * +* CYCLES: 79+ * +* SIZE: 51 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +SUB16 MAC + _MLIT ]1;WPAR1 ; {16C12B} + _MLIT ]2;WPAR2 ; {16C12B} + JSR SUBT16 ; {47C27B} + <<< +* + +``` + + + +--- + + + +### THE SUBT16 SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | --------------------------------------- | +| Name | `SUBT16` | +| Type | Subroutine | +| File | `SUB.SUBT16.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | subtract one 16-bit number from another | +| Input | WPAR1 = Minuend
WPAR2 = Subtrahend | +| Output | none | +| Dependencies | `SUB.SUBT16` | +| Flags Destroyed | NZCV | +| Cycles | 41+ | +| Bytes | 24 | +| Notes | None | +| See Also | `SUB16` | + +--- + +*DETAILS* + +The `SUBT16` subroutine subtracts one 16-bit number from another, storing the difference in `RETURN` as well as in the .A (low byte) and .X registers (high byte). The byte-length of the return value, two, is held in `RETLEN`. + + + +`LISTING 4.74: ADDIT16 Macro Source` + +```assembly +* +*``````````````````````````````* +* SUBT16 (NATHAN RIGGS) * +* * +* SUBTRACT A 16-BIT SUBTRAHEND * +* FROM A MINUEND. * +* * +* INPUT * +* * +* WPAR1 = MINUEND * +* WPAR2 = SUBTRAHEND * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 41+ * +* SIZE: 24 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]MINU EQU WPAR1 ; MINUEND +]SUBT EQU WPAR2 ; SUBTRAHEND +* +SUBT16 + LDA #2 ; {3C2B} + STA RETLEN ; {4C3B} + LDA ]MINU ; {3C2B} SUBTRACT SUBTRAHEND + SEC ; {2C1B} LOBYTE FROM MINUEND + SBC ]SUBT ; {3C2B} LOBYTE + TAY ; {2C1B} HOLD LOBYTE IN .Y + LDA ]MINU+1 ; {3C2B} SUBTRACT SUBTRAHEND + SBC ]SUBT+1 ; {3C2B} HIBYTE FROM MINUEND + TAX ; {2C1B} HIGH BYTE, PASS IN .X + TYA ; {2C1B} LOBYTE BACK IN .A + STA RETURN ; {4C3B} + STX RETURN+1 ; {4C3B} + RTS ; {6C1B} + +``` + + + +--- + + + +### THE MUL16 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | -------------------------------------- | +| Name | `SUB16` | +| Type | Macro | +| File | `MAC.MATH16.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Multiply two 16-bit numbers | +| Input | ]1 = Multiplier
]2 = Multiplicand | +| Output | none | +| Dependencies | `SUB.MULTU16` | +| Flags Destroyed | NZCV | +| Cycles | 144+ | +| Bytes | 91 | +| Notes | None | +| See Also | `MUL8` `MULT16` | + +--- + +*DETAILS* + +The `MUL16` macro multiplies two 16-bit values, holding the product in `RETURN` with a total byte-length of 4 in `RETLEN` (32-bit product). If the full 32 bits is not needed, the lower 16-bits of the product are held in the **.A** register (low byte) and the **.X** register (high byte). + + + +`LISTING 4.75: MUL16 Macro Source` + +```assembly +* +*``````````````````````````````* +* MUL16 (NATHAN RIGGS) * +* * +* MULTIPLIES TWO 16BIT NUMBERS * +* AND RETURNS THE PRODUCT IN * +*.A, .X (LOW, HIGH). * +* * +* PARAMETERS * +* * +* ]1 = MULTIPLICAND * +* ]2 = MULTIPLIER * +* * +* CYCLES: 144+ * +* SIZE: 91 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +MUL16 MAC + _MLIT ]1;WPAR1 ; {16C12B} + _MLIT ]2;WPAR2 ; {16C12B} + JSR MULTU16 ; {112C67B} + <<< +* + +``` + + + +--- + + + +### THE MULTU16 SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | -------------------------------------------- | +| Name | `MULTU16` | +| Type | Subroutine | +| File | `SUB.MULTU16.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Multiply two 16-bit numbers | +| Input | WPAR1 = Multiplier
WPAR2 = Multiplicand | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 106+ | +| Bytes | 64 | +| Notes | None | +| See Also | `MUL16` | + +--- + +*DETAILS* + +The `MULTu16` subroutine accepts two 16-bit values and multiplies them, passing back the 32-bit product via `RETURN`, with `RETLEN` holding its byte-length, four. Additionally, the bottom two bytes of the product are held in the **.A** (low byte) and **.X** (high byte) registers for faster retrieval if the number is known to be 16 bits. + + + +`LISTING 4.76: MULTU16 Subroutine Source` + +```assembly +* +*``````````````````````````````* +* MULT16 (NATHAN RIGGS) * +* * +* MULTIPLY TWO 16-BIT VALUES. * +* NOTE THAT THIS ONLY WORKS * +* CORRECTLY WITH UNSIGNED * +* VALUES. SIGNS MUST BE * +* DETERMINED BEFOREHAND. * +* * +* INPUT: * +* * +* WPAR1 = MULTIPLICAND * +* WPAR2 = MULTIPLIER * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 106+ * +* SIZE: 64 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]MCAND EQU WPAR1 ; MULTIPLICAND +]MLIER EQU WPAR2 ; MULTIPLIER +]HPROD EQU WPAR3 ; HIGH BYTES OF PRODUCT +* +MULTU16 + LDA #0 ; {3C2B} ZERO OUT TOP TWO + STA ]HPROD ; {4C3B} HIGH BYTES OF 32-BIT + STA ]HPROD+1 ; {4C3B} RESULT + LDX #17 ; {4C3B} # OF BITS IN MLIER PLUS 1 + ; FOR LAST CARRY INTO PRODUCT + CLC ; {2C1B} CLEAR CARRY FOR 1ST TIME + ; THROUGH LOOP. +:MLP +* +** IF NEXT BIT = 1, HPROD += 1 +* + ROR ]HPROD+1 ; {5C2B} SHIFT HIGHEST BYTE + ROR ]HPROD ; {5C2B} SHIFT 2ND-HIGHEST + ROR ]MLIER+1 ; {5C2B} SHIFT 3RD-HIGHEST + ROR ]MLIER ; {5C2B} SHIFT LOW BYTE + BCC :DX ; {3C2B} BRANCH IF NEXT BIT = 0 + CLC ; {2C1B} OTHERWISE NEXT BIT =1, + LDA ]MCAND ; {3C2B} SO ADD MCAND TO PRODUCT + ADC ]HPROD ; {3C2B} + STA ]HPROD ; {3C2B} STORE NEW LOBYTE + LDA ]MCAND+1 ; {3C2B} + ADC ]HPROD+1 ; {3C2B} + STA ]HPROD+1 ; {3C2B} STORE NEW HIBYTE +:DX + DEX ; {2C1B} DECREASE COUNTER + BNE :MLP ; {3C2B} DO MUL LOOP UNTIL .X = 0 +* +** NOW STORE IN RETURN, WITH LOWEST TWO +** BYTES ALSO LEFT IN .A (LO) AND .X (HI) +* + LDA #4 ; {3C2B} LENGTH OF PRODUCT + STA RETLEN ; {4C3B} STORED IN RETLEN + LDA ]HPROD+1 ; {3C2B} + STA RETURN+3 ; {4C3B} + LDA ]HPROD ; {3C2B} + STA RETURN+2 ; {4C3B} + LDX ]MLIER+1 ; {3C2B} + STX RETURN+1 ; {4C3B} + LDA ]MLIER ; {3C2B} + STA RETURN ; {4C3B} + RTS ; {6C1B} + +``` + + + +--- + + + +### THE DIV16 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ----------------------------------- | +| Name | `DIV16` | +| Type | Macro | +| File | `MAC.MATH16.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Divide one 16-bit number by another | +| Input | ]1 = Dividend
]2 = Divisor | +| Output | none | +| Dependencies | `SUB.DIVDU16` | +| Flags Destroyed | NZCV | +| Cycles | 133+ | +| Bytes | 81 | +| Notes | None | +| See Also | `DIV8` `DIVDU16` | + +--- + +*DETAILS* + +The `DIV16` macro divides one 16-bit number by another, storing the 16-bit result in both `RETURN` and in the **.A** (low byte) and **.X** (high) registers. Additionally, any possible remainder is passed back via the **.Y** register. `RETLEN` holds the value 2, which represents the byte-length of the result. + +Note that no considerations are given for signed numbers in this subroutine. If necessary, a macro wrapper for signed values will be provided in future updates. + + + +`LISTING 4.77: DIV16 Macro Source` + +```assembly +* +*``````````````````````````````* +* DIV16 (NATHAN RIGGS) * +* * +* DIVIDES ONE 16BIT NUMBER BY * +* ANOTHER AND RETURNS THE * +* RESULT IN .A, .X (LOW,HIGH). * +* * +* PARAMETERS * +* * +* ]1 = DIVIDEND * +* ]2 = DIVISOR * +* * +* CYCLES: 133+ * +* SIZE: 81 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +DIV16 MAC + _MLIT ]1;WPAR1 ; {16C12B} + _MLIT ]2;WPAR2 ; {16C12B} + JSR DIVDU16 ; {101C57B} UNSIGNED + FIN + <<< +* + +``` + + + +--- + + + +### THE DIVDU16 SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------- | +| Name | `DIVDU16` | +| Type | Subroutine | +| File | `SUB.DIVDU16.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Divide one 16-bit number by another | +| Input | WPAR1 = Dividend
WPAR2 = Divisor | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 95+ | +| Bytes | 54 | +| Notes | None | +| See Also | `DIV16` | + +--- + +*DETAILS* + +The `MULTu16` subroutine accepts two 16-bit values and multiplies them, passing back the 32-bit product via `RETURN`, with `RETLEN` holding its byte-length, four. Additionally, the bottom two bytes of the product are held in the **.A** (low byte) and **.X** (high byte) registers for faster retrieval if the number is known to be 16 bits. + + + +`LISTING 4.78: DIVDU16 Subroutine Source` + +```assembly +* +*``````````````````````````````* +* DIVDU16 (NATHAN RIGGS) * +* * +* DIVIDE WITH 16-BIT VALUES. * +* * +* ADAPTED FROM LISTINGS IN THE * +* C=64 MAGAZINES. * +* * +* INPUT: * +* * +* WPAR1 = DIVIDEND * +* WPAR2 = DIVISOR * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 95+ * +* SIZE: 54 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]DVEND EQU WPAR1 ; DIVIDEND +]DVSOR EQU WPAR2 ; DIVISOR +]REM EQU WPAR3 ; REMAINDER +]RESULT EQU WPAR1 ; DIVISION RESULT +* +DIVDU16 + LDA #0 ; {4C3B} RESET REMAINDER + STA ]REM ; {3C2B} + STA ]REM+1 ; {3C2B} + LDX #16 ; {3C2B} NUMBER OF BITS +:DVLP + ASL ]DVEND ; {5C2B} LOBYTE * 2 + ROL ]DVEND+1 ; {5C2B} HIBYTE * 2 + ROL ]REM ; {5C2B} LOBYTE * 2 + ROL ]REM+1 ; {5C2B} HIBYTE * 2 + LDA ]REM ; {3C2B} + SEC ; {2C1B} SET CARRY + SBC ]DVSOR ; {3C2B} SUBTRACT DIVISOR + TAY ; {2C1B} TO SEE IF IT FITS IN DVEND, + LDA ]REM+1 ; {3C2B} HOLD LOBYTE IN .Y + SBC ]DVSOR+1 ; {3C2B} AND DO SAME WITH HIBYTES + BCC :SKIP ; {3C2B} IF C=0, DVSOR DOESN'T FIT +* + STA ]REM+1 ; {3C2B} ELSE SAVE RESULT AS REM + STY ]REM ; {3C2B} + INC ]RESULT ; {5C2B} AND INC RES +:SKIP + DEX ; {2C1B} DECREASE BIT COUNTER + BNE :DVLP ; {3C2B} RELOOP IF > 0 + LDA #2 ; {3C2B} LENGTH OF RESULT IN BYTES + STA RETLEN ; {4C3B} STORED IN RETLEN + LDA ]RESULT ; {3C2B} STORE RESULT LOBYTE + STA RETURN ; {4C3B} IN .A AND RETURN + LDX ]RESULT+1 ; {3C2B} STORE HIBYTE IN .X + STX RETURN+1 ; {4C3B} AND IN RETURN+1 + RTS ; {6C1B} + +``` + + + +--- + + + +### THE CMP16 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `CMP16` | +| Type | Macro | +| File | `MAC.MATH16.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Compare two 16-bit numbers | +| Input | ]1 = First comparison argument
]2 = Second comparison argument | +| Output | none | +| Dependencies | `SUB.COMP16` | +| Flags Destroyed | NZCV | +| Cycles | 100+ | +| Bytes | 53 | +| Notes | None | +| See Also | `COMP16` | + +--- + +*DETAILS* + +The `CMP16` macro mimics the `CMP` instruction, except that it compares 16-bit values instead of the regular 8-bit values. The macro accepts two parameters, then sets the status flags based on the results of a comparison between the values. The status flags are set as such: + +- If the values are equal, then the Zero flag is set to 1 + +- If the first value is greater than the second, then the Carry flag is set to zero + +- If the first value is less than or equal to the second, then the Carry flag is set to 1 + +- If a signed first value is greater than a signed second value, then the Negative flag is set to 0 + +- If a signed first value is less than or equal to the second, the the Negative flag is set to 1 + + + +`LISTING 4.79: CMP16 Macro Source` + +```assembly +* +*``````````````````````````````* +* CMP16 (NATHAN RIGGS) * +* * +* COMPARES TWO 16BIT VALUES * +* AND ALTERS THE P-REGISTER * +* ACCORDINGLY (FLAGS). * +* * +* PARAMETERS * +* * +* ]1 = WORD 1 TO COMPARE * +* ]2 = WORD 2 TO COMPARE * +* * +* CYCLES: 100+ * +* SIZE: 53 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +CMP16 MAC + _MLIT ]1;WPAR1 ; {16C12B} + _MLIT ]2;WPAR2 ; {16C12B} + JSR COMP16 ; {59C29B} + <<< + +``` + + + +--- + + + +### THE COMP16 SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `COMP16` | +| Type | Subroutine | +| File | `SUB.COMP16.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Compare one 16-bit value to another | +| Input | WPAR1 = First comparison argument
WPAR2 = Second comparison argument | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 53+ | +| Bytes | 26 | +| Notes | None | +| See Also | `CMP16` | + +--- + +*DETAILS* + +The `COMP16` subroutine compares one 16-bit value to another, much like the `CMP` instruction for 8-bit values. The flags are set given the following conditions: + +- If the values are equal, then the Zero flag is set to 1 +- If the first value is greater than the second, then the Carry flag is set to zero +- If the first value is less than or equal to the second, then the Carry flag is set to 1 +- If a signed first value is greater than a signed second value, then the Negative flag is set to 0 +- If a signed first value is less than or equal to the second, the the Negative flag is set to 1 + + + +`LISTING 4.80: COMP16 Subroutine Source` + +```assembly +* +*``````````````````````````````* +* COMP16 (NATHAN RIGGS) * +* * +* 16-BIT COMPARISON DIRECTIVE * +* * +* BASED ON LEVENTHAL AND * +* SAVILLE'S /6502 ASSEMBLY * +* LANGUAGE ROUTINES/ LISTING * +* * +* Z FLAG = 1 IF VALUES EQUAL * +* C FLAG = 0 IF CMP1 > CMP2, * +* 1 IF CMP1 <= CMP2 * +* N FLAG = 1 IF SIGNED CMP1 > * +* SIGNED CMP2, 0 IF * +* SIGNED CMP1 <= * +* SIGNED CMP2 * +* * +* INPUT: * +* * +* ]WPAR1 = 16-BIT CMP VALUE * +* ]WPAR2 = 16-BIT CMP VALUE * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 53+ * +* SIZE: 26 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]CMP1 EQU WPAR1 ; COMPARISON VAR 1 +]CMP2 EQU WPAR2 ; COMPARISON VAR 2 +* +COMP16 + LDA ]CMP1 ; {3C2B} FIRST, COMPARE LOW BYTES + CMP ]CMP2 ; {3C2B} + BEQ :EQUAL ; {3C2B} BRANCH IF EQUAL + LDA ]CMP1+1 ; {3C2B} COMPARE HIGH BYTES + SBC ]CMP2+1 ; {3C2B} SET ZERO FLAG TO 0, + ORA #1 ; {2C1B} SINCE LOW BYTES NOT EQUAL + BVS :OVFLOW ; {3C2B} HANDLE V FLAG FOR SIGNED + RTS ; {6C1B} +:EQUAL + LDA ]CMP1+1 ; {3C2B} COMPARE HIGH BYTES + SBC ]CMP2+1 ; {3C2B} + BVS :OVFLOW ; {3C2B} HANDLE OVERFLOW FOR SIGNED + RTS ; {6C1B} +:OVFLOW + EOR #$80 ; {3C2B} COMPLEMENT NEGATIVE FLAG + ORA #1 ; {3C2B} IF OVERFLOW, Z = 0 + RTS ; {6C1B} + +``` + + + +--- + + + +### Pseudorandom Number Generation Macros and Subroutines + +The final group of macros and subroutines in the math collection is dedicated to generating and managing pseudorandom numbers. The value of the number generated depends on an initial **seed** and a "magic number" that the seed is `EOR`'d by, and these are usually set before any pseudorandom number is generated. Additionally, these numbers are highly variable, but ultimately predictable if you know the seed, the `EOR` value, and the number of times a number is generated. This is useful for instances when a programmer wants definite and discrete results that are reproducible and seem planned but are simply based on the generation algorithm. The Atari game *Pitfall*, for instance, uses this exact method to determine the makeup of each screen of the game, saving valuable memory space while remaining regular enough to seem intentionally designed. + + + +`LISTING 4.90: MAC.MATHRND.ASM Header Source` + +```assembly +* +*``````````````````````````````* +* MAC.MATHEXT.ASM * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 03-APR-2021 * +* ASSEMBLER: MERLIN 8 PRO * +* OS: DOS 3.3 * +* * +* SUBROUTINE FILES USED * +* * +* SUB.RAND16.ASM * +* SUB.RAND8.ASM * +* SUB.RANDB.ASM * +* SUB.PERCT16.ASM * +* SUB.GCFCT16.ASM * +* SUB.LCMUL16.ASM * +* * +* LIST OF MACROS * +* * +* RAND : GET RANDOM BETWEEN * +* RND16 : RANDOM WORD * +* RND8 : RANDOM BYTE * +* RNDMZ : RANDOMIZE * +* RNDEOR: SET PRNG SEED + EOR * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* + +``` + + + +--- + + + +### THE RNDEOR MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------ | +| Name | `RNDEOR16` | +| Type | Macro | +| File | `MAC.MATHRND.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Set the seed and magic number for pseudorandom numbers | +| Input | ]1 = Seed
]2 = EOR value | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 30+ | +| Bytes | 22 | +| Notes | None | +| See Also | `RNDMZ` | + +--- + +*DETAILS* + +The `RNDEOR` macro allows you to directly set both the seed and the magic number used by `EOR` in pseudorandom number generation. This is different from the `RNDMZ` in that the number you assign to the magic number becomes its actual value, rather than be used as an index in a lookup table. + + + +`LISTING 4.91: RNDEOR Macro Source` + +```assembly +* +*``````````````````````````````* +* RNDEOR (NATHAN RIGGS) * +* * +* DIRECTLY DETERMINE THE PRNG * +* SEED AND EOR VALUES. * +* * +* PARAMETERS * +* * +* ]1 = SEED VALUE * +* ]2 = EOR MAGIC NUMBER VALUE * +* * +* CYCLES: 30+ * +* SIZE: 22 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +RNDEOR MAC + LDA ]1 ; {4C3B} + STA RNDL ; {3C2B} + LDA ]1+1 ; {4C3B} + STA RNDH ; {3C2B} + LDA ]2 ; {4C3B} + STA ]MAGEOR ; {4C3B} + LDA ]2+1 ; {4C3B} + STA ]MAGEOR+1 ; {4C3B} + <<< +* + +``` + + + +--- + + + +### THE RNDMZ MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------ | +| Name | `RNDMZ` | +| Type | Macro | +| File | `MAC.MATHRND.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Set the seed and magic number for pseudorandom numbers | +| Input | ]1 = Seed
]2 = EOR value | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 56+ | +| Bytes | 35 | +| Notes | None | +| See Also | `RNDEOR` | + +--- + +*DETAILS* + +The `RNDMZ` macro first lets you set the seed value that initializes the pseudorandom number generator. Then, in the separate parameter, a byte value is passed that acts as the index for a table of 64 predetermined "magic numbers" (2 bytes long each) that are used for maximum variability in the generation of each number outputted. The second parameter is divided by four to ensure that it is a value between zero and 63, and then the magic number is picked from the following table of values (found in the math collection header file): + +`LISTING 3.91: Magic Number Table` + +``` +002D0039003F005300BD00D7012F013D +014F015D019701A101AD01BF01C70215 +02190225022F025D026D0285029102A1 +02E5031D034B036903710387038D039F +03A303DD03F904290457046704830489 +049104BF04C1053305470569058705C3 +05DD05EB0641064B0653068B06C3076B +076D0779078307F1080D086108BF08D5 + +``` + + + +`LISTING 4.92: RNDEOR Macro Source` + +```assembly +* +*``````````````````````````````* +* RNDMZ (NATHAN RIGGS) * +* * +* ASSIGN A SEED VALUE FOR THE * +* PSEUDORANDOM NUMBER * +* GENERATOR AS WELL AS AN EOR * +* "MAGIC NUMBER." NO MATTER * +* THE EOR VALUE PROVIDED, A * +* VALUE WILL BE TAKEN FROM A * +* TABLE THAT ALLOWS FOR A FULL * +* 65536 NON-REPEATED NUMBERS. * +* * +* PARAMETERS * +* * +* ]1 = SEED VALUE * +* ]2 = EOR NUMBER REFERENCE * +* * +* CYCLES: 56+ * +* SIZE: 35 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +RNDMZ MAC + LDA ]1 ; {4C3B} + STA RNDL ; {3C2B} + LDA ]1+1 ; {4C3B} + STA RNDH ; {3C2B} +* + LDA ]2 ; {4C3B} + LSR ; {2C1B} + LSR ; {2C1B} + TAY ; {2C1B} + LDA #]MAGNUM ; {3C2B} + STA ADDR1 ; {3C2B} + LDA #]MAGNUM/$100 ; {3C2B} + STA ADDR1+1 ; {3C2B} + LDA (ADDR1),Y ; {5C2B} + STA ]MAGEOR ; {4C3B} + INY ; {2C1B} + LDA (ADDR1),Y ; {5C2B} + STA ]MAGEOR+1 ; {4C3B} + <<< +* + +``` + + + +--- + + + +### THE RND8 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------- | +| Name | `RND8` | +| Type | Macro | +| File | `MAC.MATHRND.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Generate an 8-bit pseudorandom number | +| Input | none | +| Output | none | +| Dependencies | `RAND8` | +| Flags Destroyed | NZCV | +| Cycles | 51+ | +| Bytes | 30 | +| Notes | None | +| See Also | `RAND8` `RND16` | + +--- + +*DETAILS* + +The `RND8` macro generates a pseudorandom number between 0 and 255. The number is passed back via the .A register as well as in `RETURN`. + +`LISTING 4.93: RND8 Macro Source` + +``` +* +*``````````````````````````````* +* RND8 (NATHAN RIGGS) * +* * +* RETURN AN 8-BIT PSEUDORANDOM * +* NUMBER. * +* * +* PARAMETERS * +* * +* NONE * +* * +* CYCLES: 51+ * +* SIZE: 30 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +RND8 MAC + JSR RAND8 ; {51C30B} + <<< +* + +``` + + + +--- + + + +### THE RAND8 SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------- | +| Name | `RAND8` | +| Type | Subroutine | +| File | `SUB.RAND8.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Generate an 8-bit pseudorandom number | +| Input | none | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 45+ | +| Bytes | 27 | +| Notes | None | +| See Also | `RND8` `RAND16` | + +--- + +*DETAILS* + +The `RAND8` subroutine generates a pseudorandom number between 0 and 255. + + + +`LISTING 4.94: RND8 Subroutine Source` + +```assembly +* +*``````````````````````````````* +* RAND8 (NATHAN RIGGS) * +* * +* GENERATE PSEUDO-RANDOM BYTE * +* * +* INPUT: * +* * +* NONE * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 45+ * +* SIZE: 27 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +RAND8 + LDX #8 ; {3C2B} NUMBER OF BITS + LDA RNDL+0 ; {3C2B} GET SEED +:A + ASL ; {2C1B} SHIFT THE REG + ROL RNDL+1 ; {5C2B} ROTATE HIGH BYTE + BCC :B ; {3C2B} IF 1 BIT SHIFTED OUT, + EOR ]MAGEOR ; {2C2B} APPLY XOR FEEDBACK +:B + DEX ; {2C1B} DECREASE BIT COUNTER + BNE :A ; {3C2B} IF NOT ZERO, RELOOP + STA RNDL+0 ; {3C2B} STORE NEW SEED + STA RETURN ; {4C3B} STORE IN RETURN + LDY #1 ; {3C2B} RETURN BYTE LENGTH + STY RETLEN ; {4C3B} IN RETLEN + CMP #0 ; {2C2B} RELOAD FLAGS + RTS ; {6C1B} +* + +``` + + + +--- + + + +### THE RAND MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `RAND` | +| Type | Macro | +| File | `MAC.MATHRND.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Generate an 8-bit pseudorandom number
between a given low and high value | +| Input | ]1 = lower boundary
]2 = higher boundary | +| Output | none | +| Dependencies | `RANDB` | +| Flags Destroyed | NZCV | +| Cycles | 263+ | +| Bytes | 171 | +| Notes | None | +| See Also | `RND8` `RND16` `RANDB` | + +--- + +*DETAILS* + +The `RAND` macro takes a lower boundary and a higher boundary, both between 0 and 255, and generates a pseudo-random number between them. When possible, `RND8` should be used due to the much higher number of cycles and bytes used in calculating the number between new boundaries. + +`LISTING 4.95: RAND Macro Source` + +```assembly +* +*``````````````````````````````* +* RAND (NATHAN RIGGS) * +* * +* RETURNS A RANDOM NUMBER IN * +* REGISTER .A THAT IS BETWEEN * +* THE LOW AND HIGH BOUNDARIES * +* PASSED IN THE PARAMETERS. * +* * +* NOTE THAT THIS RETURNS A * +* BYTE, AND THUS ONLY DEALS * +* WITH VALUES BETWEEN 0..255. * +* * +* PARAMETERS * +* * +* ]1 = LOW BOUNDARY * +* ]2 = HIGH BOUNDARY * +* * +* CYCLES: 263+ * +* SIZE: 171 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +RAND MAC + LDA ]1 ; {4C3B} LOW + LDX ]2 ; {4C3B} HIGH + JSR RANDB ; {255C165B} + <<< +* + +``` + + + +--- + + + +### THE RANDB SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------- | +| Name | `RANDB` | +| Type | Subroutine | +| File | `SUB.RANDB.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Generate a pseudorandom number between a low and a high | +| Input | BPAR1 = Lower boundary
BPAR2 = Upper boundary | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 249+ | +| Bytes | 162 | +| Notes | None | +| See Also | `RAND` `RND8` | + +--- + +*DETAILS* + +The `RANDB` subroutine generates a pseudorandom number between a given lower boundary and a high boundary. The number generated is stored in the **.A** register as well as in `RETURN`. + + + +`LISTING 4.96: RANDB Subroutine Source` + +```assembly +* +*``````````````````````````````* +* RANDB (NATHAN RIGGS) * +* * +* GET A RANDOM VALUE BETWEEN * +* A MIN AND MAX 8-BIT BOUNDARY * +* * +* INPUT: * +* * +* BPAR1 = MINIMUM VALUE * +* BPAR2 = MAXIMUM VALUE * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 249+ * +* SIZE: 162 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]NEWMIN EQU BPAR1 ; MINIMUM PARAMETER +]NEWMAX EQU BPAR2 ; MAXIMUM PARAMETER +]OLDMIN EQU WPAR1 ; OLD MINIMUM (1) +]OLDMAX EQU WPAR1+1 ; OLD MAXIMUM (255) +]OLDRNG EQU VARTAB ; OLD RANGE +]NEWRNG EQU VARTAB+2 ; NEW RANGE +]MULRNG EQU VARTAB+4 ; MULTIPLIED RANGE +]DIVRNG EQU VARTAB+6 ; DIVIDED RANGE +]VALRNG EQU VARTAB+8 ; VALUE RANGE +]OLDVAL EQU VARTAB+10 ; OLD VALUE +]NEWVAL EQU VARTAB+12 ; NEW VALUE +]NUM1HI EQU VARTAB+14 ; MULTIPLICATION HI BYTE +]REMAIN EQU VARTAB+16 ; REMAINDER +* +RANDB + STX ]NEWMAX ; {4C3B} NEW HIGH VALUE + STA ]NEWMIN ; {4C3B} NEW LOW VALUE OF RANGE +* +** GET OLDMIN,OLDMAX,OLDVAL +* + LDA #1 ; {3C2B} OLD LOW IS ALWAYS 1 + STA ]OLDMIN ; {3C2B} + LDA #255 ; {3C2B} OLD HIGH IS ALWAYS 255 + STA ]OLDMAX ; {3C2B} + LDX #8 ; {3C2B} NUMBER OF BITS IN # + LDA RNDL+0 ; {3C2B} LOAD SEED VALUE +:AA + ASL ; {2C1B} SHIFT ACCUMULATOR + ROL RNDL+1 ; {5C2B} + BCC :BB ; {3C2B} IF NEXT BIT IS 0, BRANCH + EOR ]MAGEOR ; {2C2B} ELSE, APPLY XOR FEEDBACK +:BB + DEX ; {2C1B} DECREASE .X COUNTER + BNE :AA ; {3C2B} IF > 0, KEEP LOOPING + STA RNDL+0 ; {3C2B} OVERWRITE SEED VALUE + CMP #0 ; {2C2B} RESET FLAGS + STA ]OLDVAL ; {4C3B} STORE RANDOM NUMBER +* +** NEWVALUE = (((OLDVAL-NEWMIN) * (NEWMAX-NEWMIN) / +** (OLDMAX-OLDMIN)) + NEWMIN +* +** OLDRANGE = (OLDMAX-OLDMIN) +** NEWRANGE = (NEWMAX - NEWMIN) +** NEWVAL = (((OLDVAL-OLDMIN) * NEWRANGE) / OLDRANGE) + NEWMIN +* + LDA ]OLDMAX ; {3C2B} SUBTRACT OLDMIN + SEC ; {2C1B} FROM OLDMAX, STORE + SBC ]OLDMIN ; {3C2B} IN OLDRANGE + STA ]OLDRNG ; {4C3B} + LDA ]NEWMAX ; {4C3B} SUBTRACT NEWMIN + SEC ; {2C1B} FROM NEWMAX, THEN + SBC ]NEWMIN ; {4C3B} STORE IN NEWRANGE + STA ]NEWRNG ; {4C3B} + LDA ]OLDVAL ; {4C3B} SUBTRACT OLDMIN + SEC ; {2C1B} FROM OLDVAL AND + SBC ]OLDMIN ; {3C2B} STORE IN VALRANGE + STA ]VALRNG ; {4C3B} +* +** GET MULRANGE: VALRANGE * NEWRANGE +* + LDA #00 ; {3C2B} CLEAR ACCUMULATOR, + TAY ; {2C1B} .Y AND THE HIGH BYTE + STY ]NUM1HI ; {4C3B} + BEQ :ENTLP ; {3C2B} IF ZERO, BRANCH +:DOADD + CLC ; {2C1B} CLEAR CARRY + ADC ]VALRNG ; {4C3B} ADD VALUE RANGE TO .A + TAX ; {2C1B} HOLD IN .X + TYA ; {2C1B} .Y BACK TO .A + ADC ]NUM1HI ; {4C3B} ADD HIBYTE + TAY ; {2C1B} MOVE BACK TO .Y + TXA ; {2C1B} .X BACK TO .A +:MLP + ASL ]VALRNG ; {5C2B} SHIFT VALUE RANGE + ROL ]NUM1HI ; {5C2B} ADJUST HIGH BYTE +:ENTLP + LSR ]NEWRNG ; {5C2B} SHIFT NEW RANGE + BCS :DOADD ; {3C2B} IF LAST BIT WAS 1, LOOP ADD + BNE :MLP ; {3C2B} IF ZERO FLAG CLEAR, LOOP SHIFT + STA ]MULRNG ; {4C3B} STORE RESULT LOW BYTE + STY ]MULRNG+1 ; {4C3B} STORE HIGH BYTE +* +** NOW GET DIVRANGE: MULRANGE / OLDRANGE +* +:DIVIDE + LDA #0 ; {3C2B} CLEAR ACCUMULATOR + STA ]REMAIN ; {4C3B} AND THE REMAINDER LOBYTE + STA ]REMAIN+1 ; {4C3B} AND REMAINDER HIBYTE + LDX #16 ; {3C2B} NUMBER OF BYTES +:DIVLP + ASL ]MULRNG ; {5C2B} LOW BYTE * 2 + ROL ]MULRNG+1 ; {5C2B} HIGH BYTE * 2 + ROL ]REMAIN ; {5C2B} REMAINDER LOW BYTE * 2 + ROL ]REMAIN+1 ; {5C2B} HIGH BYTE * 2 + LDA ]REMAIN ; {4C3B} SUBTRACT OLDRANGE + SEC ; {2C1B} FROM REMAINDER + SBC ]OLDRNG ; {4C3B} + TAY ; {2C1B} HOLD IN .Y + LDA ]REMAIN+1 ; {4C3B} SUBTRACT HIGH BYTES + SBC ]OLDRNG+1 ; {4C3B} + BCC :SKIP ; {3C2B} IF NO CARRY, THEN NOT DONE + STA ]REMAIN+1 ; {4C3B} SAVE SBC AS NEW REMAINDER + STY ]REMAIN ; {4C3B} + INC ]DIVRNG ; {6C3B} INCREMENT THE RESULT +:SKIP DEX ; {2C1B} DECREMENT COUNTER + BNE :DIVLP ; {3C2B} IF ZERNO, RELOOP +* +** NOW ADD NEWMIN TO DIVRANGE +* + LDA ]DIVRNG ; {4C3B} USE LOW BYTE ONLY + CLC ; {2C1B} AND ADD TO ]NEWMIN + ADC ]NEWMIN ; {4C3B} TO GET THE NEW VALUE + STA ]NEWVAL ; {4C3B} + STA RETURN ; {4C3B} COPY TO RETURN + LDX #1 ; {3C2B} RETURN LENGTH + STX RETLEN ; {4C3B} + RTS ; {6C1B} + +``` + + + +--- + + + +### THE RND16 MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | -------------------------------------- | +| Name | `RND16` | +| Type | Macro | +| File | `MAC.MATHRND.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Generate an 16-bit pseudorandom number | +| Input | none | +| Output | none | +| Dependencies | `RND16` | +| Flags Destroyed | NZCV | +| Cycles | 101+ | +| Bytes | 63 | +| Notes | None | +| See Also | `RND8` `RAND16` | + +--- + +*DETAILS* + +The `RND16` macro generates a 16-bit pseudorandom number. After being called, the value is stored in `RETURN` as well as in the .A (low byte) and .X (high byte) registers. `RETLEN` holds #2, the byte-length of the number. + + + +`LISTING 4.97: RND16 Macro Source` + +```assembly +* +*``````````````````````````````* +* RND16 (NATHAN RIGGS) * +* * +* RETURN A 16-BIT PSEUDORANDOM * +* NUMBER. * +* * +* PARAMETERS * +* * +* NONE * +* * +* CYCLES: 101+ * +* SIZE: 63 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +RND16 MAC + JSR RAND16 ; {101C63B} + <<< +* + +``` + + + +--- + + + +### THE RAND16 SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------- | +| Name | `RAND16` | +| Type | Subroutine | +| File | `SUB.RAND16.ASM` | +| Author | Nathan Riggs | +| Last Revision | 02-APR-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Generate a 16-bit pseudorandom number | +| Input | none | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 95+ | +| Bytes | 60 | +| Notes | None | +| See Also | `RND16` | + +--- + +*DETAILS* + +The `RAND16` subroutine generates a pseudorandom number between 0 and 65,535 (16-bits). This value is stored in `RETURN` with its byte-length, two, in `RETLEN`. Additionally, the value is returned in the **.A** (low byte) and **.X** (high byte) registers. + + + +`LISTING 4.98: RANDB Subroutine Source` + +```assembly +*``````````````````````````````* +* RAND16 : 16BIT RANDOM NUMBER * +* * +* GENERATE A 16BIT PSEUDO- * +* RANDOM NUMBER AND RETURN IT * +* IN A,X (LOW, HIGH). * +* * +*------------------------------* +* ORIGINAL AUTHOR IS WHITE * +* FLAME, AS SHARED ON * +* CODEBASE64. * +*------------------------------* +* * +* INPUT: * +* * +* NONE * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 95+ * +* SIZE: 60 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]SEED EQU WPAR1 ; SEED FOR PRNG +* +RAND16 + LDA RNDL ; {3C2B} GET SEED LOBYTE + STA ]SEED ; {3C2B} + LDA RNDH ; {3C2B} GET SEED HIBYTE + STA ]SEED+1 ; {3C2B} +* + LDA ]SEED ; {3C2B} CHECK IF $0 OR $8000 + BEQ :LOW0 ; {3C2B} +* +** DO A NORMAL SHIFT +* + ASL ]SEED ; {5C2B} MUTATE + LDA ]SEED+1 ; {3C2B} + ROL ; {2C1B} + BCC :NOEOR ; {3C2B} IF CARRY CLEAR, EXIT +:DOEOR ; HIGH BYTE IN A + EOR ]MAGEOR+1 ; {3C2B} EOR WITH MAGIC NUMBER + STA ]SEED+1 ; {3C2B} STORE BACK INTO HIBYTE + LDA ]SEED ; {3C2B} + EOR ]MAGEOR ; {3C2B} DO SAME WITH LOW BYTE + STA ]SEED ; {3C2B} + JMP :EXIT ; {3C3B} +:LOW0 + LDA ]SEED+1 ; {3C2B} + BEQ :DOEOR ; {3C2B} IF HI ALSO 0, APPLY EOR + ASL ; {2C1B} + BEQ :NOEOR ; {3C2B} IF 00, THEN IT WAS $80 + BCS :DOEOR ; {3C2B} ELSE DO EOR +:NOEOR + STA ]SEED+1 ; {3C2B} +:EXIT + LDX ]SEED+1 ; {3C2B} VAL HIBYTE IN .X + LDY ]SEED ; {3C2B} LOBYTE TEMP IN .Y + STX RNDH + STY RNDL + STY RETURN ; {4C3B} TRANSFER TO RETURN AREA + STX RETURN+1 ; {4C3B} + LDA #2 ; {3C2B} LENGTH OF RETURN IN BYTES + STA RETLEN ; {4C3B} + TYA ; {2C1B} TRANSFER LOBYTE TO .A + RTS ; {6C1B} + +``` + + + +--- + + + +# PART II: MATH COLLECTION DEMONSTRATIONS + +The following demo programs illustrate how to use the macros in the math collection that deal with dividing by constants, general-purpose integer math routines for 8-bit and 16-bit addition, subtraction, multiplication and division, and macros dedicated to generating and manipulating pseudorandom numbers. These span three different demo programs, grouped together as presented here. Note that these are not extensive tests, and should not be mistaken as such; they merely show the basic usage of the macros and one instance of each macro working correctly. The purpose of the demo files is to provide assistance to beginners rather than to do a thorough test of every subroutine. + + + +`LISTING 4.9A: The DEMO.MATHBY.ASM File Source` + +```assembly +* +*``````````````````````````````* +* DEMO.MATH * +* * +* A DEMO OF THE INTEGER MATH * +* MACROS INCLUDED AS PART OF * +* THE APPLEIIASM LIBRARY. * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 16-JUL-2019 * +* ASSEMBLER: MERLIN 8 PRO * +* OS: DOS 3.3 * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** ASSEMBLER DIRECTIVES +* + CYC AVE + EXP OFF + TR ON + DSK DEMO.MATHBY + OBJ $BFE0 + ORG $6000 +* +*``````````````````````````````* +* TOP INCLUDES (HOOKS,MACROS) * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* + PUT MIN.HEAD.REQUIRED.ASM + USE MIN.MAC.REQUIRED.ASM + PUT MIN.HEAD.MATH.ASM + USE MIN.MAC.M8BY.ASM + USE MIN.MAC.M16BY.ASM + USE MIN.MAC.D8BY.ASM +]HOME2 EQU $FC58 +* +*``````````````````````````````* +* PROGRAM MAIN BODY * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* + JSR ]HOME2 +* +** THIS DEMO SHOWS HOW TO USE THE QUICK +** MATH MACROS AVAILABLE FOR USE AS ALTERNATIVES +** TO THE NORMAL MATH ROUTINES. THESE MACROS +** DO NOT CALL ANY ROUTINES, AND ALL PASSING +** OF VARIABLES IS DONE VIA THE REGISTERS. THESE +** TEND TO BE QUICKER BECAUSE WE ARE MULTIPLYING +** AND DIVIDING BY CONSTANTS, THUS NOT REQUIRING +** THE USE OF LOOPING, CHECKING VALUES, ETC. +** EVERYTHING IS STRAIGHTFORWARDLY DEDICATED TO +** ONLY A SINGLE FUNCTION. +* +** WE SHALL START WITH 8-BIT MULTIPLICATION MACROS, +** FOLLOWED BY 16-BIT MULTIPLICATION, THEN 8-BIT +** DIVISION. CURRENTLY, NO 16-BIT DIVISION QUICK +** MATH ROUTINES EXIST, BECAUSE THE CYCLES SAVED IN +** COMPARISON TO THE BYTES USED WOULD PROBABLY BE +** MINIMAL. +* +*``````````````````````````````* +* 8-BIT QUICK MULTIPLICATION * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** STARTING WITH THE LOWEST NUMBER TO MULTIPLY BY, +** THE NUMBER TWO. +* + JSR ]HOME2 + _PRN "8-BIT CONSTANT MATH MACROS",8D + _PRN "==========================",8D8D + _PRN "#5 * 2 = ",8D8D + M8BY2 BIT8 + STA RETURN + DUMP #RETURN;#1 + _PRN " ",8D8D + _WAIT +* + M8BY3 BIT8 + STA RETURN + _PRN "#5 * #3 = ",8D8D + DUMP #RETURN;#1 + _PRN " ",8D8D + _WAIT +* + M8BY4 BIT8 + STA RETURN + _PRN "#5 * #4 = ",8D8D + DUMP #RETURN;#1 + _PRN " ",8D8D + _WAIT +* + M8BY5 BIT8 + STA RETURN + _PRN "#5 * #5 = ",8D8D + DUMP #RETURN;#1 + _PRN " ",8D8D + _WAIT +* + M8BY6 BIT8 + STA RETURN + _PRN "#5 * #6 = ",8D8D + DUMP #RETURN;#1 + _PRN " ",8D8D + _WAIT +* + M8BY7 BIT8 + STA RETURN + _PRN "#5 * #7 = ",8D8D + DUMP #RETURN;#1 + _PRN " ",8D8D + _WAIT +* + M8BY8 BIT8 + STA RETURN + _PRN "#5 * #8 = ",8D8D + DUMP #RETURN;#1 + _PRN " ",8D8D + _WAIT +* + M8BY9 BIT8 + STA RETURN + _PRN "#5 * #9 = ",8D8D + DUMP #RETURN;#1 + _PRN " ",8D8D + _WAIT +* + M8BY10 BIT8 + STA RETURN + _PRN "#5 * #10 = ",8D8D + DUMP #RETURN;#1 + _WAIT +* +*``````````````````````````````* +* 16-BIT CONST MULTIPLICATION * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** NOW WE WILL TEST THE 16-BIT MULTIPLY-BY- +** A-CONSTANT MACROS. +* + JSR ]HOME2 + _PRN "16-BIT CONSTANT MULTIPLICATION",8D + _PRN "==============================",8D8D +* + _PRN "#500 * #2 = ",8D8D + MBY2 BIT16 + STA RETURN + STX RETURN+1 + DUMP #RETURN;#2 + _WAIT +* + _PRN " ",8D8D + MBY3 BIT16 + STA RETURN + STX RETURN+1 + _PRN "#500 * #3 = ",8D8D + DUMP #RETURN;#2 + _WAIT + _PRN " ",8D8D +* + _PRN " ",8D8D + MBY4 BIT16 + STA RETURN + STX RETURN+1 + _PRN "#500 * #4 = ",8D8D + DUMP #RETURN;#2 + _WAIT + _PRN " ",8D8D +* + _PRN " ",8D8D + MBY5 BIT16 + STA RETURN + STX RETURN+1 + _PRN "#500 * #5 = ",8D8D + DUMP #RETURN;#2 + _WAIT + _PRN " ",8D8D +* + _PRN " ",8D8D + MBY6 BIT16 + STA RETURN + STX RETURN+1 + _PRN "#500 * #6 = ",8D8D + DUMP #RETURN;#2 + _WAIT + _PRN " ",8D8D +* + _PRN " ",8D8D + MBY7 BIT16 + STA RETURN + STX RETURN+1 + _PRN "#500 * #7 = ",8D8D + DUMP #RETURN;#2 + _WAIT + _PRN " ",8D8D +* + _PRN " ",8D8D + MBY8 BIT16 + STA RETURN + STX RETURN+1 + _PRN "#500 * #8 = ",8D8D + DUMP #RETURN;#2 + _WAIT + _PRN " ",8D8D +* + _PRN " ",8D8D + MBY9 BIT16 + STA RETURN + STX RETURN+1 + _PRN "#500 * #9 = ",8D8D + DUMP #RETURN;#2 + _WAIT + _PRN " ",8D8D +* + _PRN " ",8D8D + MBY10 BIT16 + STA RETURN + STX RETURN+1 + _PRN "#500 * #10 = ",8D8D + DUMP #RETURN;#2 + _WAIT + _PRN " ",8D8D +* + _PRN " ",8D8D + MBY1H BIT16 + STA RETURN + STX RETURN+1 + _PRN "#500 * #100 = ",8D8D + DUMP #RETURN;#2 + _WAIT + _PRN " ",8D8D +* +*``````````````````````````````* +* 8BIT DIVISION BY CONSTANTS * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* + JSR ]HOME2 + _PRN "8-BIT DIVISION BY CONSTANTS",8D + _PRN "===========================",8D8D +* + _PRN "#100 / 2 = ",8D8D + D8BY2 BIT82 + STA RETURN + DUMP #RETURN;#1 + _PRN " ",8D8D + _WAIT +* + _PRN "#100 / 3 = ",8D8D + D8BY3 BIT82 + STA RETURN + DUMP #RETURN;#1 + _PRN " ",8D8D + _WAIT +* + _PRN "#100 / 4 = ",8D8D + D8BY4 BIT82 + STA RETURN + DUMP #RETURN;#1 + _PRN " ",8D8D + _WAIT +* + _PRN "#100 / 5 = ",8D8D + D8BY5 BIT82 + STA RETURN + DUMP #RETURN;#1 + _PRN " ",8D8D + _WAIT +* + _PRN "#100 / 6 = ",8D8D + D8BY6 BIT82 + STA RETURN + DUMP #RETURN;#1 + _PRN " ",8D8D + _WAIT +* + _PRN "#100 / 7 = ",8D8D + D8BY7 BIT82 + STA RETURN + DUMP #RETURN;#1 + _PRN " ",8D8D + _WAIT +* + _PRN "#100 / 8 = ",8D8D + D8BY8 BIT82 + STA RETURN + DUMP #RETURN;#1 + _PRN " ",8D8D + _WAIT +* + _PRN "#100 / 9 = ",8D8D + D8BY9 BIT82 + STA RETURN + DUMP #RETURN;#1 + _PRN " ",8D8D + _WAIT +* +* + _PRN "#100 / 10 = ",8D8D + D8BY10 BIT82 + STA RETURN + DUMP #RETURN;#1 + _PRN " ",8D8D + _WAIT + JMP REENTRY +* +*``````````````````````````````* +* BOTTOM INCLUDES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** BOTTOM INCLUDES +* + PUT MIN.LIB.REQUIRED.ASM +* +BIT8 HEX 05 ; #5 +BIT82 HEX 64 +BIT16 HEX F401 ; #500 + +``` + + + + + +`LISTING 4.9B: The DEMO.MATHBAS.ASM File Source` + +```assembly +* +*``````````````````````````````* +* DEMO.MATHBAS.ASM * +* * +* A DEMO OF THE 8-BIT AND THE * +* 16-BIT MACROS FOR ADDING, * +* SUBTRACTING, MULTIPLYING AND * +* DIVIDING. A 16-BIT COMPARE * +* MACRO IS ALSO INCLUDED AS * +* PART OF THE COLLECTION. * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 02-APR-2021 * +* ASSEMBLER: MERLIN 8 PRO * +* OS: DOS 3.3 * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** ASSEMBLER DIRECTIVES +* + CYC AVE + EXP OFF + TR ON + DSK DEMO.MATHBAS + OBJ $BFE0 + ORG $6000 +* +*``````````````````````````````* +* TOP INCLUDES (HOOKS,MACROS) * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* + PUT MIN.HEAD.REQUIRED.ASM + USE MIN.MAC.REQUIRED.ASM + PUT MIN.HEAD.MATH.ASM + USE MIN.MAC.MATH8.ASM + USE MIN.MAC.MATH16.ASM +]HOME2 EQU $FC58 +* +*``````````````````````````````* +* PROGRAM MAIN BODY * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* + JSR ]HOME2 +* +*``````````````````````````````* +* 8-BIT BASIC MATH MACROS * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THIS COLLECTION CONTAINS MACROS FOR 8-BIT +** BASIC MATH, INCLUDING ADDITION, SUBTRACTION, +** MULTIPLICATION AND DIVISION. CURRENTLY, ONLY +** UNSIGNED NUMBERS ARE SUPPORTED, THOUGH THIS +** WILL LIKELY CHANGE IN A NEAR-FUTURE REVISION. +* +*``````````````````````````````* +* 8-BIT ADDITION * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** 8-BIT ADDITION IS RATHER TRIVIAL, BUT IS +** INCLUDED HERE FOR BEGINNERS' REFERENCE. IF +** YOU ARE CAPABLE OF WRITING THESE ROUTINES +** YOURSELF, YOU ARE LIKELY TO SAVE AT LEAST +** 8 CYCLES, AS THE MACRO USES THE RETURN +** ADDRESS FOR PASSING BACK THE SUM. THIS SUM +** IS ALSO PASSED BACK VIA THE .A REGISTER, +** BUT THERE ARE STILL WASTED CYCLES. +* +** ADDITION IS PARTICULARLY SIMPLE: PASS THE +** TWO BYTE VALUES TO BE ADDED, THEN EXPECT +** THE SUM IN .A AS WELL AS IN RETURN. +* +** NOTE THAT CURRENTLY, THE 8-BIT ADDITION +** AND SUBTRACTION ROUTINES ONLY ACCEPT A +** DIRECT ADDRESS, AND THUS DOES NOT ALLOW +** FOR THE INDIRECT ADDRESSING WORKAROUND THAT +** MOST OF THE REST OF THE LIBRARY USES. THIS +** IS FOR THE SAKE OF PRESERVING CYCLES AND +** DISCARDING BYTES. +* + JSR ]HOME2 + _PRN "8-BIT ADDITION",8D + _PRN "==============",8D8D + _PRN "10 + 20 =",8D + ADD8 NUM81;NUM82 + DUMP #RETURN;RETLEN + _WAIT +* +*``````````````````````````````* +* 8-BIT SUBTRACTION * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** 8-BIT SUBTRACTION WORKS THE SAME WAY AS +** ADDITION (EXCEPT, OBVIOUSLY, IN THE FACT +** THAT THEY ARE OPPOSITE OPERATIONS), AND +** ALSO HAS THE SAME LIMITATION: NO ADDRESSING +** MODES OTHER THAN THE STANDARD PASSING OF +** ADDRESSES WHERE THE ARGUMENT VALUES ARE +** LOCATED. +* + JSR ]HOME2 + _PRN "8-BIT SUBTRACTION",8D + _PRN "=================",8D8D + _PRN "20 - 10 =",8D + SUB8 NUM82;NUM81 + DUMP #RETURN;#1 + _WAIT +* +*``````````````````````````````* +* 8-BIT MULTIPLICATION * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** 8-BIT MULTIPLICATION IS A BIT MORE COMPLICATED +** THAN ADDITION AND SUBTRACTION UNDER THE HOOD, +** BUT CALLING IT REMAINS THE SAME. LIKE WITH +** THE OTHER 8-BIT BASIC MATH MACROS, ONLY LITERALS +** AND DIRECT ADDRESSES ARE ACCEPTED AS PARAMETERS, +** UNLIKE THE REST OF THE LIBRARY, TO SAVE RESOURCES +** IN SUBROUTINES THAT ARE LIKELY TO BE CALLED MUCH +** MORE THAN OTHERS AS PART OF A PROGRAM (AND OFTEN +** IN LOOPS). +* + JSR ]HOME2 + _PRN "8-BIT MULTIPLICATION",8D + _PRN "====================",8D8D + _PRN "10 * 20 = ",8D + MUL8 NUM81;NUM82 + DUMP #RETURN;RETLEN + _WAIT +* +*``````````````````````````````* +* 8-BIT DIVISION * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THIS MACRO IS USED LIKE ALL OTHER 8-BIT +** MACROS, BUT THE ORDER IS IMPORTANT HERE: +** THE FIRST VALUE PASSED IS THE DIVIDEND +** WHILE THE SECOND VALUE US THE DIVISOR. IT +** MAY HELP TO THINK OF THE SEMI-COLON HERE +** AS STANDING FOR "BY" SO THAT THE STATEMENT +** CAN BE READ AS "DIVIDE DIVIDEND BY DIVISOR." +* +** 8-BIT DIVISION IS LIMITED TO ARGUMENTS THAT +** ARE EITHER A DIRECT ADDRESS OR A LITERAL VALUE, +** LIKE OTHER 8-BIT MATH ROUTINES. +* + JSR ]HOME2 + _PRN "8-BIT DIVISION",8D + _PRN "==============",8D8D + _PRN "20 / 10 = ",8D + DIV8 NUM82;NUM81 + DUMP #RETURN;RETLEN + _WAIT +* +*``````````````````````````````* +* 16-BIT BASIC MATH MACROS * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** BOTH ADDITION AND SUBTRACTION OF 16-BIT +** NUMBERS IS ALSO RATHER TRIVIAL, BUT CAN +** BE CONFUSING TO SOMEONE NEW TO 6502 +** ASSEMBLY (ESPECIALLY WITHOUT A FULL +** UNDERSTANDING OF HOW ADC WORKS). EVEN STILL, +** IT CAN BECOME A BIT TEDIOUS TO CONSTANTLY +** WRITE OUT THE ROUTINES MANUALLY, AS THEY DO +** CONSTITUTE A SMALL NUMBER OF BYTES THAT CAN +** GET TIRESOME ON REPEAT. +* +*``````````````````````````````* +* 16-BIT ADDITION * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** USING THE 16-BIT ADDITION MACRO IS SIMILAR TO ITS +** 8-BIT COUSIN: TWO ADDRESSES ARE PROVIDED THAT HOLD +** THE VALUES TO BE ADDED. THE TWO-BYTE SUM IS THEN +** PASSED BACK VIA RETURN (WITH RETLEN HOLDING ITS +** LENGTH, 2) AS WELL AS IN .A (LOW BYTE) AND .X (HIGH). +* +** LIKE 8-BIT ADDITION, 16-BIT ADDITION ALSO HAS EXTRA +** BYTES AND CYCLES THAT CAN BE EASILY DISCARDED EITHER +** BY ALTERING THE MACRO AND RELATED SUBROUTINE ITSELF OR +** BY SIMPLY DOING THE ADDITION MANUALLY. +* + JSR ]HOME2 + _PRN "16-BIT ADDITION",8D + _PRN "===============",8D8D + ADD16 NUM161;NUM162 + _PRN "300 + 400 =",8D + DUMP #RETURN;RETLEN + _WAIT +* +*``````````````````````````````* +* 16-BIT SUBTRACTION * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** 16-BIT SUBTRACTION FOLLOWS THE SAME SETUP +** AND RULES AS 16-BIT ADDITION: +* + JSR ]HOME2 + _PRN "16-BIT SUBTRACTION",8D + _PRN "==================",8D8D + _PRN "400 - 300 = ",8D + SUB16 NUM162;NUM161 + DUMP #RETURN;RETLEN + _WAIT +* +*``````````````````````````````* +* 16-BIT MULTIPLICATION * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** BY NOW, THE METHOD SHOULD BE OBVIOUS: MUL16 +** TAKES TWO ARGUMENTS WHICH ARE ADDRESSES THAT +** HOLD THE TWO VALUES TO BE MULTIPLIED. +* + JSR ]HOME2 + _PRN "16-BIT MULTIPLICATION",8D + _PRN "=====================",8D8D + _PRN "300 * 400 = ",8D + MUL16 NUM161;NUM162 + DUMP #RETURN;RETLEN + _WAIT +* +*``````````````````````````````* +* 16-BIT DIVISION * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** 16-BIT DIVISION WORKS LIKE 8-BIT DIVISION, +** WITH THE RESULT STORED IN RETURN (2 BYTES) +** AS WELL AS IN .A (LOW BYTE) AND .X (HIGH). +** ADDITIONALLY, THE REMAINDER IS HELD IN THE +** .Y REGISTER--A MODULUS FUNCTION CAN SIMPLY +** CALL THIS DIVISION MACRO AND THEN READ THE +** VALUE OF .Y FOR THE ANSWER. +* + JSR ]HOME2 + _PRN "16-BIT DIVISION",8D + _PRN "===============",8D8D + _PRN "400 / 300 = ",8D + DIV16 NUM162;NUM161 + DUMP #RETURN;RETLEN + _WAIT +* +*``````````````````````````````* +* 16-BIT COMPARISON * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** LASTLY, A 16-BIT COMPARISON MACRO IS PROVIDED +** THAT MIMICS THE FUNCTIONALITY OF THE CMP INSTRUCTION, +** BUT OF COURSE FOR 16-BIT VALUES. THE ADDRESSES +** OF TWO SEPARATE VALUES ARE PROVIDED, AND THEN THE +** COMPARISON IS MADE WITH THE FOLLOWING RESULTS: +* +** Z FLAG = 1 IF BOTH VALUES ARE EQUAL +** C FLAG = 0 IF FIRST IS GREATER THAN SECOND +** C FLAG = 1 IF FIRST IS LESS THAN OR EQUAL TO SECOND +** N FLAG = 1 IF A SIGNED FIRST IS GREATER THAN A SIGNED SECOND +** N FLAG = 0 IF SIGNED FIRST IS LESS THAN OR EQUAL TO +** THE SIGNED SECOND PARAMETER +* + JSR ]HOME2 + _PRN "16-BIT COMPARISON",8D + _PRN "=================",8D8D + _PRN "CMP16 #300;#400",8D8D + CMP16 NUM161;NUM162 + BEQ :GREATER + BNE :LESSOREQ +:GREATER + _PRN "FIRST IS GREATER THAN SECOND.",8D8D + JMP :EXIT +:LESSOREQ + _PRN "FIRST IS <= THE SECOND PARAMETER.",8D8D +:EXIT + _WAIT +* + JSR ]HOME2 + _PRN "DONE.",8D8D8D +* + JMP REENTRY +* +*``````````````````````````````* +* BOTTOM INCLUDES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** BOTTOM INCLUDES +* + PUT MIN.LIB.REQUIRED.ASM +* +** INDIVIDUAL SUBROUTINE INCLUDES +* +** 8-BIT MATH SUBROUTINES +* + PUT MIN.SUB.MULTU8.ASM + PUT MIN.SUB.DIVDU8.ASM +* +** 16-BIT MATH SUBROUTINES +* + PUT MIN.SUB.ADDIT16.ASM + PUT MIN.SUB.SUBT16.ASM + PUT MIN.SUB.COMP16.ASM + PUT MIN.SUB.MULTU16.ASM + PUT MIN.SUB.DIVDU16.ASM +* +NUM81 DB 10 +NUM82 DB 20 +NUM161 DW 300 +NUM162 DW 400 + +``` + + + + + +`LISTING 3.9C: The DEMO.MATHRND.ASM File Source` + +```assembly +* +*``````````````````````````````* +* DEMO.MATH * +* * +* A DEMO OF THE INTEGER MATH * +* MACROS INCLUDED AS PART OF * +* THE APPLEIIASM LIBRARY. * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 02-APR-2021 * +* ASSEMBLER: MERLIN 8 PRO * +* OS: DOS 3.3 * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** ASSEMBLER DIRECTIVES +* + CYC AVE + EXP OFF + TR ON + DSK DEMO.MATHRND + OBJ $BFE0 + ORG $6000 +* +*``````````````````````````````* +* TOP INCLUDES (HOOKS,MACROS) * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* + PUT MIN.HEAD.REQUIRED.ASM + USE MIN.MAC.REQUIRED.ASM + PUT MIN.HEAD.MATH.ASM + USE MIN.MAC.MATHRND.ASM +]HOME2 EQU $FC58 +* +*``````````````````````````````* +* PROGRAM MAIN BODY * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +*``````````````````````````````* +* PSEUDORANDOM NUMBER MACROS * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THERE ARE FIVE MACROS DEDICATED TO USING AND +** GENERATING PSEUDORANDOM NUMBERS. THE RND8 +** MACRO AND THE RND16 MACRO GENERATE EITHER 8-BIT +** OR 16-BIT RANDOM NUMBERS, RESPECTIVELY, WHILE +** THE RAND MACRO GENERATES AN 8-BIT NUMBER BETWEEN +** A LOW AND A HIGH. +* +** IN ADDITION TO THESE ARE TWO OTHER IMPORTANT MACROS: +** THE RNDMZ MACRO AND THE RNDEOR MACRO. THESE ARE BOTH +** DEDICATED TO ALTERING THE SEED AND THE EOR VALUE +** FOR GENERATING PSEUDORANDOM NUMBERS, BUT THE RNDEOR +** ALLOWS YOU TO DIRECTLY SPECIFY THE SEED AND THE EOR +** VALUE WHEREAS THE RNDMZ MACRO ALLOWS YOU TO SET THE +** SEED DIRECTLY BUT NOT THE EOR VALUE. INSTEAD, THE +** VALUE PASSED FOR THE EOR ARGUMENT IS DIVIDED BY FOUR +** TO SET IT TO A NUMBER BETWEEN 0 AND 63, AND A FINAL +** EOR VALUE IS CHOSEN FROM A TABLE OF CONSTANTS THAT +** ENSURES MAXIMUM VARIABILITY (TO THE POINT OF BEING +** "UNNATURAL" RANDOMNESS). +* +** EITHER RNDMZ OR RNDEOR SHOULD BE EXECUTED BEFORE +** RND8, RND16, OR RAND ARE USED. OTHERWISE, THE SEED +** AND EOR VALUE WILL REVERT TO DEFAULT VALUES, +** POSSIBLY BECOMING HIGHLY PREDICTABLE BY SOMEONE +** LOOKING TO EXPLOIT SUCH SYSTEMS. +* +*``````````````````````````````* +* THE RNDEOR MACRO * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** AS NOTED ABOVE, THE RNDEOR MACRO ALLOWS YOU TO SET +** THE SEED AND EOR VALUE FOR THE PSEUDORANDOM NUMBER +** GENERATOR. THE SEED IS PROVIDED FIRST, FOLLOWED BY +** THE EOR "MAGIC NUMBER." +* + JSR ]HOME2 + _PRN "THE RNDEOR MACRO",8D + _PRN "================",8D8D + RNDEOR SEED1;NUM1 + _PRN "SEED LOW AND HIGH NOW:",8D + DUMP #RNDL;#2 + _PRN " ",8D8D + _PRN "AND THE MAGIC NUMBER IS:",8D + DUMP #]MAGEOR;#2 + _WAIT +* +*``````````````````````````````* +* THE RNDMZ MACRO * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** AS DESCRIBED ABOVE, THE RNDMZ (RANDOMIZE) MACRO +** SETS THE SEED OF THE PRNG TO A GIVEN VALUE, AND +** PULLS FROM A TABLE OF PRESET VALUES THE "MAGIC +** NUMBER" THAT IS USED TO EOR THE SEED. THE NUMBER +** PROVIDED AS ARGUMENT TWO SERVES AS AN INDEX. +* + JSR ]HOME2 + _PRN "THE RNDMZ MACRO",8D + _PRN "===============",8D8D + RNDMZ SEED2;NUM2 + _PRN "THE NEW SEED IS: ",8D + DUMP #RNDL;#2 + _PRN " ",8D8D + _PRN "AND THE NEW MAGIC NUMBER IS:",8D + DUMP #]MAGEOR;#2 + _WAIT +* +*``````````````````````````````* +* THE RND8 MACRO * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE RND8 MACRO RETURNS A PSEUDORANDOM NUMBER +** BETWEEN 0 AND 255 (THUS THE 8, FOR 8-BIT). +** THIS IS PASSED BACK VIA RETURN AND .A. +* + JSR ]HOME2 + _PRN "THE RND8 MACRO",8D + _PRN "==============",8D8D + _PRN "SOME RANDOM NUMBERS:",8D8D + RND8 + DUMP #RETURN;#1 + _PRN " " + RND8 + DUMP #RETURN;#1 + _PRN " " + RND8 + DUMP #RETURN;#1 + _WAIT +* +*``````````````````````````````* +* THE RAND MACRO * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE RAND MACRO TAKES A LOW VALUE AND A +** HIGH VALUE AND PASSES BACK A RANDOM NUMBER +** BETWEEN THE TWO IN RETURN AND .A. +* + JSR ]HOME2 + _PRN "THE RAND MACRO",8D + _PRN "==============",8D8D + _PRN "SOME RANDOM NUMBERS:",8D8D + RAND #1;#30 + DUMP #RETURN;#1 + _PRN " " + RAND #1;#30 + DUMP #RETURN;#1 + _PRN " " + RAND #1;#30 + DUMP #RETURN;#1 + _WAIT +* +*``````````````````````````````* +* THE RND16 MACRO * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE RND16 MACRO WORKS THE SAME WAY AS +** THE RND8 MACRO, EXCEPT THAT IT PASSES BACK +** A 16-BIT RANDOM VALUE VIA RETURN AS WELL AS +** IN .A (LOW BYTE) AND .X (HIGH). +* + JSR ]HOME2 + _PRN "THE RND16 MACRO",8D + _PRN "==============",8D8D + _PRN "SOME RANDOM NUMBERS:",8D8D + RND16 + DUMP #RETURN;#2 + _PRN " " + RND16 + DUMP #RETURN;#2 + _PRN " " + RND16 + DUMP #RETURN;#2 + _WAIT +* + JSR ]HOME2 + _PRN "FIN!",8D8D8D +* + JMP REENTRY +* +*``````````````````````````````* +* BOTTOM INCLUDES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** BOTTOM INCLUDES +* + PUT MIN.LIB.REQUIRE +* +** INDIVIDUAL SUBROUTINE INCLUDES +* + PUT MIN.SUB.RAND8.ASM + PUT MIN.SUB.RANDB.ASM + PUT MIN.SUB.RAND16.ASM +* +SEED1 DW 1000 +NUM1 DW 666 +SEED2 DW 2000 +NUM2 DB 200 + +``` + + + diff --git a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/34.0 Detailed_Reference_D5_STRINGS.md b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/34.0 Detailed_Reference_D5_STRINGS.md new file mode 100644 index 0000000..4d57ec9 --- /dev/null +++ b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/34.0 Detailed_Reference_D5_STRINGS.md @@ -0,0 +1,3044 @@ +# 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
]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
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
]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
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+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` + +```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
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
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
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
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
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
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 `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
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
]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` + +```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
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` + +```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
]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` + +```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
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` + +```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
]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` + +```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
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` + +```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
]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` + +```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
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` + +```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
]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` + +```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 + +``` + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/35.0 Detailed_Reference_D6_DOS.md b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/35.0 Detailed_Reference_D6_DOS.md new file mode 100644 index 0000000..c185edb --- /dev/null +++ b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/35.0 Detailed_Reference_D6_DOS.md @@ -0,0 +1,4456 @@ +# Disk 6: Apple DOS + + + +- [Part I: The DOS Collection](#part-i-the-dos-collection) + - [DOS Components](#dos-components) + - [DOS Header File](#dos-collection-header-file) + - [_SETRWTS](#the-_setrwts-subroutine) + - [_FMGETPARM](#the-_fmgetparm-subroutine) + - [_FMBUFSCAN](#the-_fmbufscan-subroutine) + - [_SETPBUFFS](#the-_setpbuffs-subroutine) + - [_NAMESTR](#the-_namestr-subroutine) + - [_NAMESTR2](#the-_namestr2-subroutine) + - [_FN2BUFF](#the-_fn2buff-subroutine) + - [_FMNM2](#the-_fmnm2-subroutine) + - [_BUFFCLEAR](#the-_buffclear-subroutine) + - [_FMPSET](#the-_fmpset-subroutine) + - [_FMPSETA](#the-_fmpseta-subroutine) + - [_FMPGET](#the-_fmpget-subroutine) + - [_FMPGETA](#the-_fmpgeta-subroutine) + - [Required DOS Macros](#required-dos-macros) + - [FMFIL](#the-fmfil-macro) + - [FMNAM](#the-fmnam-macro) + - [FRWB](#the-frwb-macro) + - [FWRTB](#the-fwrtb-macro) + - [FRDB](#the-frdb-macro) + - [FRWR](#the-frwr-macro) + - [FRDR](#the-frdr-macro) + - [FWRTR](#the-fwrtr-macro) + - [DOS File Manager Macros and Subroutines](#dos-file-manager-macros) + - [FCAT](#the-fcat-macro) + - [FULCK](#the-fulck-macro) + - [FLOCK](#the-flock-macro) + - [FDEL](#the-fdel-macro) + - [FVFY](#the-fvfy-macro) + - [FCLOS](#the-fclos-macro) + - [FRENM](#the-frenm-macro) + - [FOPEN](#the-fopen-macros) + - [BLOAD](#the-bload-macro) + - [FBLOAD](#the-fbload-subroutine) + - [BSAVE](#the-bsave-macro) + - [FBSAVE](#the-fbsave-subroutine) + - [Miscellaneous DOS Macros and Subroutines](#miscellaneous-dos-macros) + - [DVER](#the-dver-macro) + - [DOSIN](#the-dosin-macro) + - [ABAS](#the-abas-macro) + - [IBEX](#the-ibex-macro) + - [ABEX](#the-abex-macro) +- [Part II: DOS Collection Demos](#part-ii-dos-collection-demos) + - File Manager Demo + - Miscellaneous DOS Macros Demo + + + + + +--- + + + +## Part I: The DOS Collection + + + +The sixth disk of the AppleIIAsm library is dedicated to DOS operations. For the most part, the macros and subroutines here act as an abstraction layer between the lower level functions of DOS and the programmer. Unfortunately, this does carry with it some substantial overhead in terms of bytes used, but the ease of functionality is usually worth it unless memory is an extreme concerned. In such a case, the DOS collection can be narrowed to only the functions that are needed. + +There are a number of macros and subroutines that are likely to go unused by most programmers, as they are mostly meant for internal use by the collection. These are covered as part of this documentation, but are not the primary focus of functionality. Those macros and subroutines that are the primary focus includes functions for the following: + +- Disk Catalog +- File Locking and Unlocking +- File Deletion +- File Verification +- File Renaming +- File Opening and Closing +- Binary File Loading and Saving +- DOS Version Checking +- Integer and Applesoft Runtime Checking +- RWTS Access + + + +--- + + + +## DOS Components + + + +The DOS Collection contains the following components: + +- A head file that includes a number of hooks, vectors and subroutines that are necessary for the operation of the whole collection. +- A macro library that is required to be included for using the rest of the collection. +- Two further sets of macro files, MAC.DOSFM.ASM and MAC.DOSMORE.ASM. The former contains macros used for managing files while the latter contains miscellaneous macros related to DOS. +- Two demo files that illustrate how all of the major macros are used. Note that this does not include macros that are meant for internal use. + + + +--- + + + +## DOS Collection Header File + + + +| Condition | Value | +| ------------- | ------------------------------------------------------------ | +| Name | File: HEAD.DOS.ASM | +| Type | Header File | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin 8 Pro | +| OS | Apple DOS 3.3 | +| Purpose | Provide appropriate hooks and routines for the DOS collection | +| Dependencies | none | +| Bytes | 584 | +| Notes | none | +| See Also | none | + +--- + +*DETAILS* + +The DOS Header file includes a number of required subroutines that are detailed later in this section. + +`LISTING 6.00: HEAD.DOS.ASM Hooks & Vectors Source` + +```assembly +* +*``````````````````````````````* +* HEAD.DOS.ASM * +* * +* THIS HEADER FILE IS USED BY * +* THE DOS COLLECTION AS A * +* REPOSITORY FOR VARIABLE * +* SPACE, HOOKS, SUBROUTINES * +* NECESSARY FOR THE REST OF * +* THE COLLECTION AND OTHER * +* MISCELLANEOUS TASKS. * +* * +* CURRENTLY, THIS COLLECTION * +* HAS QUITE A BIT OF OVERHEAD * +* IN TERMS OF BYTES. THIS WILL * +* HOPEFULLY BE REDUCED IN * +* FUTURE REVISIONS. * +* * +* NOTE THAT THE METHOD USED * +* FOR FINDING AN EMPTY FILE * +* BUFFER IS ADAPTED FROM A * +* LISTING IN DON WORTH'S AND * +* PETER LECHNER'S /BENEATH * +* APPLE DOS AND BENEAT APPLE * +* PRODOS 2020/. AS SUCH, THE * +* LICENSE MAY VARY. * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 07-MAY-2021 * +* ASSEMBLER: MERLIN PRO 8 * +* OS: DOS 3.3 * +* * +* SIZE: 584 BYTES (OVERHEAD) * +* * +* SUBROUTINES: * +* * +* _SETRWTS * +* _FMGETPARM * +* _FMBUFSCAN * +* _SETPBUFFS * +* _NAMESTR * +* _NAMESTR2 * +* _FN2BUFF * +* _FMNM2 * +* _BUFFCLEAR * +* _FMPSET * +* _FMPSETA * +* _FMPGET * +* _FMPGETA * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +*``````````````````````````````* +* RWTS EQUATES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]RWTSPTR EQU ADDR4 ; WORK POINTER FOR RWTS ROUTINES +]RWTSRD EQU 1 ; READ CODE FOR RWTS +]RWTSWR EQU 1 ; WRITE CODE FOR RWTS +]LOCRPL EQU $3E3 ; LOCATE RWTS PARAMLIST +]RWTS EQU $3D9 ; RWTS ROUTINE +* +]RPLIOB EQU $0 ; IOB TYPE, ALWAYS $01 +]RPLSLT EQU $1 ; IOB SLOT * 16 +]RPLDRV EQU $2 ; IOB DRIVE +]RPLVOL EQU $3 ; IOB VOLUME (#0 FOR ANY) +]RPLTRK EQU $4 ; IOB TRACK +]RPLSEC EQU $5 ; IOB SECTOR +]RPLDCT EQU $6 ; IOB ADDR TO DCT ($6,$7) +]RPLBUF EQU $8 ; IOB ADDR TO BUFFER ($8,$9) +]RPLSIZ EQU $A ; IOB SIZE ($A,$B) +]RPLCMD EQU $C ; IOB COMMAND +]RPLCNL EQU $00 ; NULL COMMAND +]RPLCRD EQU $01 ; READ COMMAND +]RPLCWR EQU $02 ; WRITE COMMAND +]RPLCFM EQU $04 ; FORMAT COMMAND +]RPLRCD EQU $D ; IOB RETURN CODE +]RPLRWP EQU $10 ; WRITED PROTECTED ERROR +]RPLRVM EQU $20 ; VOLUME MISMATCH ERROR +]RPLRDE EQU $40 ; DRIVE ERROR +]RPLRRE EQU $80 ; READ ERROR +]RPLTVL EQU $E ; IOB TRUE VOLUME +]RPLPSL EQU $F ; IOB PREVIOUS SLOT +]RPLPDR EQU $10 ; IOB PREVIOUS DRIVE +* +*``````````````````````````````* +* FILE PARAMETER LIST EQUATES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]FMPADDR EQU $3DC ; FILE MGR PARAM ADDR GET +]FMRUN EQU $3D6 ; RUN FILE MAMAGER ROUTINE +]DOSWARM EQU $3D0 ; DOS WARM START +* +]TYP_TXT EQU $00 ; FILE TYPE TEXT VALUE +]TYP_INT EQU $01 ; FILE TYPE INT VALUE +]TYP_APP EQU $02 ; FILE TYPE APPLESOFT VALUE +]TYP_BIN EQU $04 ; FILE TYPE BINARY VALUE +]TYP_REL EQU $08 ; FILE TYPE REL VALUE +]TYP_S EQU $10 ; FILE TYPE S VALUE +]TYP_A EQU $20 ; FILE TYPE A VALUE +]TYP_B EQU $40 ; FILE TYPE B VALUE +* +]SC_NON EQU $00 ; SUBCODE NOTHING VALUE +]SC_RWB EQU $01 ; SUBCODE READ OR WRITE A BYTE VALUE +]SC_RWR EQU $02 ; SUBCODE READ OR WRITE RANGE VALUE +]SC_PRWB EQU $03 ; SUBCODE POSITION THEN RW 1 BYTE VAL +]SC_PRWR EQU $04 ; SUBCODE POSITION THEN RW RANGE VAL +* +]FC_OPEN EQU $01 ; COMMAND CODE FOR OPEN +]FC_CLOS EQU $02 ; COMMAND CODE FOR CLOSE +]FC_READ EQU $03 ; COMMAND CODE FOR READ +]FC_WRIT EQU $04 ; COMMAND CODE FOR WRITE +]FC_DEL EQU $05 ; COMMAND CODE FOR DELETE +]FC_CAT EQU $06 ; COMMAND CODE FOR CATALOG +]FC_LOCK EQU $07 ; COMMAND CODE FOR LOCK +]FC_UNLK EQU $08 ; COMMAND CODE FOR UNLOCK +]FC_REN EQU $09 ; COMMAND CODE FOR RENAME +]FC_POS EQU $0A ; COMMAND CODE FOR POSITION +]FC_INIT EQU $0B ; COMMAND CODE FOR INIT +]FC_VFY EQU $0C ; COMMAND CODE FOR VERIFY +* +]P_CMD EQU $00 ; PARAM COMMAND OFFSET +]P_REC EQU $02 ; PARAM RECORD OFFSET +]P_VOL EQU $04 ; PARAM VOLUME OFFSET +]P_DRV EQU $05 ; PARAM DRIVE OFFSET +]P_SLOT EQU $06 ; PARAM SLOT OFFSET +]P_TYPE EQU $07 ; PARAM TYPE OFFSET +]P_NAMAD EQU $08 ; PARAM NAME BUFFER ADDRESS OFFSET +]P_WORK EQU $0C ; PARAM WORKAREA BUFFER ADDRESS OFFSET +]P_TSLS EQU $0E ; PARAM TSLS BUFFER ADDRESS OFFSET +]P_DATA EQU $10 ; PARAM DATA BUFFER ADDRESS OFFSET +]P_RETC EQU $0A ; PARAM RETURN CODE OFFSET +]P_RDWR EQU $00 ; READ OR WRITE OFFSET +]P_SUBC EQU $01 ; PARAM SUBCODE OFFSET +]P_BOFF EQU $04 ; PARAM BYTE OFFSET OFFSET +]P_RLEN EQU $06 ; PARAM RANGE LENGTH OFFSET +]P_RADDR EQU $08 ; PARAM RANGE ADDRESS OFFSET +]P_BYTE EQU $08 ; PARAM SINGLE WRITE BYTE OFFSET +]P_DPAGE EQU $01 ; PARAM 1ST DOS PAGE ADDR OFFSET +* + JMP __DHEXIT ; {6C3B} PASS OVER ROUTINES +* +MSLOT DFB $60 ; {0C1B} MASTER SLOT +MDRIVE DFB $2 ; {0C1B} MASTER DRIVE +MTRACK DFB $11 ; {0C1B} MASTER TRACK FOR RWTS +MSECTOR DFB $0 ; {0C1B} MASTER SECTOR FOR RWTS +MBUFFER DFB $9000 ; {0C1B} MASTER BUFFER ADDRESS FOR RWTS + DFB $9000/$100 ; {0C1B} +MCMD DFB $1 ; {0C1B} MASTER COMMAND FOR RWTS +MVOL DFB $0 ; {0C1B} MASTER VOLUME FOR RWTS +* +]TEMP HEX 0000 ; {0C2B} +]TEMP2 HEX 0000 ; {0C2B} +]FPARM HEX 0000 ; {0C2B} FILE MGR PARAM ADDRESS +]FNBUF HEX 0000 ; {0C2B} FILE NAME BUFFER +]FWORK HEX 0000 ; {0C2B} FILE WORKAREA BUFFER +]FDATA HEX 0000 ; {0C2B} FILE DATA BUFFER +]FTSLS HEX 0000 ; {0C2B} FILE TRACK/SECTOR LIST BUFFER +]FNAME DS 30 ; {0C30B} FILENAME TEMP STORAGE +]FNAME2 DS 30 ; {0C30B} SECOND FILENAME BUFFER + +``` + + + +--- + + + +### THE _SETRWTS SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | -------------------------- | +| Name | `_SETRWTS` | +| Type | Subroutine | +| File | `HEAD.DOS.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Set common RWTS parameters | +| Input | none | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 142+ | +| Bytes | 78 | +| Notes | none | +| See Also | none | + +--- + +*DETAILS* + +The `_SETRWTS` routine sets the parameters for the **Input/Output Control Block** **(IOB)** before calling the **Read/Write Track/Sector** **(RWTS)** Routine. For the most part, this is used internally, and is not meant to be used by the end programmer. The following parameters are set in the **IOB** via the values given in global variables, which are set via another routine: + +- Slot +- Drive +- Volume +- Track +- Sector +- Buffer +- Command + +While the DOS collection makes knowing the format of the **Input/Output Control Block** unnecessary, there may be cases where one needs to access DOS at a lower level. Thus, the format of the **IOB** is listed here: + + + +**INPUT / OUTPUT CONTROL BLOCK** + +| Byte | Description | +|------------------|------------------------| +|00| Table type, must always be #00 | +|01| Slot number * 16 ($10, $20, $30, $40, $50, $60, $70) | +|02| Drive number | +|03| Volume number (#00 for any) | +|04| Track number | +|05| Sector number | +|06-07| Address of the Device Characteristics Table (DCT) | +|08-09| Address of the 256 byte buffer for READ/WRITE | +|0A| Unused | +|0B| Byte count for partial sector | +|0C| Command Code:
#$00 = SEEK
#$01 = READ
#$02 = WRITE
#$04 = FORMAT | +|0D| Return Code. The CARRY flag is set upon returning from RWTS to indicate that an error has occurred. The following values are passed here to communicate the type of error:
#$00 = No error
#$08 = Initialization error
#$10 = Write protect error
#$20 = Volume mismatch
#$40 = Drive error
#$80 = Read error | +|0E| Volume number last accessed | +|0F| Slot number last accessed * 16 | +|10| Drive number last accessed | + + + +While the **Device Characteristics Table (DCT)** is almost always the same and should remain unchanged (for a Disk II drive), its format is also listed here for reference: + + + +**DEVICE CHARACTERISTICS TABLE** + +| Byte | Description | +|--------|-------------------| +|00| Device Type (should be #$00) | +|01| Phases per track (should be #$01) | +|02-03| Motor on time count (should be #$EFD8) | + + + +`LISTING 6.01: HEAD.DOS.ASM _SETRWTS Routine Source` + +```assembly +* +*``````````````````````````````* +* _SETRWTS * +* * +* SET THE RWTS IOB PARAMETERS * +* * +* INPUT * +* * +* NONE. * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 142+ * +* SIZE: 78 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +_SETRWTS + JSR ]LOCRPL ; {6C3B} LOCATE RWTS PARAMETER LIST + STY ]RWTSPTR ; {4C3B} SAVE POINTER LOW AND HIGH + STA ]RWTSPTR+1 ; {4C3B} + LDA MSLOT ; {4C3B} GET DEFAULT SLOT + ASL ; {2C1B} MULTIPLY BY 16 + ASL ; {2C1B} TO GET APPROPRIATE VALUE + ASL ; {2C1B} + ASL ; {2C1B} + LDY #]RPLSLT ; {3C2B} LOAD IOB SLOT OFFSET + STA (]RWTSPTR),Y ; {6C2B} STORE SLOT VAL AT OFFSET + LDA MDRIVE ; {4C3B} LOAD DEFAULT DRIVE + LDY #]RPLDRV ; {3C2B} GET IOB DRIVE OFFSET + STA (]RWTSPTR),Y ; {6C2B} STORE DRIVE IN IOB + LDA MTRACK ; {4C3B} GET MASTER TRACK + LDY #]RPLTRK ; {3C2B} OFFSET IN PARAM LIST + STA (]RWTSPTR),Y ; {6C2B} STORE TRACK IN IOB + LDA MSECTOR ; {4C3B} GET MASTER SECTOR + CMP #16 ; {3C2B} BIGGER THAN 16? + BCC :SOK ; {3C2B} NOPE, IT'S OKAY + LDA #0 ; {3C2B} ELSE YES, SO CLEAR + STA MSECTOR ; {4C3B} MASTER SECTOR +:SOK LDY #]RPLSEC ; {3C2B} OFFSET IN PARMLIST + STA (]RWTSPTR),Y ; {6C2B} STORE SECTOR IN IOB + LDY #]RPLBUF ; {3C2B} OFFSET IN PARAMLIST + LDA MBUFFER ; {4C3B} GET LOW BYTE OF BUFFER ADDR + STA (]RWTSPTR),Y ; {6C2B} STORE LOW BYTE OF BUFF IN IOB + INY ; {2C1B} INCREASE OFFSET + LDA MBUFFER+1 ; {4C3B} GET HIGH BYTE + STA (]RWTSPTR),Y ; {6C2B} STORE HIGH BYTE IN IOB + LDA MCMD ; {4C3B} GET RWTS MASTER COMMAND + LDY #]RPLCMD ; {3C2B} OFFSET OF PARMLIST + STA (]RWTSPTR),Y ; {6C2B} STORE COMMAND IN IOB + LDA MVOL ; {4C3B} GET RWTS MASTER VOLUME + LDY #]RPLVOL ; {3C2B} OFFSET + STA (]RWTSPTR),Y ; {6C2B} STORE VOLUME IN IOB + RTS ; {6C1B} +``` + + + +--- + + + +### THE _FMGETPARM SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------- | +| Name | `_FMGETPARM` | +| Type | Subroutine | +| File | `HEAD.DOS.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Get the FPL Address | +| Input | none | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 20+ | +| Bytes | 10 | +| Notes | none | +| See Also | `_SETPBUFFS` | + +--- + +*DETAILS* + +The `_FMGETPARM` subroutine gets the address of the **File Manager Parameter List (FPL)** and returns it in **.Y** (low byte) and **.A** (high byte), with the addition of storing it in the ]FPARM variable space. This is different from the usual passing of addresses in **.A** and **.X** due to the nature of how DOS was programmed; since this is used internally, however, this should not present a problem. + +If the format of the **File Manager Parameter List (FPL)** is needed, it is available under the `SETPBUFFS` entry. + + + +`LISTING 6.02: HEAD.DOS.ASM _FMGETPARM Routine Source` + +```assembly +* +*``````````````````````````````* +* _FMGETPARM (NATHAN RIGGS) * +* * +* GET THE ADDRESS OF THE FILE * +* PARAMETER LIST. * +* * +* INPUT * +* * +* NONE. * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 20+ * +* SIZE: 10 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +_FMGETPARM +* + JSR ]FMPADDR ; {6C3B} GET PARAM ADDRESS + STA ]FPARM+1 ; {4C3B} STORE HIGH BYTE + STY ]FPARM ; {4C3B} STORE LOW BYTE + RTS ; {6C1B} +* + +``` + + + +--- + + + +### THE _FMBUFSCAN SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ----------------------- | +| Name | `_FMBUFSCAN` | +| Type | Subroutine | +| File | `HEAD.DOS.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Find a free file buffer | +| Input | none | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 211+ | +| Bytes | 117 | +| Notes | none | +| See Also | `_BUFFCLEAR` | + +--- + +*DETAILS* + +The `_FMBUFSCAN` subroutine finds an open file buffer and stores the addresses to various necessary sections in the appropriate memory locations. Primarily, this includes the **File Workarea**, the **Filename Buffer**, the **Data Sector Buffer**, and the **T/S List Sector (TSLS) Buffer**. If no file buffers are available then the carry is set to indicate an error, and the memory locations are all cleared to zero. A file buffer is considered clear if the first byte of the filename buffer carries a value of **#0**. + +While the DOS collection makes it unnecessary to know the inner workings of DOS, it may be useful for the programmer to know the format of each file buffer. The format is as follows: + + + +|BYTE | DESCRIPTION| +|--------------|--------------------| +|000-0FF | Data Sector Buffer (256 bytes)| +|100-1FF | T/S List Sector Buffer (256 bytes)| +|200-22C | File Manager Workarea Buffer (45 bytes)| +|22D-24A | File Name Buffer (30 bytes)| +|24B-24C | Address of File Manager Work Area Buffer| +|24D-24E | Address of T/S List Sector Buffer| +|24F-250 | Address of Data Sector Buffer| +|251-252 | Address of the File Name Buffer in the next File Buffer. If this is the last buffer available, then the value is #0000.| + + + +`LISTING 6.03: HEAD.DOS.ASM _FMBUFSCAN Routine Source` + +```assembly +* +*``````````````````````````````* +* _FMBUFSCAN (NATHAN RIGGS) * +* * +* FIND A FREE FILE BUFFER. IF * +* NONE EXISTS, RETURN #0. * +* * +* INPUT * +* * +* NONE. * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 211+ * +* SIZE: 117 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +_FMBUFFSCAN +* + LDA $3D2 ; {4C3B} LOCATE DOS LOAD POINT + STA ADDR1+1 ; {3C2B} STORE THIS AS FIRST FNAME BUFF + LDY #0 ; {3C2B} LOW BYTE SHOULD START AT 0 + STY ADDR1 ; {3C2B} +:GBUF0 + LDA (ADDR1),Y ; {6C2B} LOCATE NEXT DOS BUFFER + PHA ; {3C1B} TEMPORARILY HOLD + INY ; {2C1B} INCREASE .Y INDEX + LDA (ADDR1),Y ; {6C2B} LOAD HIGH BYTE OF ADDRESS + STA ADDR1+1 ; {3C2B} STORE HIGH BYTE IN ZERO PAGE + PLA ; {3C1B} PULL BACK EARLIER VALUE + STA ADDR1 ; {3C2B} STORE AS LOW BYTE ON ZERO + BNE :GBUF ; {4C3B} FOUND A BUFFER + LDA ADDR1+1 ; {3C2B} ELSE LOAD HIGH BYTE + BEQ :NBUF ; {4C3B} IF ZERO, NO BUFFER +:GBUF + LDY #0 ; {3C2B} GET FILENAME FIRST CHAR + LDA (ADDR1),Y ; {6C2B} FROM THE BUFFER + BEQ :GOTBUF ; {3C2B} IF 0, THEN BUFFER IS UNUSED + LDY #36 ; {3C2B} IF NOT, SKIP TO NEXT LINK + BNE :GBUF0 ; {3C2B} AND RERUN THE SEARCH LOOP +:GOTBUF + LDA ADDR1 ; {3C2B} LOAD FNAME BUFFER LOW BYTE + STA ]FNBUF ; {4C3B} STORE FILENAME BUFFER ADDRESS + LDA ADDR1+1 ; {3C2B} READ THE HIGH BYTE + STA ]FNBUF+1 ; {4C3B} AND STORE AS POINTER ADDRESS + LDY #30 ; {3C2B} INDEX TO FILE MANAGER WORKAREA + LDA (ADDR1),Y ; {6C2B} FOR CONVENIENT STORAGE + STA ]FWORK ; {4C3B} STORE WORKAREA POINTER + INY ; {2C1B} INCREASE .Y INDEX + LDA (ADDR1),Y ; {6C2B} LOAD THE HIGH BYTE + STA ]FWORK+1 ; {4C3B} AND HOLD IT IN ]FWORK + INY ; {2C1B} POINTER TO T/S LIST SECTOR BUFF + LDA (ADDR1),Y ; {6C2B} LOAD LOW BYTE OF TS BUFFER + STA ]FTSLS ; {4C3B} HOLD POINTER IN MEMORY + INY ; {2C1B} INCREASE .Y INDEX + LDA (ADDR1),Y ; {6C2B} LOAD HIGH BYTE + STA ]FTSLS+1 ; {4C3B} AND HOLD + INY ; {6C2B} INC POINTER TO DATA BUFFER + LDA (ADDR1),Y ; {6C2B} LOAD LOW BYTE OF ADDRESS + STA ]FDATA ; {4C3B} STORE IN MEMORY + INY ; {2C1B} INCREASE .Y INDEX + LDA (ADDR1),Y ; {6C2B} LOAD THE HIGH BYTE + STA ]FDATA+1 ; {4C3B} AND STORE AS WELL + CLC ; {2C1B} CLR CARRY TO INDICATE NO ERR + RTS ; {6C1B} +:NBUF + LDA #0 ; {4C3B} LOAD #0 AND CLEAR OUT + STA ]FNBUF ; {4C3B} ALL OF THE ADDRESS POINTERS + STA ]FNBUF+1 ; {4C3B} + STA ]FWORK ; {4C3B} + STA ]FWORK+1 ; {4C3B} + STA ]FTSLS ; {4C3B} + STA ]FTSLS+1 ; {4C3B} + STA ]FDATA ; {4C3B} + STA ]FDATA+1 ; {4C3B} + SEC ; {2C1B} SET CARRY TO INDICATE ERR + RTS ; {6C1B} +* + +``` + + + +--- + + + +### THE _SETPBUFFS SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | -------------------------------------------------------- | +| Name | `_SETPBUFFS` | +| Type | Subroutine | +| File | `HEAD.DOS.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Set the buffers in the File Manager Parameter List (FPL) | +| Input | none | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 119+ | +| Bytes | 64 | +| Notes | none | +| See Also | `_FMBUFSCAN` | + +--- + +*DETAILS* + +The `_SETPBUFFS` subroutine sets the buffer addresses in the **File Manager Parameter List (FPL)** to the appropriate addresses in an open file buffer, as set by `_FMBUFSCAN`. The buffers set include the **Filename Buffer**, the **TS/LS Buffer**, **Data Sector Buffer** and the **File Workarea Buffer**. + +The format of the **FPL** slightly changes depending on the command being called, and the number of minor differences are too many to list here for any useful purposes. For a detailed list of how the **FPL** is formatted according to call type, see Don Worth's and Pieter Lechner's *Beneath Apple DOS*. However, there is a general format to the **FPL**, as follows: + + + +**File Manager Parameter List (FPL)** + +| Byte | Description | +|--------|-------------------| +|00| Command/Call Type:
$01 = OPEN
$02 = CLOSE
$03 = READ
$04 = WRITE
$05 = DELETE
$06 = CATALOG
$07 = LOCK
$08 = UNLOCK
$09 = RENAME
$0A = POSITION
$0B = INIT
$0C = VERIFY | +|01| Sub-command/Sub-Call Type for READ or WRITE
$00 = No operation
$01 = READ or WRITE a byte
$02 = READ or WRITE a range of bytes
$03 = POSITION then READ or WRITE a byte
$04 = POSITION then READ or WRITE a range of bytes | +|02-09| Parameters change based on call type. | +|0A| Return Code. Upon returning from the file manager, the CARRY flag is set to indicate a non-zero return code is present. The number here can then be accessed to further understand the return status.
$00 = No errors
$01 = Language Not Available
$02 = Bad call type
$03 = Bad sub-call type
$04 = Write protected
$05 = End of Data
$06 = File not found
$07 = Volume mismatch
$08 = Disk I/O error
$09 = Disk full
$0A = File locked | +|0B| Unused | +|0C-0D| Address of File Manager Workarea Buffer | +|0E-0F| Address of the TS/LS Buffer | +|10-11| Address of the Data Sector Buffer | + + + +`LISTING 6.04: HEAD.DOS.ASM _SETPBUFFS Routine Source` + +```assembly +* +*``````````````````````````````* +* _SETPBUFFS (NATHAN RIGGS) * +* * +* SET THE FILENAME BUFFER, * +* TS/LS BUFFER, DATA BUFFER * +* AND THE FILE WORKAREA * +* BUFFER. * +* * +* INPUT * +* * +* NONE. * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 119+ * +* SIZE: 64 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +_SETPBUFFS +* + LDA ]FPARM ; {4C3B} GET PARAMATER ADDRESS LOW BYTE + STA ADDR1 ; {3C2B} AND HOLD IN ZERO PAGE + LDA ]FPARM+1 ; {4C3B} LOAD HIGH BYTE + STA ADDR1+1 ; {3C2B} INTO ZERO PAGE TOO + LDY #$10 ; {3C2B} POINT TO DATA BUFFER POINTER + LDA ]FDATA ; {4C3B} LOAD DATA BUFFER ADDRESS LOW + STA (ADDR1),Y ; {6C2B} COPY POINTER TO PARAM LOCATION + INY ; {2C1B} INCREASE .Y INDEX + LDA ]FDATA+1 ; {4C3B} LOAD DATA BUFFER ADDR HIGH BYTE + STA (ADDR1),Y ; {6C2B} COPY TO PARAM LOCATION + LDY #$08 ; {3C2B} POINT TO FILENAME BUFFER + LDA ]FNBUF ; {4C3B} LOAD FILENAME BUFFER POINTER ADDR + STA (ADDR1),Y ; {6C2B} AND COPY TO PARAM LOCATION + INY ; {2C1B} INCREASE .Y POINTER + LDA ]FNBUF+1 ; {4C3B} LOAD THE HIGH BYTE + STA (ADDR1),Y ; {6C2B} AND COPY AS WELL + LDY #$0E ; {3C2B} T/S LIST SECTOR BUFFER POINTER + LDA ]FTSLS ; {4C3B} LOAD CURRENT POINTER LOW BYTE + STA (ADDR1),Y ; {6C2B} AND COPY TO PARAM LOCATION + INY ; {2C1B} INCREASE .Y INDEX + LDA ]FTSLS+1 ; {3C2B} LOAD THE HIGH BYTE + STA (ADDR1),Y ; {6C2B} AND COPY TO PARAM LOCATION + LDY #$0C ; {3C2B} WORKAREA BUFFER OFFSET IN PARAM + LDA ]FWORK ; {4C3B} LOAD CURRENT WORKAREA POINTER + STA (ADDR1),Y ; {6C2B} COPY TO PARAMETER LIST + INY ; {2C1B} INCREASE .Y INDEX + LDA ]FWORK+1 ; {4C3B} LOAD HIGH BYTE + STA (ADDR1),Y ; {6C2B} AND COPY AS WELL +* + RTS ; {6C3B} +* +``` + + + +--- + + + +### THE _NAMESTR SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `_NAMESTR` | +| Type | Subroutine | +| File | `HEAD.DOS.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Copy filename from standard string format to the 30-byte string expected by DOS | +| Input | none | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 64+ | +| Bytes | 42 | +| Notes | none | +| See Also | `_NAMESTR2` `_FN2BUFF` `_FMNM2` | + +--- + +*DETAILS* + +The `_NAMESTR` subroutine is an internal procedure for transferring a standard preceding length-byte string to a temporary fixed 30-byte memory address that fills the empty bytes with spaces (this is determined by subtracting the string length from 30, which equals the number of trailing spaces needed). This subroutine should be called prior to calling the File Manager, given that the filename is not the same as the previous call. + + + +`LISTING 6.05: HEAD.DOS.ASM _NAMESTR Routine Source` + +```assembly +* +*``````````````````````````````* +* _NAMESTR (NATHAN RIGGS) * +* * +* COPY FILENAME FROM STANDARD * +* STRING TO THE 30-BYTE STRING * +* EXPECTED BY DOS IN A TEMP * +* MEMORY AREA. * +* * +* INPUT * +* * +* .A = FILENAME STR ADDR LOW * +* .X = HIGH BYTE * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 64+ * +* SIZE: 42 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +_NAMESTR +* + LDY #0 ; {3C2B} + STA ADDR1 ; {3C2B} CLEAR OUT ZERO LOCATION + STX ADDR1+1 ; {3C2B} + LDA (ADDR1),Y ; {6C2B} GET STRING LENGTH + STA BPAR1 ; {3C2B} HOLD TEMP IN ZERO PAGE + LDY #0 ; {3C2B} RESET .Y INDEX + LDX #255 ; {3C2B} SET .X INDEX TO -1 +:_NSLP1 + INX ; {2C1B} INCREASE .X INDEX + INY ; {2C1B} INCREASE .Y INDEX + LDA (ADDR1),Y ; {6C2B} GET CHARACTER + STA ]FNAME,X ; {5C3B} STORE AT Y-1 LOC IN HOLD AREA + CPX #29 ; {3C2B} COMPARE TO MAX STRING LENGTH + BEQ :_NSEXIT ; {3C2B} IF MAX IS REACHED, EXIT + CPY BPAR1 ; {3C2B} OTHERWISE COMPARE TO STRING LENGTH + BNE :_NSLP1 ; {3C2B} IF NOT EQUAL, RELOOP TO NEXT CHAR +:_NSLP2 + INX ; {2C1B} STILL INCREASE .X INDEX + LDA #" " ; {4C3B} NEXT CHAR IS SPACE + STA ]FNAME,X ; {5C3B} STORE IN NAME AREA AT .X + CPX #29 ; {3C2B} CHECK NEW STRING LENGTH + BNE :_NSLP2 ; {3C2B} IF < 30, KEEP LOOPING +:_NSEXIT + RTS ; {6C1B} +* +``` + + + +--- + + + +### THE _NAMESTR2 SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `_NAMESTR2` | +| Type | Subroutine | +| File | `HEAD.DOS.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Copy second filename from standard string
format to the 30-byte string expected by DOS | +| Input | none | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 64+ | +| Bytes | 42 | +| Notes | none | +| See Also | `_NAMESTR` `_FN2BUFF` `_FMNM2` | + +--- + +*DETAILS* + +The `_NAMESTR2` subroutine accomplishes the same task as `_NAMESTR`, but does so for a second filename that is needed for procedures like renaming a file. This subroutine will likely be merged with the `_NAMESTR` subroutine in future revisions. + + + +`LISTING 6.06: HEAD.DOS.ASM _NAMESTR2 Routine Source` + +```assembly +* +*``````````````````````````````* +* _NAMESTR2 (NATHAN RIGGS) * +* * +* FULFILLS THE SAME FUNCTION * +* AS _NAMESTR, EXCEPT FOR A * +* SECOND STRING USED BY CMDS * +* LIKE RENAME. * +* * +* .A = STRING ADDR LOW BYTE * +* .X = STRING ADDR HIGH BYTE * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 64+ * +* SIZE: 42 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +_NAMESTR2 +* + LDY #0 ; {3C2B} + STA ADDR1 ; {3C2B} CLEAR OUT ZERO LOCATION + STX ADDR1+1 ; {3C2B} + LDA (ADDR1),Y ; {6C3B} GET STRING LENGTH + STA BPAR1 ; {3C2B} HOLD TEMP IN ZERO PAGE + LDY #0 ; {3C2B} RESET .Y INDEX + LDX #255 ; {3C2B} SET .X INDEX TO -1 +:_NSLP1 + INX ; {2C1B} INCREASE .X INDEX + INY ; {2C1B} INCREASE .Y INDEX + LDA (ADDR1),Y ; {6C2B} GET CHARACTER + STA ]FNAME2,X ; {5C2B} STORE AT Y-1 LOC IN HOLD AREA + CPX #29 ; {3C2B} COMPARE TO MAX STRING LENGTH + BEQ :_NSEXIT ; {3C2B} IF MAX IS REACHED, EXIT + CPY BPAR1 ; {3C2B} OTHERWISE COMPARE TO STRING LENGTH + BNE :_NSLP1 ; {3C2B} IF NOT EQUAL, RELOOP TO NEXT CHAR +:_NSLP2 + INX ; {2C1B} STILL INCREASE .X INDEX + LDA #" " ; {4C3B} NEXT CHAR IS SPACE + STA ]FNAME2,X ; {5C2B} STORE IN NAME AREA AT .X + CPX #29 ; {2C2B} CHECK NEW STRING LENGTH + BNE :_NSLP2 ; {3C2B} IF < 30, KEEP LOOPING +:_NSEXIT + RTS ; {6C1B} +* +``` + + + +--- + + + +### THE _FN2BUFF SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------- | +| Name | `_FN2BUFF` | +| Type | Subroutine | +| File | `HEAD.DOS.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Copy temporary 30-byte filename to the filename buffer. | +| Input | none | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 41+ | +| Bytes | 22 | +| Notes | none | +| See Also | `_NAMESTR` `_NAMESTR2` `_FMNM2` | + +--- + +*DETAILS* + +The `_FN2BUFF` subroutine simply copies the 30-byte filename created by `_NAMESTR` to the filename buffer for use by the File Manager. + + + +`LISTING 6.07: HEAD.DOS.ASM _FN2BUFF Routine Source` + +```assembly +* +*``````````````````````````````* +* _FN2BUFF (NATHAN RIGGS) * +* * +* MOVE THE TEMPORARY 30-BYTE * +* FILENAME STRING INTO THE * +* FILENAME BUFFER OF THE FILE * +* BUFFER IN USE. * +* * +* INPUT * +* * +* NONE. * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* CYCLES: 41+ * +* SIZE: 22 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +_FN2BUFF +* + LDA ]FNBUF ; {4C3B} LOAD FILENAME BUFFER ADDR + STA ADDR2 ; {3C2B} STORE LOW IN ZERO + LDA ]FNBUF+1 ; {4C3B} + STA ADDR2+1 ; {3C2B} STORE HIGH IN ZERO + LDY #255 ; {3C2B} SET .Y INDEX TO -1 +:_LPC1 + INY ; {2C1B} INCREASE .Y INDEX + LDA ]FNAME,Y ; {5C2B} GET CHAR STORED IN NAME AREA + STA (ADDR2),Y ; {6C2B} STORE IN FILENAME BUFFER + CPY #29 ; {2C2B} IS THE END OF STRING REACHED? + BNE :_LPC1 ; {3C2B} IF NO, KEEP LOOPING + RTS ; {6C1B} +* +``` + + + +--- + + + +### THE _FMNM2 SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `_FMNM2` | +| Type | Subroutine | +| File | `HEAD.DOS.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Register the address of the second filename
to be used by the File Manager. | +| Input | none | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 55+ | +| Bytes | 30 | +| Notes | none | +| See Also | `_NAMESTR` `_NAMESTR2` `_FN2BUFF` | + +--- + +*DETAILS* + +The `_FMNM2` subroutine is functionally equivalent to the `_FN2BUFF` subroutine except for a second filename, which is usually a destination filename. Although equivalent, the process for `_FMNM2` is different than `_FN2BUFF` and therefore cannot be merged into a single subroutine. + + + +`LISTING 6.08: HEAD.DOS.ASM _FMNM2 Routine Source` + +```assembly +* +*``````````````````````````````* +* _FMNM2 (NATHAN RIGGS) * +* * +* REGISTER THE ADDRESS OF THE * +* SECOND FILENAME IN THE FILE * +* PARAMETER LIST. * +* * +* INPUT * +* * +* NONE. * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* CYCLES: 55+ * +* SIZE: 30 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +_FMNM2 + LDA #]FNAME2 ; {3C2B} + LDX #>]FNAME2 ; {3C2B} + STA ADDR2 ; {3C2B} LOW BYTE OF REC ADDRESS IN .A + STX ADDR2+1 ; {3C2B} HIGH BYTE IN .X + LDA ]FPARM ; {4C3B} LOAD PARAM ADDRESS + STA ADDR1 ; {3C2B} AND HOLD IN ZERO PAGE + LDA ]FPARM+1 ; {4C3B} ALSO DO FOR HIGH BYTE + STA ADDR1+1 ; {3C2B} + LDY #$02 ; {3C2B} OFFSET TO THE RECORD VARIABLE + LDA ADDR2 ; {3C2B} LOAD LOW BYTE + STA (ADDR1),Y ; {6C2B} AND STORE IN PARAM LIST + INY ; {2C1B} INCREASE .Y INDEX + LDA ADDR2+1 ; {3C2B} LOAD HIGH BYTE + STA (ADDR1),Y ; {6C2B} AND STORE IN PARAM LIST + RTS ; {6C1B} +* +``` + + + +--- + + + +### THE _BUFFCLEAR SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | -------------------------------------------- | +| Name | `_BUFFCLEAR` | +| Type | Subroutine | +| File | `HEAD.DOS.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Clear the current file buffer for use again. | +| Input | none | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 31+ | +| Bytes | 17 | +| Notes | none | +| See Also | `_FMBUFFSCAN` | + +--- + +*DETAILS* + +The `_BUFFCLEAR` subroutine clears out the last used file buffer for use again in the future. It does this by replacing the first byte in the filename buffer with the value **#$00**, which indicates to DOS that the buffer is free. This subroutine should be called whenever a program is finished using a file and closes it; otherwise, the file buffer will not be cleared, and multiple uses of the file buffers will eventually result in error. + + + +`LISTING 6.09: HEAD.DOS.ASM _BUFFCLEAR Routine Source` + +```assembly +* +*``````````````````````````````* +* _BUFFCLEAR (NATHAN RIGGS) * +* * +* CLEAR THE FILE BUFFER THAT * +* IS CURRENTLY IN USE SO THAT * +* THE BUFFER MAY BE REUSED. * +* * +* INPUT * +* * +* NONE. * +* * +* CYCLES: 31+ * +* SIZE: 17 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +_BUFFCLEAR +* + LDA ]FNBUF ; {4C3B} GET FILENAME BUFFER ADDR + STA ADDR2 ; {3C2B} STORE IN ZERO + LDA ]FNBUF+1 ; {4C3B} DO SAME FOR HIGH BYTE + STA ADDR2+1 ; {3C2B} + LDA #0 ; {3C2B} RESET .A + TAY ; {2C1B} AND .Y + STA (ADDR2),Y ; {6C3B} STORE #00 IN FIRST SPOT + RTS ; {6C1B} TO INDICATE EMPTY BUFFER +* + +``` + + + +--- + + + +### THE _FMPSET SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------ | +| Name | `_FMPSET` | +| Type | Subroutine | +| File | `HEAD.DOS.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Set a file parameter. | +| Input | .A = Value
.Y = File Parameter Offset | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 30+ | +| Bytes | 16 | +| Notes | none | +| See Also | `_FMPSETA` `_FMPGET` `_FMPGETA` | + +--- + +*DETAILS* + +The `_FMPSET` subroutine sets a file parameter in the **FPL** indicated by the offset held in the **.Y** register to the value held in the **.A** register. This only sets a single byte. + + + +`LISTING 6.10: HEAD.DOS.ASM _FMPSET Routine Source` + +```assembly +* +*``````````````````````````````* +* _FMPSET (NATHAN RIGGS) * +* * +* SET A FILE PARAMTER TO THE * +* VALUE HELD IN .A AT THE * +* OFFSET INDICATED BY .Y. * +* * +* INPUT * +* * +* .A = VALUE * +* .Y = FILE PARAMETER OFFSET * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 30+ * +* SIZE: 16 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +_FMPSET + ; .Y HOLDS THE OFFSET + TAX ; {2C1B} .A HOLDS THE VALUE + LDA ]FPARM ; {4C3B} LOAD FILE MGR PARAMETER LOW + STA ADDR1 ; {3C2B} HOLD ON ZERO PAGE + LDA ]FPARM+1 ; {4C3B} LOAD HIGH BYTE + STA ADDR1+1 ; {3C2B} ONTO ZERO PAGE + TXA ; {2C1B} RETURN VALUE BACK TO .A + STA (ADDR1),Y ; {6C3B} STORE VALUE AT OFFSET IN PARAMS + RTS ; {6C1B} +* +``` + + + +--- + + + +### THE _FMPSETA SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `_FMPSETA` | +| Type | Subroutine | +| File | `HEAD.DOS.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Set a file parameter. | +| Input | .A = Value Low Byte
.X = Value High Byte
.Y = File Parameter Offset | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 46+ | +| Bytes | 26 | +| Notes | none | +| See Also | `_FMPSET` `_FMPGET` `_FMPGETA` | + +--- + +*DETAILS* + +The `_FMPSETA` subroutine sets a file parameter address in the **FPL** indicated by the offset held in the **.Y** register to the value held in the **.A** register (low byte) and the **.X** register (high byte), setting a two consecutive bytes. + + + +`LISTING 6.11: HEAD.DOS.ASM _FMPSETA Routine Source` + +```assembly +* +*``````````````````````````````* +* _FMPSETA (NATHAN RIGGS) * +* * +* SET A TWO-BYTE VALUE, SUCH * +* AS AN ADDRESS, IN THE FILE * +* PARAMETER LIST. * +* * +* INPUT * +* * +* .A = LOWEST BYTE * +* .X = HIGHEST BYTE * +* .Y = OFFSET VALUE * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 46+ * +* SIZE: 26 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +_FMPSETA + ; .Y HOLDS THE OFFSET + STA ADDR2 ; {3C2B} .A HOLDS LOW BYTE OF ADDR + STX ADDR2+1 ; {3C2B} .X HOLDS ADDR HIGH BYTE + LDA ]FPARM ; {4C3B} LOAD FILE MGR PARAMETER LOW + STA ADDR1 ; {3C2B} HOLD ON ZERO PAGE + LDA ]FPARM+1 ; {4C3B} LOAD HIGH BYTE + STA ADDR1+1 ; {3C2B} ONTO ZERO PAGE + LDA ADDR2 ; {3C2B} + STA (ADDR1),Y ; {6C3B} STORE VAL AT OFFSET IN PARAMS + LDA ADDR2+1 ; {3C2B} + INY ; {2C1B} + STA (ADDR1),Y ; {6C3B} + RTS ; {6C1B} +* +``` + + + +--- + + + +### THE _FMPGET SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | --------------------------------------- | +| Name | `_FMPGET` | +| Type | Subroutine | +| File | `HEAD.DOS.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Retrieve a file parameter from the FPL. | +| Input | .Y = File Parameter Offset | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 26+ | +| Bytes | 14 | +| Notes | none | +| See Also | `_FMPGETA` `_FMPSET` `_FMPSETA` | + +--- + +*DETAILS* + +The `_FMPGET` subroutine retrieves a single byte from a given parameter in the **FPL**, as indicated by the offset held in **.Y**. + + + +`LISTING 6.12: HEAD.DOS.ASM _FMPGET Routine Source` + +```assembly +* +*``````````````````````````````* +* _FMPGET (NATHAN RIGGS) * +* * +* GET A VALUE RETURNED FROM * +* THE FILE MANAGER AFTER IT * +* RUNS. THIS VALUE IS HELD AT * +* AN OFFSET IN THE FILE PARAM * +* LIST. * +* * +* INPUT: * +* * +* .Y = OFFSET TO READ * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 26+ * +* SIZE: 14 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +_FMPGET + ; OFFSET IS PASSED IN .Y + LDA ]FPARM ; {4C3B} + STA ADDR1 ; {3C2B} + LDA ]FPARM+1 ; {4C3B} + STA ADDR1+1 ; {3C2B} + LDA (ADDR1),Y ; {6C3B} VALUE RETURNED IN .A + RTS ; {6C1B} +* +``` + + + +--- + + + +### THE _FMPGETA SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | -------------------------------------------------------- | +| Name | `_FMPGETA` | +| Type | Subroutine | +| File | `HEAD.DOS.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Retrieve a two-byte address file parameter from the FPL. | +| Input | .Y = File Parameter Offset | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 42+ | +| Bytes | 21 | +| Notes | none | +| See Also | `_FMPGET` `_FMPSET` `_FMPSETA` | + +--- + +*DETAILS* + +The `_FMPGETA` subroutine retrieves two consecutive bytes from the offset held in **.Y** in the **FPL**. This is usually an address; the low byte is returned in **.A** and the high byte is returned in **.X**. + + + +`LISTING 6.13: HEAD.DOS.ASM _FMPGETA Routine Source` + +```assembly +* +*``````````````````````````````* +* _FMPGETA (NATHAN RIGGS) * +* * +* RETRIEVE A 2-BYTE VALUE LIKE * +* AN ADDRESS FROM THE FILE * +* PARAMETER LIST AT THE GIVEN * +* OFFSET. * +* * +* INPUT * +* * +* .Y = OFFSET INDEX * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 42+ * +* SIZE: 21 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +_FMPGETA + ; OFFSET PASSED IN .Y + LDA ]FPARM ; {4C3B} + STA ADDR1 ; {3C2B} + LDA ]FPARM+1 ; {4C3B} + STA ADDR1+1 ; {3C2B} + LDA (ADDR1),Y ; {6C3B} GET LOW BYTE + PHA ; {3C1B} HOLD ON STACK + INY ; {2C1B} INCREASE INDEX + LDA (ADDR1),Y ; {6C3B} GET HIGH BYTE + TAX ; {2C1B} PASS BACK IN .X + PLA ; {3C1B} PASS LOW IN .A + RTS ; {6C1B} +* +``` + + + +--- + + + +## Required DOS Macros + + + +The MAC.DOSREQ.ASM file contains various macros that are required to be included (USE) in order for the entire DOS collection to function. This is unlike other collections, which usually do not require a separate macro file that the other macros and subroutines depend upon. While these macros could be replaced in each macro and subroutine with the appropriate code, the Merlin 8 Pro compiler would quickly run into memory management issues due to limitations on file size. + +Most of these macros call a combination of the subroutines found in the HEAD.DOS.ASM file, and should be used in place of calling the subroutines directly. + + + +`LISTING 6.20: MAC.DOSREQ.ASM Heading Source` + +```assembly +* +*``````````````````````````````* +* MAC.DOSREQ.ASM * +* * +* THESE MACROS ARE REQUIRED * +* FOR USING THE DOS MACROS AND * +* SUBROUTINES. GENERALLY, THIS * +* INCLUDES LOW LEVEL ROUTINES * +* AND MACRO SHORTCUTS TO SAVE * +* MEMORY DURING ASSEMBLY. * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 06-MAY-2021 * +* ASSEMBLER: MERLIN 8 PRO * +* OS: DOS 3.3 * +* * +* SUBROUTINE FILES USED * +* * +* * +* LIST OF MACROS * +* * +* SRWTS : SET RWTS PARAMETERS * +* GRWTS : RUN RWTS ROUTINE * +* FMFIL : FILL FILE MGR PARAMS * +* FMNAM : STRING TO PARAM AREA * +* FRWB : BYTE READ/WRITE MAC * +* FWRTB : WRITE A BYTE TO FILE * +* FRDB : READ BYTE FROM FILE * +* FRWR : RANGE READ/WRITE MAC * +* FRDR : READ RANGE FROM FILE * +* FWRTR : WRITE RANGE TO FILE * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +``` + + + +--- + + + +### THE SRWTS MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `SRWTS` | +| Type | Macro | +| File | `MAC.DOSREQ.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Set common RWTS parameters. | +| Input | ]1 = Slot
]2 = Drive
]3 = Volume
]4 = Track
]5 = Sector
]6 = Buffer Address
]7 = Command | +| Output | none | +| Dependencies | `_SETRWTS` | +| Flags Destroyed | NZCV | +| Cycles | 228+ | +| Bytes | 141 | +| Notes | none | +| See Also | `_SETRWTS` | + +--- + +*DETAILS* + +The `SRWTS` macro sets the most common parameters used in the **IOB** before calling the **RWTS** routine. This includes the slot, drive, volume, track, sector, buffer address and **RWTS** command. If the volume is set to **#0**, then any volume will be acceptable for **RWTS**. + + + +`LISTING 6.21: MAC.DOSREQ.ASM SRWTS Macro Source` + +```assembly +* +*``````````````````````````````* +* SETRWTS * +* * +* SET THE PARAMETERS FOR RWTS. * +* * +* PARAMETERS * +* * +* ]1 = RWTS SLOT * +* ]2 = RWTS DRIVE * +* ]3 = RWTS VOLUME * +* ]4 = RWTS TRACK * +* ]5 = RWTS SECTOR * +* ]6 = RWTS BUFFER * +* ]7 = RWTS COMMAND * +* * +* CYCLES: 228+ * +* SIZE: 141 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +SRWTS MAC + LDA ]1 ; {4C3B} RWTS SLOT + STA MSLOT ; {4C3B} + LDA ]2 ; {4C3B} RWTS DRIVE + STA MDRIVE ; {4C3B} + LDA ]3 ; {4C3B} RWTS VOLUME + STA MVOL ; {4C3B} + LDA ]4 ; {4C3B} RWTS TRACK + STA MTRACK ; {4C3B} + LDA ]5 ; {4C3B} RWTS SECTOR + STA MSECTOR ; {4C3B} + IF #=]6 ; IF VALUE IS A LITERAL + LDA ]6 ; {4C3B} THEN STORE LITERAL + STA MBUFFER ; {4C3B} LOW AND HIGH BYTES + LDA ]6/$100 ; {4C3B} + STA MBUFFER+1 ; {4C3B} + ELSE ; OTHERWISE, + LDA ]6 ; {4C3B} LOAD VALUE FROM ADDRESS + STA MBUFFER ; {4C3B} AND STORE IN BUFFER + LDA ]6+1 ; {4C3B} ADDRESS POINTER + STA MBUFFER+1 ; {4C3B} + FIN + LDA ]7 ; {4C3B} RWTS COMMAND + STA MCMD ; {4C3B} + JSR _SETRWTS ; {148C81B} SET THE PARAMETERS + <<< +* +``` + + + +--- + + + +### THE GRWTS MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------- | +| Name | `GRWTS` | +| Type | Macro | +| File | `MAC.DOSREQ.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Run the DOS RWTS Routine. | +| Input | none | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 19+ | +| Bytes | 11 | +| Notes | none | +| See Also | `SRWTS` | + +--- + +*DETAILS* + +The `GRWTS` macro simply runs the **RWTS** routine found in DOS. At the very least, the `SRWTS` macro should be called beforehand to set the various parameters used by **RWTS**. + + + +`LISTING 6.22: MAC.DOSREQ.ASM GRWTS Macro Source` + +```assembly +* +*``````````````````````````````* +* GRWTS * +* * +* RUN THE RWTS ROUTINE. * +* * +* PARAMETERS * +* * +* NONE. * +* * +* CYCLES: 19+ * +* SIZE: 11 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +GRWTS MAC + JSR ]LOCRPL ; {6C3B} (GET ]LOCRPL CYCLES/SIZE) + JSR ]RWTS ; {6C3B} (GET CYCLES/SIZE) + LDA #0 ; {3C2B} + STA $48 ; {4C3B} FIX P REG FOR DOS + <<< +* +``` + + + +--- + + + +### THE FMFIL MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `FMFIL` | +| Type | Macro | +| File | `MAC.DOSREQ.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Fill the most common parameters in the FPL. | +| Input | ]1 = Command
]2 = Slot
]3 = Drive
]4 = Volume
]5 = Record Number | +| Output | none | +| Dependencies | `_FMGETPARM` `_FMBUFFSCAN` `_SETPBUFFS` `_FMPSET` `_FMPSETA` | +| Flags Destroyed | NZCV | +| Cycles | 603+ | +| Bytes | 303 | +| Notes | none | +| See Also | `SRWTS` `FMNAM` | + +--- + +*DETAILS* + +The `FMFIL` macro fills out the most common parameters used in the **File Parameter List (FPL)**. This includes the file command, slot, drive, volume, and record number. Note that not all of these are applicable to every use. Like with `SRWTS`, the volume can be set to **#0** to indicate that any volume is acceptable. + + + +`LISTING 6.23: MAC.DOSREQ.ASM FMFIL Macro Source` + +```assembly +* +*``````````````````````````````* +* FMFIL (NATHAN RIGGS) * +* * +* FILLS THE MOST COMMON AREAS * +* IN THE FILE PARAMETERS. THIS * +* INCLUDES THE COMMAND, SLOT, * +* DISK, VOLUME AND RECORD. * +* * +* PARAMETERS * +* * +* ]1 = COMMAND * +* ]2 = SLOT * +* ]3 = DRIVE * +* ]4 = VOLUME (0 FOR ANY) * +* ]5 = RECORD NUMBER * +* * +* CYCLES: 603+ * +* SIZE: 303 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +FMFIL MAC + JSR _FMGETPARM ; {26C13B} GET FILE PARAMS ADDRESS + JSR _FMBUFFSCAN ; {217C120B} SCAN FOR FREE FILE BUFF + JSR _SETPBUFFS ; {125C67B} SET PARAMETER BUFFERS + LDA ]1 ; {4C3B} COMMAND TO PARAMETERS + LDY #]P_CMD ; {3C2B} SET INDEX TO COMMAND OFFSET + JSR _FMPSET ; {36C19B} SET COMMAND + LDA ]2 ; {4C3B} SLOT TO PARAMETERS + LDY #]P_SLOT ; {3C2B} SET INDEX TO SLOT OFFSET + JSR _FMPSET ; {36C19B} SET SLOT + ; VOLUME AND RECORD NUMBER + LDA ]3 ; {4C3B} DRIVE TO PARAMETERS + LDY #]P_DRV ; {3C2B} SET INDEX TO DRIVE OFFSET + JSR _FMPSET ; {36C19B} SET DRIVE + LDA ]4 ; {4C3B} VOLUME TO PARAMS, 0 FOR ANY + LDY #]P_VOL ; {3C2B} SET INDEX TO VOLUME OFFSET + JSR _FMPSET ; {36C19B} SET VOLUME + _AXLIT ]5 ; {8C6B} + LDY #]P_REC ; {3C2B} SET INDEX TO RECORD OFFSET + JSR _FMPSETA ; {52C29B} SET RECORD LOW AND HIGH + <<< +* +``` + + + +--- + + + +### THE FMNAM MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `FMNAM` | +| Type | Macro | +| File | `MAC.DOSREQ.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Copy a string with a preceding length-byte to the filename buffer. | +| Input | ]1 = Source string address | +| Output | none | +| Dependencies | `_NAMESTR` `_FN2BUFF` | +| Flags Destroyed | NZCV | +| Cycles | 117+ | +| Bytes | 70 | +| Notes | none | +| See Also | `FMFIL` | + +--- + +*DETAILS* + +The `FMNAM` macro copies a string with a preceding length-byte into a 30-byte memory location, filling the unused bytes in the destination with spaces. This is then copied to the **Filename Buffer** in a currently used file buffer. This filename is used for commands such as deletion, verification, opening and closing, and so on. This is also used with the renaming command, with the addition of a destination filename. + + + +`LISTING 6.24: MAC.DOSREQ.ASM FMNAM Macro Source` + +```assembly +* +*``````````````````````````````* +* FMNAM (NATHAN RIGGS) * +* * +* COPY A STRING HOLDING THE * +* NAME OF A FILE BEING HANDLED * +* TO A 30-BYTE LOCATION FILLED * +* WITH SPACES, THEN COPY THAT * +* TO THE FILE PARAMETER LIST. * +* * +* PARAMETERS * +* * +* ]1 = ADDRESS OF STRING * +* * +* CYCLES: 117+ * +* SIZE: 70 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +FMNAM MAC + _AXLIT ]1 + JSR _NAMESTR ; {70C45B} COPY STRING TO TEMP BUFF + JSR _FN2BUFF ; {47C25B} COPY TEMP BUFF TO FNAME BUFF + <<< +* +``` + + + +--- + + + +### THE FRWB MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `FRWB` | +| Type | Macro | +| File | `MAC.DOSREQ.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Read or write a byte in a file. | +| Input | ]1 = Read/Write flag
]2 = Command
]3 = Slot
]4 = Drive
]5 = Volume
]6 = Record Number
]7 = Byte offset
]8 = Byte to write, if writing | +| Output | none | +| Dependencies | `FMFIL` `_FMPSET` `_FMPSETA` | +| Flags Destroyed | NZCV | +| Cycles | 779+ | +| Bytes | 404 | +| Notes | none | +| See Also | `FWRTB` `FRDB` `FRWR` `FRDR` `FWRTR` | + +--- + +*DETAILS* + +The `FRWB` Macro either reads or writes a single byte in an open file, depending on which flag is provided the macro in the first parameter. Usually, this macro is used internally, rather than by the end user; the `FWRTB` macro and the `FRDB` macro are used instead to write and read a byte in a file. If writing, the eighth parameter is set to the byte to write; if not, this parameter is ignored. It should also be noted that this macro does not return a value when reading a byte; instead, this is handled by the `FRDB` macro, as the byte is returned by the file manager in the **File Parameter List (FPL)**. + +As usual, a **#0** value can be passed to the volume parameter to indicate that any volume is acceptable. + + + +`LISTING 6.25: MAC.DOSREQ.ASM FRWB Macro Source` + +```assembly +* +*``````````````````````````````* +* FRWB (NATHAN RIGGS) * +* * +* EITHER READ OR WRITE A BYTE. * +* THIS USUALLY ISN'T CALLED * +* DIRECTLY, BUT THROUGH * +* ANOTHER MACRO. * +* * +* PARAMETERS * +* * +* ]1 = READ/WRITE FLAG (#3/4) * +* ]2 = COMMAND * +* ]3 = SLOT * +* ]4 = DRIVE * +* ]5 = VOLUME (0 FOR ANY) * +* ]6 = RECORD NUMBER * +* ]7 = BYTE OFFSET * +* ]8 = BYTE TO WRITE (0 READ) * +* * +* CYCLES: 779+ * +* SIZE: 404 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +FRWB MAC +* + FMFIL ]2;]3;]4;]5;]6 ; {603C303B} +* + LDA ]1 ; {4C3B} READ OR WRITE FLAG + LDY #]P_RDWR ; {3C2B} INDEX TO READ/WRITE FLAG + JSR _FMPSET ; {30C16B} SET THE READ/WRITE FLAG + LDA #]SC_PRWB ; {3C2B} SET POS THEN READ OR WRITE + LDY #]P_SUBC ; {3C2B} SET INDEX TO SUBCODE OFFSET + JSR _FMPSET ; {30C16B} SET THE SUBCODE + _AXLIT ]7 ; {8C6B} + LDY #]P_BOFF ; {3C2B} INDEX TO BYTE OFFSET + JSR _FMPSETA ; {46C26B} SET TWO BYTE OFFSET VALUE + LDA ]8 ; {4C3B} BYTE TO WRITE, IF WRITING + LDY #]P_BYTE ; {3C2B} WRITE BYTE + JSR _FMPSET ; {30C16B} SET BYTE + LDX #1 ; {3C2B} DON'T CREATE NEW FILE + JSR ]FMRUN ; {6C3B} RUN FILE MANAGER + <<< +* +``` + + + +--- + + + +### THE FWRTB MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `FWRTB` | +| Type | Macro | +| File | `MAC.DOSREQ.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Write a byte to a file. | +| Input | ]1 = Slot
]2 = Drive
]3 = Volume
]4 = Record number
]5 = Byte offset
]6 = Byte to write | +| Output | none | +| Dependencies | `_FMPGET` `_BUFFCLEAR` | +| Flags Destroyed | NZCV | +| Cycles | 665+ | +| Bytes | 337 | +| Notes | none | +| See Also | `FRWB` `FRDB` `FRWR` `FRDR` `FWRTR` `FOPEN` `FCLOS` | + +--- + +*DETAILS* + +The `FWRTB` macro is used for writing a single byte at a time to an open file. Like with most File Manager operations, a value of **#0** placed in the volume parameter indicates that any volume is acceptable. + +Records in files work much the same way as they do in Applesoft BASIC: each record in a file is separated by a carriage return (**#$8D**), with the zeroth record being the first string of text prior to the first carriage return. These are usually used with text files, which are only marginally supported by the DOS collection: binary files make much more sense to use in Assembly, even when the data itself represents text characters. This is not to say that text files cannot be used--the `FOPEN` macro allows you to read and write text files--but that it is better for the end programmer to devise his or her own system for dealing with text files, as there is no standard method for recognizing an End-of-File (EOF) marker. Both binary files and text files can be fully read into memory before being parsed by a given routine. + +The byte offset is particularly useful with `FWRTB`, as without it only the first byte of the file could be written. A byte counter can be used to read or write each consecutive byte at a time, passing the counter value via the offset. + +The `FOPEN` macro should be called before `FWRTB`, and the `FCLOS` macro should be called when all operations on the file are finished. + + + +`LISTING 6.26: MAC.DOSREQ.ASM FWRTB Macro Source` + +```assembly +* +*``````````````````````````````* +* FWRTB (NATHAN RIGGS) * +* * +* WRITE A BYTE TO A FILE. * +* * +* PARAMETERS * +* * +* ]1 = SLOT * +* ]2 = DRIVE * +* ]3 = VOLUME * +* ]4 = RECORD NUMBER * +* ]5 = BYTE OFFSET * +* ]6 = BYTE TO WRITE (0 READ) * +* * +* CYCLES: 665+ * +* SIZE: 337 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +FWRTB MAC +* +** FRWB ARGUMENTS +** +** ]1 = READ OR WRITE CODE (3 = READ, 4 = WRITE) +** ]2 = READ OR WRITE COMMAND +** ]3 = SLOT +** ]4 = DRIVE +** ]5 = VOLUME (0 FOR ANY) +** ]6 = RECORD NUMBER +** ]7 = BYTE OFFSET +** ]8 = BYTE TO WRITE, IF WRITING +* + FRWB #4;#]FC_WRIT;]1;]2;]3;]4;]5;]6 ; {603C303B} +* + LDY #]P_RETC ; {3C2B} INDEX TO RETURN CODE OFFSET + JSR _FMPGET ; {26C14B} GET THE RETURN CODE + TAX ; {2C1B} PASS BACK IN .X + JSR _BUFFCLEAR ; {31C17B} CLEAR FILE BUFFER FOR USE + <<< +* +``` + + + +--- + + + +### THE FRDB MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `FRDB` | +| Type | Macro | +| File | `MAC.DOSREQ.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Read a byte from a file. | +| Input | ]1 = Slot
]2 = Drive
]3 = Volume
]4 = Record number
]5 = Byte offset
]6 = Byte to write (always 0) | +| Output | none | +| Dependencies | `_FMPGET` `_BUFFCLEAR` | +| Flags Destroyed | NZCV | +| Cycles | 700+ | +| Bytes | 357 | +| Notes | none | +| See Also | `FRWB` `FWRTB` `FRWR` `FRDR` `FWRTR` | + +--- + +*DETAILS* + +The `FRDB` macro reads a byte from an open file and returns the byte in the **.A** register. If the volume is set to **#0**, any volume will be accepted. + +Records in files work much the same way as they do in Applesoft BASIC: each record in a file is separated by a carriage return (**#$8D**), with the zeroth record being the first string of text prior to the first carriage return. These are usually used with text files, which are only marginally supported by the DOS collection: binary files make much more sense to use in Assembly, even when the data itself represents text characters. This is not to say that text files cannot be used--the `FOPEN` macro allows you to read and write text files--but that it is better for the end programmer to devise his or her own system for dealing with text files, as there is no standard method for recognizing an End-of-File (EOF) marker. Both binary files and text files can be fully read into memory before being parsed by a given routine. + +The byte offset is particularly useful with `FRDB`, as without it only the first byte of the file could be read. A byte counter can be used to read or write each consecutive byte at a time, passing the counter value via the offset. + +The `FOPEN` macro should be called before `FWRTB`, and the `FCLOS` macro should be called when all operations on the file are finished. + + + +`LISTING 6.27: MAC.DOSREQ.ASM FRDB Macro Source` + +```assembly +* +*``````````````````````````````* +* FRDB (NATHAN RIGGS) * +* * +* READ A BYTE FROM A FILE. * +* * +* PARAMETERS * +* * +* ]1 = SLOT * +* ]2 = DRIVE * +* ]3 = VOLUME * +* ]4 = RECORD NUMBER * +* ]5 = BYTE OFFSET * +* ]6 = BYTE TO WRITE (0 READ) * +* * +* CYCLES: 700+ * +* SIZE: 357 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +FRDB MAC +* +** FWRB ARGUMENTS +** +** ]1 = READ OR WRITE CODE (3 = READ, 4 = WRITE) +** ]2 = READ OR WRITE COMMAND +** ]3 = SLOT +** ]4 = DRIVE +** ]5 = VOLUME (0 FOR ANY) +** ]6 = RECORD NUMBER +** ]7 = BYTE OFFSET +** ]8 = BYTE TO WRITE, IF WRITING +* + FRWB #3;#]FC_READ;]1;]2;]3;]4;]5;]6 ; {603C303B} +* + LDY #]P_BYTE ; {3C2B} INDEX TO BYTE TO READ + JSR _FMPGET ; {26C14B} GET BYTE IN THE PARAMETERS + STA BPAR1 ; {3C2B} HOLD IN ZERO PAGE + LDY #]P_RETC ; {3C2B} INDEX TO THE RETURN CODE OFFSET + JSR _FMPGET ; {26C14B} GET THE RETURN CODE + TAX ; {2C1B} PASS BACK IN .X + JSR _BUFFCLEAR ; {31C17B} CLEAR FILE BUFFER FOR USE + LDA BPAR1 ; {3C2B} PASS READ BYTE IN .A + <<< +* +``` + + + +--- + + + +### THE FRWR MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `FRWR` | +| Type | Macro | +| File | `MAC.DOSREQ.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Read or write a range of bytes in a file. | +| Input | ]1 = READ/WRITE flag
]2 = Command
]3 = Slot
]4 = Drive
]5 = Volume
]6 = Record number
]7 = Byte offset
]8 = Range Address
]TEMP = Range length | +| Output | none | +| Dependencies | `_FMPSET` `_FMPSETA` | +| Flags Destroyed | NZCV | +| Cycles | 846+ | +| Bytes | 451 | +| Notes | none | +| See Also | `FRWB` `FWRTB` `FRDB` `FRDR` `FWRTR` | + +--- + +*DETAILS* + +The `FRWR` macro reads or writes a range of bytes from or to an open file, with the Range Address either containing the bytes to be written up to the specified length or the memory area to store the bytes read from the file at the given length. It is notable that because **Merlin 8 Pro** can only handle eight built-in variables (]1 - ]8), the ]TEMP variable is used to pass the range length. Additionally, and like usual, a value of **#0** in the volume parameter indicates that any volume number is valid. + +For the most part, this macro is meant to be used internally. The end user should use the `FRDR` macro for reading a range of bytes and the `FWRTR` macro for writing a range of bytes. + + + +`LISTING 6.28: MAC.DOSREQ.ASM FRWR Macro Source` + +```assembly +* +*``````````````````````````````* +* FRWR (NATHAN RIGGS) * +* * +* READ OR WRITE A RANGE. * +* * +* PARAMETERS * +* * +* ]1 = READ/WRITE FLAG * +* ]2 = COMMAND * +* ]3 = SLOT * +* ]4 = DRIVE * +* ]5 = VOLUME * +* ]6 = RECORD NUMBER * +* ]7 = BYTE OFFSET * +* ]8 = RANGE ADDRESS * +* ]TEMP = RANGE LENGTH * +* * +* CYCLES: 846+ * +* SIZE: 451 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +FRWR MAC +* +** SET THE COMMAND, SLOT, DRIVE, VOLUME AND RECORD NUMBER +* + FMFIL ]2;]3;]4;]5;]6 ; {603C303B} +* + LDA ]1 ; {4C3B} LOAD READ OR WRITE FLAG + LDY #]P_RDWR ; {3C2B} INDEX TO RD/WRT FLAG OFFSET + JSR _FMPSET ; {30C16B} SET READ OR WRITE FLAG + LDA #]SC_PRWR ; {3C2B} SUBCODE FOR POS AND READ/WRITE + LDY #]P_SUBC ; {3C2B} INDEX TO SUBCODE OFFSET + JSR _FMPSET ; {30C16B} SET SUBCODE + _AXLIT ]7 ; {8C6B} + LDY #]P_BOFF ; {3C2B} INDEX TO BYTE OFFSET + JSR _FMPSETA ; {46C26B} SET BYTE OFFSET + _AXLIT ]8 ; {8C6B} + LDY #]P_RADDR ; {3C2B} INDEX TO RANGE ADDR OFFSET + JSR _FMPSETA ; {46C26B} SET RANGE ADDRESS + LDA ]TEMP ; {4C3B} GET RANGE LENGTH + LDX ]TEMP+1 ; {4C3B} LOW BYTE AND HIGH BYTE + LDY #]P_RLEN ; {3C2B} INDEX TO RANGE LENGTH OFFSET + JSR _FMPSETA ; {46C26B} SET RANGE LENGTH + LDX #1 ; {3C2B} DON'T CREATE NEW FILE + JSR ]FMRUN ; {6C3B} RUN FILE MANAGER + <<< +* +``` + + + +--- + + + +### THE FRDR MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `FRDR` | +| Type | Macro | +| File | `MAC.DOSREQ.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Read a range of bytes from a file. | +| Input | ]1 = Slot
]2 = Drive
]3 = Volume
]4 = Record number
]5 = Byte offset
]6 = Range address
]7 = Range length | +| Output | none | +| Dependencies | `_FMPGET` `FRWR` `_BUFFCLEAR` | +| Flags Destroyed | NZCV | +| Cycles | 697+ | +| Bytes | 361 | +| Notes | none | +| See Also | `FRWB` `FWRTB` `FRDB` `FRWR` `FWRTR` | + +--- + +*DETAILS* + +The `FRDR` macro reads a range of bytes from an open file, storing the range starting at the given range address through to the given range length (two bytes). As per the collection standard, a value of **#0** in the volume parameter indicates that any volume number is valid. + +Records in files work much the same way as they do in Applesoft BASIC: each record in a file is separated by a carriage return (**#$8D**), with the zeroth record being the first string of text prior to the first carriage return. These are usually used with text files, which are only marginally supported by the DOS collection: binary files make much more sense to use in Assembly, even when the data itself represents text characters. This is not to say that text files cannot be used--the `FOPEN` macro allows you to read and write text files--but that it is better for the end programmer to devise his or her own system for dealing with text files, as there is no standard method for recognizing an End-of-File (EOF) marker. Both binary files and text files can be fully read into memory before being parsed by a given routine. + +The byte offset is used to start reading or writing the range at a particular location in the file. + +The `FOPEN` macro should be called before `FRDR`, and the `FCLOS` macro should be called when all operations on the file are finished. + + + +`LISTING 6.29: MAC.DOSREQ.ASM FRDR Macro Source` + +```assembly +* +*``````````````````````````````* +* FRDR (NATHAN RIGGS) * +* * +* READ A RANGE FROM A FILE. * +* * +* PARAMETERS * +* * +* ]1 = SLOT * +* ]2 = DRIVE * +* ]3 = VOLUME * +* ]4 = RECORD NUMBER * +* ]5 = BYTE OFFSET * +* ]6 = RANGE ADDRESS * +* ]7 = RANGE LENGTH * +* * +* CYCLES: 697+ * +* SIZE: 361 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +FRDR MAC + IF #=]7 ; IF RANGE ADDR IS A LITERAL + LDA ]7/$100 ; {4C3B} THEN LOAD HIGH BYTE + STA ]TEMP+1 ; {4C3B} AND STORE + LDA ]7 ; {4C3B} AND LOAD LOW BYTE + STA ]TEMP ; {4C3B} AND STORE IN TEMP + ELSE + LDA ]7+1 ; {4C3B} LOAD HIGH BYTE + STA ]TEMP+1 ; {4C3B} STORE HIGH BYTE + LDA ]7 ; {4C3B} LOAD LOW BYTE + STA ]TEMP ; {4C3B} STORE LOW BYTE + FIN +* + FRWR #3;#]FC_READ;]1;]2;]3;]4;]5;]6 ; {603C303B} +* + LDY #]P_RETC ; {3C2B} INDEX TO RETURN CODE + JSR _FMPGET ; {26C14B} GET RETURN CODE + TAX ; {2C1B} HOLD IN .X + JSR _BUFFCLEAR ; {31C17B} CLEAR FILE BUFFER FOR USE + <<< +* +``` + + + +--- + + + +### THE FWRTR MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `FWRTR` | +| Type | Macro | +| File | `MAC.DOSREQ.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Write a range of bytes to a file. | +| Input | ]1 = Slot
]2 = Drive
]3 = Volume
]4 = Record number
]5 = Byte offset
]6 = Range address
]7 = Range length | +| Output | none | +| Dependencies | `_FMPGET` `FRWR` `_BUFFCLEAR` | +| Flags Destroyed | NZCV | +| Cycles | 940+ | +| Bytes | 509 | +| Notes | none | +| See Also | `FRWB` `FWRTB` `FRDB` `FRWR` `FRDR` | + +--- + +*DETAILS* + +The `FWRTR` macro writes to an open file a range of bytes found at a given starting range address through a given range length. As per the collection standard, a value of #0 in the volume parameter indicates that any volume number is valid. + +Records in files work much the same way as they do in Applesoft BASIC: each record in a file is separated by a carriage return (**#$8D**), with the zeroth record being the first string of text prior to the first carriage return. These are usually used with text files, which are only marginally supported by the DOS collection: binary files make much more sense to use in Assembly, even when the data itself represents text characters. This is not to say that text files cannot be used--the `FOPEN` macro allows you to read and write text files--but that it is better for the end programmer to devise his or her own system for dealing with text files, as there is no standard method for recognizing an End-of-File (EOF) marker. Both binary files and text files can be fully read into memory before being parsed by a given routine. + +The byte offset is used to start reading or writing the range at a particular location in the file. + +The `FOPEN` macro should be called before `FWRTR`, and the `FCLOS` macro should be called when all operations on the file are finished. + + + +`LISTING 6.30: MAC.DOSREQ.ASM FWRTR Macro Source` + +```assembly +* +*``````````````````````````````* +* FWRTR (NATHAN RIGGS) * +* * +* WRITE A RANGE TO A FILE. * +* * +* PARAMETERS * +* * +* ]1 = SLOT * +* ]2 = DRIVE * +* ]3 = VOLUME * +* ]4 = RECORD NUMBER * +* ]5 = BYTE OFFSET * +* ]6 = RANGE ADDRESS * +* ]7 = RANGE LENGTH * +* * +* CYCLES: 940+ * +* SIZE: 509 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +FWRTR MAC + IF #=]7 ; IF RANGE LENGTH IS A LITERAL THEN + LDA ]7/$100 ; {4C3B} LOAD HIGH BYTE + STA ]TEMP+1 ; {4C3B} AND STORE IN TEMP + LDA ]7 ; {4C3B} LOAD LOW BYTE + STA ]TEMP ; {4C3B} AND STORE IN TEMP + ELSE ; OTHERWISE + LDA ]7+1 ; {4C3B} LOAD HIGH BYTE + STA ]TEMP+1 ; {4C3B} AND STORE IN TEMP + LDA ]7 ; {4C3B} LOAD LOW BYTE + STA ]TEMP ; {4C3B} AND STORE IN TEMP + FIN +* + FRWR #4;#]FC_WRIT;]1;]2;]3;]4;]5;]6 ; {846C451B} +* + LDY #]P_RETC ; {3C2B} INDEX TO RETURN CODE + JSR _FMPGET ; {26C14B} GET RETURN CODE + TAX ; {2C1B} PASS BACK IN .X + JSR _BUFFCLEAR ; {31C17B} CLEAR FILE BUFFER FOR USE + <<< +* +``` + + + +--- + + + +## DOS File Manager Macros + + + +The MAC.DOSFM.ASM file contains various macros that are used for most file operations. Currently, this includes disk cataloging, locking and unlocking files, deleting files, verifying files, opening and closing files, reading and writing files (both text and binary), renaming files, and bloading and bsaving binary files. Additionally, subroutines are provided for using DOS's **RWTS** routine for low-level access to a disk, though this is mostly limited to reading and writing bytes in a given track and sector. + +It should be noted that all of these macros need to have both the HEAD.DOS.ASM file and the MAC.DOSREQ.ASM file included (PUT and USE, respectively) in order to function properly (or, rather, at all). + + + +`LISTING 6.40: MAC.DOSFM.ASM Heading Source` + +```assembly +* +*``````````````````````````````* +* MAC.DOSFM.ASM * +* * +* THIS FILE CONTAINS MACROS * +* USED TO INTERFACE TO THE DOS * +* FILE MANAGER. NOTE THE * +* FOLLOWING: * +* * +* 1) THIS COLLECTION CURRENTLY * +* HAS A LOT OF OVERHEAD DUE TO * +* THE TIGHTLY INTEGRATED * +* NATURE OF THE FILE MANAGER. * +* * +* 2) THIS COLLECTION IS THE * +* ONLY ONE IN THE LIBRARY THAT * +* ALLOWS FOR USING THE MACROS * +* WITHIN THE SUBROUTINES, SAVE * +* FOR THE REQUIRED COLLECTION. * +* THIS IS TO MAKE EACH OF THE * +* SUBROUTINES MORE LEGIBLE, AS * +* JUST THE ACT OF PASSING * +* VARIABLES TO OTHER ROUTINES * +* CAN TAKE UP A SIGNIFICANT * +* NUMBER OF BYTES. * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 20-APR-2021 * +* ASSEMBLER: MERLIN 8 PRO * +* OS: DOS 3.3 * +* * +* SUBROUTINE FILES USED * +* * +* * +* LIST OF MACROS * +* * +* FCAT : CATALOG * +* FULCK : UNLOCK FILE * +* FLOCK : LOCK FILE * +* FDEL : DELETE FILE * +* FVFY : VERIFY FILE * +* FCLOS : CLOSE FILE * +* FRENM : RENAME FILE * +* FOPEN : OPEN FILE * +* BLOAD : LOAD BINARY FILE * +* BSAVE : SAVE BINARY FILE * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +``` + + + +--- + + + +### THE FCAT MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | -------------------------------- | +| Name | `FCAT` | +| Type | Macro | +| File | `MAC.DOSFM.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Catalog the disk. | +| Input | ]1 = Slot
]2 = Drive | +| Output | A disk catalog to the screen. | +| Dependencies | `_FMFIL` `_FMPGET` `_BUFFCLEAR` | +| Flags Destroyed | NZCV | +| Cycles | 657+ | +| Bytes | 348 | +| Notes | none | +| See Also | none | + +--- + +*DETAILS* + +The `FCAT` macro simply requests a disk catalog to be printed to the text screen. This macro does not provide any way to manipulate the data present in the **Volume Table of Contents (VTOC)**; this feature will be added as a separate macro in later revisions of the collection. + + + +`LISTING 6.41: MAC.DOSFM.ASM FCAT Macro Source` + +```assembly +* +*``````````````````````````````* +* FCAT (NATHAN RIGGS) * +* * +* SIMPLY LIST THE DISK CATALOG * +* TO THE SCREEN. * +* * +* PARAMETERS * +* * +* ]1 = SLOT * +* ]2 = DRIVE * +* * +* CYCLES: 657+ * +* SIZE: 348 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +FCAT MAC +* + FMFIL #]FC_CAT;]1;]2;#0;#0 ; {603C303B} SET CMD SLOT DRIVE +* + LDX #1 ; {3C2B} DON'T CREATE NEW FILE + JSR ]FMRUN ; {6C3B} RUN THE FILE MANAGER + LDY #]P_RETC ; {3C2B} SET INDEX TO RETURN CODE + JSR _FMPGET ; {32C17B} GET RETURN CODE FROM PARAMS + TAX ; {2C1B} HOLD CODE IN .X + JSR _BUFFCLEAR ; {37C20B} CLEAR THE FILE BUFF FOR USE + <<< +* +``` + + + +--- + + + +### THE FULCK MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------------------------------- | +| Name | `FULCK` | +| Type | Macro | +| File | `MAC.DOSFM.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Unlock a file. | +| Input | ]1 = Filename address
]2 = Slot
]3 = Drive | +| Output | none. | +| Dependencies | `_FMFIL` `FMNAM` `_FMPGET` `_BUFFCLEAR` | +| Flags Destroyed | NZCV | +| Cycles | 629+ | +| Bytes | 353 | +| Notes | none | +| See Also | `FLOCK` | + +--- + +*DETAILS* + +The `FULCK` macro unlocks a file with the specified filename. + + + +`LISTING 6.42: MAC.DOSFM.ASM FULCK Macro Source` + +```assembly +* +*``````````````````````````````* +* FULCK (NATHAN RIGGS) * +* * +* UNLOCK A FILE. * +* * +* PARAMETERS * +* * +* ]1 = FILENAME STR ADDRESS * +* ]2 = SLOT * +* ]3 = DRIVE * +* * +* CYCLES: 629+ * +* SIZE: 353 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +FULCK MAC +* + FMFIL #]FC_UNLK;]2;]3;#0;#0 ; {603C303B} +* + FMNAM ]1 ; {4C3B} SET FILENAME + LDX #1 ; {4C3B} DON'T CREATE A NEW FILE + JSR ]FMRUN ; {6C3B} RUN FILE MANAGER + LDY #]P_RETC ; {4C3B} SET INDEX TO RETURN CODE + JSR _FMPGET ; {32C17B} GET RETURN CODE FROM PARAMS + TAX ; {2C1B} HOLD IN .X + JSR _BUFFCLEAR ; {37C20B} CLEAR FILE BUFF FOR USE + <<< +* +``` + + + +--- + + + +### THE FLOCK MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------------------------------- | +| Name | `FLOCK` | +| Type | Macro | +| File | `MAC.DOSFM.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Lock a file. | +| Input | ]1 = Filename address
]2 = Slot
]3 = Drive | +| Output | none. | +| Dependencies | `_FMFIL` `FMNAM` `_FMPGET` `_BUFFCLEAR` | +| Flags Destroyed | NZCV | +| Cycles | 172+ | +| Bytes | 116 | +| Notes | none | +| See Also | `FULCK` | + +--- + +*DETAILS* + +The `FLOCK` macro locks a file with the specified filename. + + + +`LISTING 6.43: MAC.DOSFM.ASM FLOCK Macro Source` + +```assembly +* +*``````````````````````````````* +* FLOCK (NATHAN RIGGS) * +* * +* LOCK A FILE. * +* * +* PARAMETERS * +* * +* ]1 = FILENAME STR ADDRESS * +* ]2 = SLOT * +* ]3 = DRIVE * +* * +* CYCLES: 172+ * +* SIZE: 116 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +FLOCK MAC +* +* + FMFIL #]FC_LOCK;]2;]3;#0;#0 ; {603C303B} +* + FMNAM ]1 ; {117C70B} SET FILENAME + LDX #1 ; {4C3B} DON'T CREATE A NEW FILE + JSR ]FMRUN ; {6C3B} RUN FILE MANAGER + LDY #]P_RETC ; {3C2B} SET INDEX TO RETURN CODE + JSR _FMPGET ; {32C17B} GET RETURN CODE FROM PARAMS + TAX ; {2C1B} HOLD IN .X + JSR _BUFFCLEAR ; {37C20B} CLEAR FILE BUFF FOR USE + <<< +* +``` + + + +--- + + + +### THE FDEL MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------------------------------- | +| Name | `FDEL` | +| Type | Macro | +| File | `MAC.DOSFM.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Delete a file. | +| Input | ]1 = Filename address
]2 = Slot
]3 = Drive | +| Output | none. | +| Dependencies | `_FMFIL` `FMNAM` `_FMPGET` `_BUFFCLEAR` | +| Flags Destroyed | NZCV | +| Cycles | 791+ | +| Bytes | 412 | +| Notes | none | +| See Also | none | + +--- + +*DETAILS* + +The `FDEL` macro deletes a file with the specified filename. + + + +`LISTING 6.44: MAC.DOSFM.ASM FDEL Macro Source` + +```assembly +* +*``````````````````````````````* +* FDEL (NATHAN RIGGS) * +* * +* DELETE A FILE. * +* * +* PARAMETERS * +* * +* ]1 = FILENAME STR ADDRESS * +* ]2 = SLOT * +* ]3 = DRIVE * +* * +* CYCLES: 791+ * +* SIZE: 412 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +FDEL MAC +* + FMFIL #]FC_DEL;]2;]3;#0;#0 ; {603C303B} +* + FMNAM ]1 ; {117C70B} SET FILE NAME + LDX #1 ; {3C2B} DON'T CREATE NEW FILE + JSR ]FMRUN ; {6C3B} RUN FILE MANAGER + LDY #]P_RETC ; {3C2B} SET INDEX TO RETURN CODE + JSR _FMPGET ; {26C14B} GET CODE FROM PARAMS + TAX ; {2C1B} HOLD IN .X + JSR _BUFFCLEAR ; {31C17B} CLEAR FILE BUFF FOR USE + <<< +* +``` + + + +--- + + + +### THE FVFY MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------------------------------- | +| Name | `FVFY` | +| Type | Macro | +| File | `MAC.DOSFM.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Verify a file. | +| Input | ]1 = Filename address
]2 = Slot
]3 = Drive | +| Output | none. | +| Dependencies | `_FMFIL` `FMNAM` `_FMPGET` `_BUFFCLEAR` | +| Flags Destroyed | NZCV | +| Cycles | 791+ | +| Bytes | 411 | +| Notes | none | +| See Also | none | + +--- + +*DETAILS* + +The `FVFY` macro verifies a file with the specified filename. + + + +`LISTING 6.45: MAC.DOSFM.ASM FVFY Macro Source` + +```assembly +* +*``````````````````````````````* +* FVFY (NATHAN RIGGS) * +* * +* VERIFY A FILE. * +* * +* PARAMETERS * +* * +* ]1 = FILENAME STR ADDRESS * +* ]2 = SLOT * +* ]3 = DRIVE * +* * +* CYCLES: 791+ * +* SIZE: 411 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +FVFY MAC +* + FMFIL #]FC_VFY;]2;]3;#0;#0 ; {603C303B} +* + FMNAM ]1 ; {117C70B} SET FILE NAME + LDX #1 ; {3C2B} DON'T CREATE A NEW FILE + JSR ]FMRUN ; {6C3B} RUN THE FILE MANAGER + LDY #]P_RETC ; {3C2B} SET INDEX TO RETURN CODE + JSR _FMPGET ; {26C14B} GET CODE FROM PARAMETERS + TAX ; {2C1B} HOLD IN .X + JSR _BUFFCLEAR ; {31C17B} CLEAR FILE BUFF FOR USE + <<< +* +``` + + + +--- + + + +### THE FCLOS MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------------------------------- | +| Name | `FCLOS` | +| Type | Macro | +| File | `MAC.DOSFM.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Close a file. | +| Input | ]1 = Filename address
]2 = Slot
]3 = Drive | +| Output | none. | +| Dependencies | `_FMFIL` `FMNAM` `_FMPGET` `_BUFFCLEAR` | +| Flags Destroyed | NZCV | +| Cycles | 797+ | +| Bytes | 415 | +| Notes | none | +| See Also | none | + +--- + +*DETAILS* + +The `FCLOS` macro closes a file with the specified filename. + + + +`LISTING 6.46: MAC.DOSFM.ASM FCLOS Macro Source` + +```assembly +* +*``````````````````````````````* +* FCLOS (NATHAN RIGGS) * +* * +* CLOSE A FILE. * +* * +* PARAMETERS * +* * +* ]1 = FILENAME STR ADDRESS * +* ]2 = SLOT * +* ]3 = DRIVE * +* * +* CYCLES: 797+ * +* SIZE: 415 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +FCLOS MAC +* + FMFIL #]FC_CLOS;]2;]3;#0;#0 ; {603C303B} +* + FMNAM ]1 ; {117C70B} SET FILENAME + LDX #1 ; {3C2B} DON'T CREATE NEW FILE + JSR ]FMRUN ; {6C3B} RUN FILE MANAGER + LDY #]P_RETC ; {3C2B} SET INDEX TO RETURN CODE + JSR _FMPGET ; {32C17B} GET RETURN CODE + TAX ; {2C1B} AND HOLD IN .X + JSR _BUFFCLEAR ; {31C17B} CLEAR FILE BUFF FOR USE + <<< +* +``` + + + +--- + + + +### THE FRENM MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `FRENM` | +| Type | Macro | +| File | `MAC.DOSFM.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Rename a file. | +| Input | ]1 = Filename address
]2 = New filename address
]3 = Slot
]4 = Drive | +| Output | none. | +| Dependencies | `_FMFIL` `FMNAM` `_FMPGET` `_BUFFCLEAR` `_FMNM2` | +| Flags Destroyed | NZCV | +| Cycles | 927+ | +| Bytes | 578 | +| Notes | none | +| See Also | none | + +--- + +*DETAILS* + +The `FRENM` macro renames a file with the filename given in the first parameter to the filename given in the second parameter. + + + +`LISTING 6.47: MAC.DOSFM.ASM FRENM Macro Source` + +```assembly +* +*``````````````````````````````* +* FRENM (NATHAN RIGGS) * +* * +* RENAME A FILE. * +* * +* PARAMETER * +* * +* ]1 = FILENAME STR ADDRESS * +* ]2 = NEW NAME STR ADDRESS * +* ]3 = SLOT * +* ]4 = DRIVE * +* * +* CYCLES: 927+ * +* SIZE: 578 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +FRENM MAC +* + FMFIL #]FC_REN;]3;]4;#0;#0 ; {603C303B} +* + FMNAM ]1 ; {117C70B} SET FILENAME + _AXLIT ]2 ; {8C6B} + JSR _NAMESTR2 ; {64C42B} COPY TO 2ND FILENAME BUFF + JSR _FMNM2 ; {64C42B} SET ADDRESS IN PARAMS + LDX #1 ; {3C2B} DON'T CREATE NEW FILE + JSR ]FMRUN ; {6C3B} RUN FILE MANAGER + LDY #]P_RETC ; {3C2B} SET INDEX TO RETURN CODE + JSR _FMPGET ; {26C14B} GET RETURN CODE + TAX ; {2C1B} AND HOLD IN .X + JSR _BUFFCLEAR ; {31C17B} CLEAR FILE BUFFER FOR USE + <<< +* +``` + + + +--- + + + +### THE FOPEN MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `FOPEN` | +| Type | Macro | +| File | `MAC.DOSFM.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Open a file. | +| Input | ]1 = Filename address
]2 = Slot
]3 = Drive
]4 = Volume
]5 = Record
]6 = File type | +| Output | none. | +| Dependencies | `_FMFIL` `FMNAM` `_FMPGET` `_BUFFCLEAR` `_FMPSET` | +| Flags Destroyed | NZCV | +| Cycles | 828+ | +| Bytes | 433 | +| Notes | none | +| See Also | `FCLOS` | + +--- + +*DETAILS* + +The `FOPEN` macro opens a file for reading or writing, which is determined by the `FRDB`, `FWRTB`, `FRDR` and `FWRTR` macros. Beyond opening the file for use, `FOPEN` determines the type of file that will be read or written to. The following table describes which type of file corresponds to the value passed in parameter number six, along with a variable name to be used in place of a number for ease of use: + + + +| Value | Variable Name | File Type | +|----------|-----------------------|--------------| +|$00| TYP_TXT | Text File | +|$01| TYP_INT | Integer BASIC File | +|$02| TYP_APP | Applesoft BASIC File | +|$04| TYP_BIN | Binary File | +|$08| TYP_REL | Relocatable File | +|$10| TYP_S | S Type File | +|$20| TYP_A | A Type File | +|$40| TYP_B | B Type File | + + + +`LISTING 6.48: MAC.DOSFM.ASM FOPEN Macro Source` + +```assembly +* +*``````````````````````````````* +* FOPEN (NATHAN RIGGS) * +* * +* OPEN FILE FOR READ OR WRITE. * +* * +* PARAMETERS * +* * +* ]1 = FILENAME STR ADDRESS * +* ]2 = SLOT * +* ]3 = DRIVE * +* ]4 = VOLUME (0 FOR ANY) * +* ]5 = RECORD NUMBER * +* ]6 = FILE TYPE * +* * +* CYCLES: 828+ * +* SIZE: 433 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +FOPEN MAC +* + FMFIL #]FC_OPEN;]2;]3;]4;]5 ; {603C303B} +* + LDA ]6 ; {4C3B} FILE TYPE IF NEW FILE + LDY #]P_TYPE ; {3C2B} SET TYPE FOR WRITING + JSR _FMPSET ; {30C16B} + FMNAM ]1 ; {117C70B} SET THE FILENAME + LDX #0 ; {3C2B} NEW FILE ON OPEN + JSR ]FMRUN ; {6C3B} RUN FILE MANAGER + LDY #]P_RETC ; {3C2B} SET INDEX TO RETURN CODE OFFSET + JSR _FMPGET ; {26C14B} GET RETURN CODE + TAX ; {2C1B} HOLD IT IN .X + JSR _BUFFCLEAR ; {31C17B} CLEAR FILE BUFFER FOR USE + <<< +* +``` + + + +--- + + + +### THE BLOAD MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `BLOAD` | +| Type | Macro | +| File | `MAC.DOSFM.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Binary-load a file. | +| Input | ]1 = Filename address
]2 = Loading address
]3 = Slot
]4 = Drive
]5 = Volume | +| Output | none. | +| Dependencies | `FBLOAD` | +| Flags Destroyed | NZCV | +| Cycles | 3768+ | +| Bytes | 2735 | +| Notes | none | +| See Also | `BSAVE` `FBSAVE` `FBLOAD` | + +--- + +*DETAILS* + +The `BLOAD` macro loads the contents of a binary file into memory either at a specified address or, if the address passed is **#$0000**, at the address stored in the start of the file. Like with all properly saved binary file, both the default address and the file byte-length is stored in the first four bytes of the file; these are not copied to the specified location. The length is passed back to the calling program via the **.A** register (low) and **.X** register (high). + + + +`LISTING 6.49: MAC.DOSFM.ASM BLOAD Macro Source` + +```assembly +* +*``````````````````````````````* +* BLOAD (NATHAN RIGGS) * +* * +* LOAD A MACHINE LANGUAGE FILE * +* INTO MEMORY AT A SPECIFIED * +* ADDRESS. IF THE ADDRESS IS * +* #0000, THEN USE THE DEFAULT * +* ADDRESS PROVIDED AT THE * +* START OF THE FILE. THE FILE * +* LENGTH IS ALSO HELD AT THE * +* BEGINNING OF THE FILE, SO * +* THERE IS NO NEED TO PASS IT * +* HERE. THIS LENGTH IS PASSED * +* BACK TO THE USER VIA THE * +* .A (LOW) AND .X (HIGH) * +* REGISTERS. * +* * +* PARAMETERS * +* * +* ]1 = FILENAME STRING ADDR * +* ]2 = LOADING ADDRESS * +* ]3 = SLOT * +* ]4 = DRIVE * +* ]5 = VOLUME * +* * +* DESTROYS: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 3768+ * +* SIZE: 2735 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +BLOAD MAC + _MLIT ]1;WPAR1 ; {16C12B} PARSE STRING ADDRESS + _MLIT ]2;WPAR2 ; {16C12B} PARSE LOAD ADDRESS + LDA ]3 ; {4C3B} LOAD SLOT + STA BPAR1 ; {3C2B} AND STORE ON ZERO + LDA ]4 ; {4C3B} LOAD DRIVE + STA BPAR2 ; {3C2B} AND STORE ON ZERO + LDA ]5 ; {4C3B} LOAD VOLUME + STA BPAR3 ; {3C2B} AND STORE ON ZERO + JSR FBLOAD ; {3715C2696B} RUN FBLOAD SUBROUTINE + <<< +* +``` + + + +--- + + + +### THE FBLOAD SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `FBLOAD` | +| Type | Subroutine | +| File | `SUB.FBLOAD.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Binary-load a file. | +| Input | WPAR1 = Filename string address
WPAR2 = Loading Address
BPAR1 = Slot
BPAR2 = Drive
BPAR3 = Volume | +| Output | none. | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 3715+ | +| Bytes | 2696 | +| Notes | none | +| See Also | `BSAVE` `FBSAVE` `BLOAD` | + +--- + +*DETAILS* + +The `FBLOAD` subroutine loads a binary file into the designated memory address, with the length being determined by a preceding length-byte in the file. If the memory address passed is **#$0000**, then the default address found in the first two bytes of the file is used in place of the address passed. These first four bytes of the file, signifying the default memory address and the length of the range in bytes, are not copied along with the rest of the file. + +The length of the data is store in **.A** (low byte) and **.X** (high byte), as well as in `RETURN`. `RETLEN` carries **#$02**. + + + +`LISTING 6.50: SUB.FBLOAD.ASM Source` + +```assembly +* +*``````````````````````````````* +* FBLOAD (NATHAN RIGGS) * +* * +* BLOAD A FILE'S MACHINE * +* LANGUAGE CONTENTS TO A GIVEN * +* MEMORY RANGE. NOTE THAT THE * +* FIRST FOUR BYTES OF A BIN * +* FILE CONTAINS THE LOADING * +* ADDRESS (TWO BYTES) AND THE * +* LENGTH OF THE CODE (TWO * +* BYTES). IF THE ADDRESS * +* EQUALS #0000, THEN THESE * +* VALUES WILL BE USED INSTEAD. * +* * +* INPUT * +* * +* WPAR1 = FILENAME STR ADDR * +* WPAR2 = LOAD ADDRESS * +* BPAR1 = SLOT * +* BPAR2 = DRIVE * +* BPAR3 = VOLUME * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 3715+ * +* SIZE: 2696 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]FNSTR EQU WPAR1 ; FILENAME STRING +]LDADDR EQU WPAR2 ; LOADING ADDRESS +]FLEN EQU WPAR3 ; FILE LENGTH +]SLOT EQU BPAR1 +]DRIVE EQU BPAR2 +]VOL EQU BPAR3 +]FLDADDR EQU ADDR4 ; ADDRESS STORED IN FILE +* +FBLOAD +* +** FIRST, READ FIRST TWO BYTES TO GET THE +** DEFAULT ADDRESS. IF THE ADDRESS PASSED IS +** #0000, THEN THE DEFAULT ADDRESS IS USED INSTEAD. +* + FOPEN ]FNSTR;]SLOT;]DRIVE;]VOL;#0;#]TYP_BIN ; {828C433B} + FRDB ]SLOT;]DRIVE;#0;#0;#0;#0 ; {700C357B} + STA ]FLDADDR ; {4C3B} + FRDB ]SLOT;]DRIVE;#0;#0;#1;#0 ; {700C357B} + STA ]FLDADDR+1 ; {4C3B} +* +** NOW GET THE LENGTH OF THE FILE. THEORETICALLY, +** THIS SHOULDN'T INCLUDE THE FIRST 4 BYTES? +* + FRDB ]SLOT;]DRIVE;#0;#0;#2;#0 ; {700C357B} + STA ]FLEN ; {4C3B} + FRDB ]SLOT;]DRIVE;#0;#0;#3;#0 ; {700C357B} + STA ]FLEN+1 ; {4C3B} +* +** IF PASSED ADDRESS IS ZERO, THEN USE THE +** DEFAULT ADDRESS. OTHERWISE, COPY THE PASSED +** ADDRESS INTO THE DEFAULT ADDRESS VARIABLE. +* + LDA ]LDADDR ; {4C3B} + CMP #0 ; {3C2B} + BNE :SET ; {3C2B} + LDA ]LDADDR+1 ; {4C3B} + CMP #0 ; {3C2B} + BNE :SET ; {3C2B} + JMP :SKIP ; {633B} +:SET + LDA ]LDADDR ; {4C3B} + STA ]FLDADDR ; {4C3B} + LDA ]LDADDR+1 ; {4C3B} + STA ]FLDADDR+1 ; {4C3B} +* +** NOW READ THE RANGE OF THE ENTIRE FILE AND +** STORE IT IN MEMORY AT THE APPROPRIATE LOCATION. +* +:SKIP + FRDR ]SLOT;]DRIVE;#0;#0;#4;]FLDADDR;]FLEN ; {697C361B} + FCLOS ]FNSTR;]SLOT;]DRIVE ; {797C415B} +* + LDA ]FLEN ; {4C3B} LOAD LENGTH LOW BYTE + STA RETURN ; {4C3B} AND HOLD IN .A AND RETURN + LDX ]FLEN+1 ; {4C3B} LOAD LENGTH HIGH BYTE + STA RETURN+1 ; {4C3B} AND HOLD IN .X AND RETURN+1 + LDY #2 ; {3C2B} LOAD BYTE LENGTH OF RETURN + STY RETLEN ; {4C3B} AND STORE IN RETLEN + RTS ; {6C1B} +* +``` + + + +--- + + + +### THE BSAVE MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `BSAVE` | +| Type | Macro | +| File | `MAC.DOSFM.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Binary-save a range of memory. | +| Input | ]1 = Filename address
]2 = Loading address
]3 = Range length
]4 = Slot
]5 = Drive
]6 = Volume | +| Output | none. | +| Dependencies | `FBSAVE` | +| Flags Destroyed | NZCV | +| Cycles | 5300+ | +| Bytes | 2758 | +| Notes | none | +| See Also | `BLOAD` `FBSAVE` `FBLOAD` | + +--- + +*DETAILS* + +The `BSAVE` macro stores a range of memory from a starting address to a given byte-length in a binary file, holding the starting address in the first two bytes of the file and the range length in the third and fourth bytes. This is how all binary files are stored in DOS, or should be stored. + + + +`LISTING 6.51: MAC.DOSFM.ASM BSAVE Macro Source` + +```assembly +* +*``````````````````````````````* +* BSAVE (NATHAN RIGGS) * +* * +* STORES A MEMORY RANGE IN A * +* FILE STARTING AT THE LOADING * +* ADDRESS AND CONTINUING FOR * +* THE GIVEN RANGE LENGTH. THE * +* FIRST TWO BYTES OF THE FILE * +* HOLD THE LOADING ADDRESS, * +* AND THE 3RD AND 4TH BYTES * +* HOLD THE RANGE LENGTH. * +* * +* PARAMETERS * +* * +* ]1 = FILENAME STRING ADDR * +* ]2 = LOADING ADDRESS * +* ]3 = RANGE LENGTH * +* ]4 = SLOT * +* ]5 = DRIVE * +* ]6 = VOLUME * +* * +* DESTROYS: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 5300+ * +* SIZE: 2758 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +BSAVE MAC + _MLIT ]1;WPAR1 ; {16C12B} PARSE STRING ADDRESS + _MLIT ]2;WPAR2 ; {16C12B} PARSE LOAD ADDRESS + _MLIT ]3;WPAR3 ; {16C12B} + LDA ]4 ; {4C3B} LOAD SLOT + STA BPAR1 ; {3C2B} AND STORE ON ZERO + LDA ]5 ; {4C3B} LOAD DRIVE + STA BPAR2 ; {3C2B} AND STORE ON ZERO + LDA ]6 ; {4C3B} LOAD VOLUME + STA BPAR3 ; {3C2B} AND STORE ON ZERO + JSR FBSAVE ; {5231C2707B} RUN FBLOAD SUBROUTINE + <<< +* +``` + + + +--- + + + +### THE FBSAVE SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `FBSAVE` | +| Type | Subroutine | +| File | `SUB.FBSAVE.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Binary-save a file. | +| Input | WPAR1 = Filename string address
WPAR2 = Loading Address
WPAR3 = Range length
BPAR1 = Slot
BPAR2 = Drive
BPAR3 = Volume | +| Output | none. | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 3715+ | +| Bytes | 2696 | +| Notes | none | +| See Also | `BSAVE` `FBLOAD` `BLOAD` | + +--- + +*DETAILS* + +The `FBSAVE` subroutine saves a memory range to a binary file that starts at a given address. The first two bytes of the file holds the starting address, while the next two bytes hold the range length. + + + +`LISTING 6.52: SUB.FBSAVE.ASM Source` + +```assembly +* +*``````````````````````````````* +* FBSAVE (NATHAN RIGGS) * +* * +* THIS SUBROUTINE TAKES AN * +* ADDRESS AND A BYTE LENGTH, * +* THEN SAVES THAT MEMORY RANGE * +* TO A FILE. THE FIRST TWO * +* BYTES HOLD THE ADDRESS WHERE * +* THE MEMORY WAS STORED, AND * +* THE THIRD AND FOURTH BYTES * +* OF THE FILE HOLD THE LENGTH * +* OF THE FILE (NOT INCLUDING * +* THE FOUR PRECEDING BYTES). * +* * +* INPUT * +* * +* WPAR1 = FILENAME STR ADDR * +* WPAR2 = LOAD ADDRESS * +* WPAR3 = RANGE LENGTH * +* BPAR1 = SLOT * +* BPAR2 = DRIVE * +* BPAR3 = VOLUME * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 5231+ * +* SIZE: 2707 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]FNSTR EQU WPAR1 +]LDADDR EQU WPAR2 +]FLEN EQU WPAR3 +]SLOT EQU BPAR1 +]DRIVE EQU BPAR2 +]VOL EQU BPAR3 +* +FBSAVE +* +** FIRST, OPEN THE FILE FOR BINARY WRITING +* + FOPEN ]FNSTR;]SLOT;]DRIVE;]VOL;#0;#]TYP_BIN ; {828C433B} +* +** NEXT, STORE THE STARTING ADDRESS OF THE +** MEMORY RANGE AT THE FIRST TWO BYTES OF THE FILE. +* + FWRTB ]SLOT;]DRIVE;]VOL;#0;#0;]LDADDR ; {665C337B} + FWRTB ]SLOT;]DRIVE;]VOL;#0;#1;]LDADDR+1 ; {665C337B} +* +** NOW STORE THE LENGTH IN THE THIRD AND FOURTH +** BYTES OF THE FILE. +* + FWRTB ]SLOT;]DRIVE;]VOL;#0;#2;]FLEN ; {665C337B} + FWRTB ]SLOT;]DRIVE;]VOL;#0;#3;]FLEN+1 ; {665C337B} +* +** THEN STORE THE MEMORY RANGE IN THE FILE, OFFSETTING +** BY FOUR TO ACCOUNT FOR THE PRECEDING ADDRESS AND +** LENGTH BYTES. +* + FWRTR ]SLOT;]DRIVE;]VOL;#0;#4;]LDADDR;]FLEN ; {940C509B} +* +** CLOSE FILE, RETURN TO CALLER +* + FCLOS ]FNSTR;]SLOT;]DRIVE {797C416B} + RTS ; {6C1B} + +``` + + + +--- + + + +## Miscellaneous DOS Macros + + + +The MAC.DOSMORE.ASM file contains macros that are primarily concerned with DOS functions but are not required and do not fit neatly in with file management. This includes macros for checking the DOS version, verifying if DOS or Applesoft is loaded, and macros for telling if an Applesoft or Integer BASIC program is running. + + + +`LISTING 6.60: MAC.DOSMORE.ASM Heading` + +```assembly +* +*``````````````````````````````* +* MAC.DOSMORE.ASM * +* * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 20-APR-2021 * +* ASSEMBLER: MERLIN 8 PRO * +* OS: DOS 3.3 * +* * +* SUBROUTINE FILES USED * +* * +* NONE * +* * +* LIST OF MACROS * +* * +* DVER : CHECK DOS VERSION * +* DOSIN : CHECK IF DOS LOADED * +* ABAS : IS APPLESOFT LOADED * +* IBEX : IS INTEGER BASIC * +* PROGRAM RUNNING * +* ABEX : IS APPLESOFT BASIC * +* PROGRAM RUNNING * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +``` + + + +--- + + + +### THE DVER MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | --------------------- | +| Name | `DVER` | +| Type | Macro | +| File | `MAC.DOSMORE.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Check the DOS version | +| Input | none | +| Output | none. | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 28+ | +| Bytes | 18 | +| Notes | none | +| See Also | none | + +--- + +*DETAILS* + +The `DVER` macro checks the current version of DOS in memory and returns the version number in the **.A** register. + + + +`LISTING 6.61: MAC.DOSMORE.ASM DVER Macro Source` + +```assembly +* +*``````````````````````````````* +* DVER (NATHAN RIGGS) * +* * +* RETURN THE DOS VERSION. THIS * +* IS ADAPTED FROM DON WORTH'S * +* AND PIETER LECHNER'S * +* /BENEATH APPLE DOS/. THE * +* LICENSE MAY ACCORDINGLY * +* VARY. * +* * +* PARAMETERS * +* * +* NONE * +* * +* CYCLES: 28 * +* SIZE: 18 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +DVER MAC + CLC ; {2C1B} CLEAR CARRY FLAG + LDA #0 ; {3C2B} CLEAR ACCUMULATOR + ADC #$BE ; {2C2B} ADD #$16BE TO DOS LOAD POINT + STA ADDR1 ; {3C2B} STORE LOW BYTE + LDA $3D2 ; {4C3B} LOAD DOS LOADING POINT + ADC #$16 ; {2C2B} ADD OFFSET + STA ADDR1+1 ; {3C2B} STORE HIGH BYTE + LDY #0 ; {3C2B} SET INDEX TO 0 + LDA (ADDR1),Y ; {6C2B} RETURNS #2 OR #3 FOR + ; THE DOS VERSION IN .A + <<< +* +``` + + + +--- + + + +### THE DOSIN MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------- | +| Name | `DOSIN` | +| Type | Macro | +| File | `MAC.DOSMORE.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Check if DOS is loaded | +| Input | none | +| Output | none. | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 17+ | +| Bytes | 12 | +| Notes | none | +| See Also | none | + +--- + +*DETAILS* + +The `DOSIN` macro checks if DOS is currently loaded into memory. The **CARRY** flag is set if DOS is loaded, and it is cleared if DOS is not loaded. + + + +`LISTING 6.62: MAC.DOSMORE.ASM DOSIN Macro Source` + +```assembly +* +*``````````````````````````````* +* DOSIN * +* * +* CHECKS IF DOS IS LOADED. * +* THIS SHOULD BE CALLED BEFORE * +* RUNNING RWTS OR THE FILE * +* MANAGER. * +* * +* THIS IS ADAPTED FROM DON * +* WORTH'S AND PIETER LECHNER'S * +* /BENEATH APPLE DOS/, SO THE * +* LICENSE MAY VARY ACCORDINGLY * +* * +* PARAMETERS * +* * +* NONE * +* * +* CYCLES: 17 * +* SIZE: 12 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +DOSIN MAC + LDA $3D0 ; {4C3B} GET VECTOR JUMP + CMP #$4C ; {3C2B} IS IT A JUMP? + BNE ]NODOS ; {3C2B} NOPE, JUMP TO NONE + SEC ; {2C1B} CARRY SET, MEANING DOS LOADED + JMP ]EXIT ; {3C3B} +]NODOS + CLC ; {2C1B} CARRY CLEAR, MEANING NO DOS +]EXIT + <<< +* +``` + + + +--- + + + +### THE ABAS MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------- | +| Name | `ABAS` | +| Type | Macro | +| File | `MAC.DOSMORE.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Find out which BASIC system is loaded | +| Input | none | +| Output | none. | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 40+ | +| Bytes | 29 | +| Notes | none | +| See Also | none | + +--- + +*DETAILS* + +The `ABAS` macro determines whether it is Applesoft BASIC or Integer BASIC that is currently loaded. If Applesoft BASIC is loaded, then the **CARRY** is set; if it is Integer BASIC loaded, then the **CARRY** is cleared. + + + +`LISTING 6.63: MAC.DOSMORE.ASM ABAS Macro Source` + +```assembly +* +*``````````````````````````````* +* ABAS * +* * +* DETERMINE WHICH BASIC IS * +* LOADED. IF CARRY IS CLEAR, * +* THEN INTEGER BASIC IS * +* LOADED--ELSE, APPLESOFT IS. * +* * +* THIS IS ADAPTED FROM DON * +* WORTH'S AND PIETER LECHNER'S * +* /BENEATH APPLE DOS/, SO THE * +* LICENSE MAY VARY ACCORDINGLY * +* * +* PARAMETERS * +* * +* NONE * +* * +* CYCLES: 40 * +* SIZE: 29 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +ABAS MAC + LDA #$4C ; {4C3B} CODE FOR APPLESOFT + ; #$20 IS FOR INTEGER BASIC + CMP $E000 ; {4C3B} CORRECT BASIC ALREADY THERE? + BEQ ]APPLES ; {3C2B} YES, GOTO RETURN CARRY + STA $C080 ; {4C3B} NOW IS IT THERE? + CMP $E000 ; {4C3B} NOW IS IT THERE? + BEQ ]APPLES ; {3C2B} YEP, GOTO RETURN CARRY + STA $C081 ; {4C3B} STILL NO, TRY ROM CARD + CMP $E000 ; {4C3B} THERE NOW? + BEQ ]APPLES ; {3C2B} YEP, GOTO RETURN CARRY + ; NOPE, DON'T RETURN CARRY + CLC ; {2C1B} + JMP ]EXIT ; {3C3B} +]APPLES + SEC ; {2C1B} +]EXIT + <<< +* +``` + + + +--- + + + +### THE IBEX MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | -------------------------------------------- | +| Name | `IBEX` | +| Type | Macro | +| File | `MAC.DOSMORE.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Check if an Integer BASIC program is running | +| Input | none | +| Output | none. | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 17+ | +| Bytes | 12 | +| Notes | none | +| See Also | none | + +--- + +*DETAILS* + +The `IBEX` macro sets the **CARRY** flag if an Integer BASIC program is currently running; otherwise, the **CARRY** is cleared. + + + +`LISTING 6.64: MAC.DOSMORE.ASM IBEX Macro Source` + +```assembly +* +*``````````````````````````````* +* IBEX * +* * +* CHECK IF AN INTEGER BASIC * +* PROGRAM IS RUNNING. * +* * +* THIS IS ADAPTED FROM DON * +* WORTH'S AND PIETER LECHNER'S * +* /BENEATH APPLE DOS/, SO THE * +* LICENSE MAY VARY ACCORDINGLY * +* * +* PARAMETERS * +* * +* NONE * +* * +* CYCLES: 17 * +* SIZE: 12 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +IBEX MAC + LDA $D9 ; {4C3B} CHECK BASIC + BMI ]YES ; {3C2B} IF NEG, THEN INT BASIC RUNNING + BPL ]NO ; {3C2B} IF POSITIVE, THEN NOT +]YES + SEC ; {2C1B} INT BASIC RUNNING, SET CARRY + JMP ]EXIT ; {3C3B} AND EXIT +]NO + CLC ; {2C1B} NOT RUNNING, SO CLEAR CARRY +]EXIT ; AND EXIT + <<< +* +``` + + + +--- + + + +### THE ABEX MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------------------------- | +| Name | `ABEX` | +| Type | Macro | +| File | `MAC.DOSMORE.ASM` | +| Author | Nathan Riggs | +| Last Revision | 07-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Check if an Applesoft BASIC program is running | +| Input | none | +| Output | none. | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 26+ | +| Bytes | 18 | +| Notes | none | +| See Also | none | + +--- + +*DETAILS* + +The `ABEX` macro sets the **CARRY** flag if an Applesoft BASIC program is currently running; otherwise, the **CARRY** is cleared. + + + +`LISTING 6.65: MAC.DOSMORE.ASM ABEX Macro Source` + +```assembly +* +*``````````````````````````````* +* ABEX * +* * +* CHECK IF AN APPLESOFT BASIC * +* PROGRAM IS RUNNING. * +* * +* THIS IS ADAPTED FROM DON * +* WORTH'S AND PIETER LECHNER'S * +* /BENEATH APPLE DOS/, SO THE * +* LICENSE MAY VARY ACCORDINGLY * +* * +* PARAMETERS * +* * +* NONE * +* * +* CYCLES: 26 * +* SIZE: 18 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +ABEX MAC + LDX $76 ; {4C3B} GET LINE NUMBER + INX ; {2C1B} + BEQ ]NO ; {3C2B} + LDX $33 ; {4C3B} GET PROMPT CHARACTER + CPX #$DD ; {3C2B} IS PROMPT A "]" ? + BEQ ]NO ; {3C2B} YES, SO NOT EXECUTING + SEC ; {2C1B} ELSE, IS EXECUTING--SET CARRY + JMP ]EXIT ; {3C3B} +]NO + CLC ; {2C1B} +]EXIT + <<< +* +``` + + + +--- + + + +## Part II: DOS Collection Demos + + + +This section lists two demo files, DEMO.DOSFM.ASM and DEMO.DOSMORE.ASM, that illustrate how to use the various macros in the DOS collection. This only concerns the macros found in the MAC.DOSFM.ASM file and the MAC.DOSMORE.ASM file, as the macros in DOS.DOSREQ.ASM are meant for internal use only. + + + +`Listing 6.70: DEMO.DOSFM.ASM Source` + +```assembly +* +*``````````````````````````````* +* DEMO.DOSFM.ASM * +* * +* A DEMO FILE FOR THE DOS * +* FILE MANAGER MACROS. * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 21-APR-2021 * +* ASSEMBLER: MERLIN 8 PRO * +* OS: DOS 3.3 * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** ASSEMBLER DIRECTIVES +* + CYC AVE + EXP OFF + TR ON + DSK DEMO.DOSFM + OBJ $BFE0 + ORG $6000 +* +*``````````````````````````````* +* TOP INCLUDES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* + PUT MIN.HEAD.REQUIRED.ASM + USE MIN.MAC.REQUIRED.ASM + USE MIN.MAC.DOSREQ.ASM + USE MIN.MAC.DOSFM.ASM + PUT MIN.HEAD.DOS.ASM +* +]HOME EQU $FC58 +]ZSLOT EQU $6 +]ZDRIVE EQU $2 +* +*``````````````````````````````* +* PROGRAM MAIN BODY * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +*``````````````````````````````* +* DOS FILE MANAGER MACROS * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE FOLLOWING MACROS BEING USED ARE EXAMPLES +** OF HOW TO USE EACH MACRO IN THE FILE MANAGER +** MACRO SELECTION OF THE DOS COLLECTION OF THE +** APPLEIIASM LIBRARY. NOTE THAT OTHER DEMO FILES +** EXIST FOR OTHER DOS FUNCTIONS, SUCH AS THE +** DEMO.DOSRWTS.ASM FILE FOR RWTS USAGE AND THE +** DEMO.DOSMORE.ASM FILE FOR MISCELLANEOUS MACROS +** THAT CAN BE OF USE WHILE USING APPLE DOS. +* +*``````````````````````````````* +* THE FCAT MACRO * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE FCAT MACRO SIMPLY RUNS THE CATALOG +** DOS COMMAND, WHICH LISTS THE FILES IN A +** DISK IN THE GIVEN SLOT AND DRIVE. A MORE +** ROBUST CATALOGING MACRO IS PLANNED AS PART +** OF THE DEMO.DOSMORE.ASM SELECTION. +* + JSR ]HOME + _PRN "THE FCAT MACRO",8D + _PRN "==============",8D8D + _WAIT + FCAT #]ZSLOT;#]ZDRIVE + _WAIT +* +*``````````````````````````````* +* THE FLOCK MACRO * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE FLOCK MACRO LOCKS A GIVEN FILE AT THE +** PROVIDED SLOT AND DRIVE NUMBERS. +* + JSR ]HOME + _PRN "THE FLOCK MACRO",8D + _PRN "===============",8D8D + _PRN "FIRST, CATALOG BEFORE LOCKING:",8D8D + _WAIT + FCAT #]ZSLOT;#]ZDRIVE + _WAIT + _PRN " ",8D + _PRN "NOW LOCK A FILE AND DO ANOTHER CAT:",8D8D + FLOCK #FN5;#]ZSLOT;#]ZDRIVE + _WAIT + FCAT #]ZSLOT;#]ZDRIVE + _WAIT +* +*``````````````````````````````* +* THE FULCK MACRO * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE FULCK MACRO SIMPLY UNLOCKS A FILE IN +** THE GIVEN SLOT AND DRIVE. +* + JSR ]HOME + _PRN "THE FULCK MACRO",8D + _PRN "===============",8D8D + _PRN "FIRST, SHOW CATALOG WITH LOCKED FILE:",8D8D + _WAIT + FCAT #]ZSLOT;#]ZDRIVE + _WAIT + _PRN " ",8D + _PRN "NOW SHOW CATALOG WITH UNLOCKED FILE:",8D8D + FULCK #FN5;#]ZSLOT;#]ZDRIVE + _WAIT + FCAT #]ZSLOT;#]ZDRIVE + _WAIT +* +*``````````````````````````````* +* THE FRENM MACRO * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE FRENM MACRO RENAMES A FILE FROM A SOURCE +** FILENAME TO A DESTINATION FILENAME, BOTH IN +** THE SAME SLOT AND DRIVE. +* + JSR ]HOME + _PRN "THE FRENM MACRO",8D + _PRN "===============",8D8D + _PRN "CATALOG WITH RENAMED FILE:",8D8D + FRENM #FN5;#FN3;#]ZSLOT;#]ZDRIVE + _WAIT + FCAT #]ZSLOT;#]ZDRIVE + _PRN " ",8D + _WAIT + _PRN "CATALOG WITH FILE RENAMED BACK:",8D8D + _WAIT + FRENM #FN3;#FN5;#]ZSLOT;]ZDRIVE + FCAT #]ZSLOT;#]ZDRIVE + _WAIT +* +*``````````````````````````````* +* THE FWRTB MACRO * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE FWRTB MACRO, WHICH STANDS FOR +** (F)ILE (WR)I(T)E (B)YTE, WRITES A SINGLE BYTE +** TO A FILE ON A DISK AT A GIVEN SLOT, DRIVE AND +** VOLUME. NOTE THAT THIS CAN BE ANY FILE TYPE. +* +** OTHER THAN THE RETURN CODE, THERE IS NO OUTPUT +** BEYOND THE "A" BEING PUT INTO THE FILE. WE CANNOT +** TEST THIS UNTIL WE ALSO USE A BYTE READING FUNCTION, +** WHICH WE WILL COVER NEXT. +* +** ALSO NOTE THAT THIS MAKES USE OF THE FOPEN AND FCLOS +** MACROS. THESE WILL BE EXHIBITED AGAIN TOWARD THE END +** OF THE DEMO. +* + FOPEN #FN5;#]ZSLOT;#]ZDRIVE;#0;#0;#]TYP_TXT + FWRTB #]ZSLOT;#]ZDRIVE;#0;#0;#0;#"A" + FCLOS #FN5;#]ZSLOT;#]ZDRIVE +* +*``````````````````````````````* +* THE FRDB MACRO * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE FRDB MACRO, WHICH STANDS FOR +** (F)ILE (R)EA(D) (B)YTE, READS A BYTE FROM +** A FILE ON A DISK IN THE GIVEN SLOT, DRIVE AND +** VOLUME. +* + JSR ]HOME + _PRN "THE FRDB AND FWRTB MACROS",8D + _PRN "=========================",8D8D + _PRN "THE FIRST BYTE OF THE FILE IS: " + FOPEN #FN5;#]ZSLOT;#]ZDRIVE;#0;#0;#]TYP_TXT + FRDB #]ZSLOT;#]ZDRIVE;#0;#0;#0;#0 + JSR $FDF0 + FCLOS #FN5;#]ZSLOT;#]ZDRIVE + _WAIT +* +*``````````````````````````````* +* THE FWRTR MACRO * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE FWRTR MACRO, WHICH STANDS FOR (F)ILE +** (WR)I(T)E RANGE, WRITES A RANGE OF BYTES IN +** MEMORY TO A FILE (THE TYPE IS UP TO THE USER). +** THIS IS USED IN CONJUNCTION WITH THE FRDR MACRO +** TO READ AND WRITE MULTIPLE BYTES FROM AND TO FILES. +* +** THIS MACRO ONLY OUTPUTS TO A FILE, SAVE FOR THE +** RETURN CODE STORED IN THE .X REGISTER. THE NEXT +** MACRO'S EXPLANATION WILL INCLUDE PRINTING THE TEXT +** SAVED TO A FILE HERE. +* +** NOTE THAT WHEN WRITING A RANGE OF BYTES, THE LENGTH +** OF THE BYTES TO BE WRITTEN SHOULD BE REDUCED BY ONE. +** THIS IS DUE TO AN INCONSISTENCY IN DOS. +* + FOPEN #FN5;#]ZSLOT;#]ZDRIVE;#0;#0;#]TYP_TXT + FWRTR #]ZSLOT;#]ZDRIVE;#0;#0;#0;#RANGE+1;#24 + FCLOS #FN5;#]ZSLOT;#]ZDRIVE +* +*``````````````````````````````* +* THE FRDR MACRO * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE FRDR MACRO, WHICH STANDS FOR (F)ILE +** (R)EA(D) (R)ANGE, READS A RANGE OF BYTES +** FROM A FILE ON A DISK AT A GIVEN SLOT AND DISK, +** STORING THE DATA AT A PROVIDED ADDRESS. IT +** IS IMPORTANT TO KNOW THE SIZE OF THE FILE +** PRIOR TO READING IT, SINCE READING PAST THE +** END OF THE FILE CAN HAVE UNPREDICTABLE +** CONSEQUENCES. +* + JSR ]HOME + _PRN "THE FRDR AND FWRTR MACROS",8D + _PRN "=========================",8D8D + _PRN "THE FILE CONTAINS:",8D8D + FOPEN #FN5;#]ZSLOT;#]ZDRIVE;#0;#0;#0 + FRDR #]ZSLOT;#]ZDRIVE;#0;#0;#0;#RDRANGE;#25 + FCLOS #FN5;#]ZSLOT;#]ZDRIVE + LDY #255 +LOOP1 + INY + LDA RDRANGE,Y + JSR $FDF0 + CPY #24 + BNE LOOP1 + _WAIT +* +*``````````````````````````````* +* THE BLOAD MACRO * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE BLOAD MACRO WORKS MUCH LIKE THE BLOAD +** COMMAND IN DOS, BUT THERE IS NO BLOAD COMMAND +** FOR THE FILE MANAGER; IT HAS TO BE BUILT FROM +** THE ALREADY AVAILABLE COMMANDS. +* +** WHEN USED, THE BLOAD MACRO LOADS A BINARY FILE'S +** CONTENTS AND STORES THEM IN THE PROVIDED ADDRESS ON +** THE DISK IN THE GIVEN SLOT AND DRIVE. IF THE ADDRESS +** PASSED IS #0000, THEN THE MACRO/SUBROUTINE ASSUMES +** THAT THE DEFAULT ADDRESS WILL BE USED, WHICH IS +** STORED IN THE FIRST TWO BYTES OF THE FILE. THE LENGTH +** IS ALSO STORED AT THE BEGINNING OF THE FILE IN BYTES +** THREE AND FOUR, FOLLOWED BY THE ACTUAL DATA REQUESTED. +* +** IT SHOULD BE NOTED THAT THERE IS NO TEXT FILE +** EQUIVALENT TO BLOAD BECAUSE ANYONE WITH ANY SENSE +** WILL USE BINARY FILES TO STORE AND RETRIEVE DATA ON +** THE DISK, AS IT IS BOTH FASTER AND EASIER--TEXT +** FILES, FOR INSTANCE, DO NOT EASILY PROVIDE THE +** LENGTH OF THE FILE. IF A TEXT ROUTINE AS SUCH IS +** NEEDED, IT IS BEST TO BUILD YOUR OWN USING FWRTR, +** FRDR, FWRTB AND FRDB, AS YOU WILL LIKELY CREATE YOUR +** OWN STRUCTURE TO THE FILE. +* + JSR ]HOME + _PRN "THE BLOAD MACRO",8D + _PRN "===============",8D8D + _PRN "$300 NOW HOLDS:",8D8D + BLOAD #FN6;#$0000;#]ZSLOT;#]ZDRIVE;#0 + DUMP #$300;#$40 + _WAIT +* +*``````````````````````````````* +* THE BSAVE MACRO * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE BSAVE MACRO IS FUNCTIONALLY EQUIVALENT TO +** THE BSAVE COMMAND FROM THE DOS PROMPT. FIRST, +** THE PROVIDED ADDRESS IS STORED IN THE FIRST TWO +** BYTES OF THE FILE TO SUPPLY THE DEAULT LOADING +** ADDRESS. FOLLOWING THAT, THE LENGTH OF THE DATA +** IS STORED IN THE THIRD AND FOURTH BYTES. THE +** DATA IS THEN STORED FOLLOWING THESE BYTES. THIS +** IS HOW DOS ITSELF HANDLES BINARY FILES, AND +** YOUR BINARY FILES SHOULD ALSO FOLLOW THIS SCHEME +** TO AVOID ANGERING THE GODS. +* +** WE WON'T BE GOING THROUGH THE OUTPUT HERE, BUT IF +** YOU EXAMINE THE FILE WITH A TOOL LIKE CIDERPRESS, +** YOU WILL SEE THAT THE FILE SIMPLY CONTAINS THE +** VALUES #$01 TO #$40. BE FOREWARNED THAT YOU WILL +** NOT SEE THE FIRST FOUR BYTES, AS CIDERPRESS HIDES +** THESE TO PREVENT CONFUSION. YOU CAN VIEW THE FIRST +** FOUR BYTES BY LOOKING AT THE SECTOR THE FILE STARTS +** AT. +* +** NOTE AGAIN THAT AN EQUIVALENT MACRO OR SUBROUTINE +** IS NOT PROVIDED IN THIS COLLECTION. +* + JSR ]HOME + _PRN "THE BSAVE MACRO",8D + _PRN "===============",8D8D + _PRN "SAVING BINARY DATA..." + BSAVE #FN7;#MEM40;#$39;#6;#2;#0 + _PRN "DONE!!!",8D8D + _WAIT +* +*``````````````````````````````* +* THE FOPEN AND FCLOS MACROS * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE FOPEN MACRO MUST BE RUN BEFORE READING +** OR WRITING A FILE, AND THE FCLOS MACRO MUST +** BE USED AFTERWARDS. IF FOPEN IS NEGLECTED, THE +** FILE WILL NOT BE CREATED; IF FCLOS IS NEGLECTED, +** THEN THAT FILE BUFFER WILL REMAIN IN USE. SINCE +** WE GENERALLY LIKE TO ACTUALLY USE OUR FILES, IT'S +** GOOD PRACTICE TO USE FOPEN AND FCLOS BECAUSE +** UM.. FILE INPUT AND OUTPUT WOULD NOT HAPPEN OTHERWISE. +* +** IF A FILE DOES NOT ALREADY EXIST, FOPEN AUTOMATICALLY +** CREATES A NEW ONE WITH THE PROVIDED FILENAME. HERE, +** WE WILL CREATE A FILE CALLED "TODELETE" THAT WILL BE +** DELETED IN THE NEXT MACRO'S EXPLANATION. +* + JSR ]HOME + _PRN "FOPEN AND FCLOS",8D + _PRN "===============",8D8D + _PRN "CATALOG BEFORE FILE CREATION:",8D8D + _WAIT + FCAT #]ZSLOT;#]ZDRIVE + _WAIT + _PRN " ",8D + _PRN "CATALOG AFTER FILE CREATION:",8D8D + FOPEN #FN8;#]ZSLOT;#]ZDRIVE;#0;#0;#]TYP_TXT + FCLOS #FN8;#]ZSLOT;#]ZDRIVE + _WAIT + FCAT #]ZSLOT;#]ZDRIVE + _WAIT +* +*``````````````````````````````* +* THE FDEL MACRO * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** LASTLY (FOR GOOD REASON), THE FDEL MACRO +** DELETES A FILE WITH THE GIVEN NAME IN THE +** NOTED SLOT AND DISK DRIVE. HERE, LET US +** DELETE THE LAST FILE WE CREATED, "TODELETE". +* + JSR ]HOME + _PRN "THE FDEL MACRO",8D + _PRN "==============",8D8D + _PRN "LOOK MA, NO TODELETE FILE!",8D8D + FDEL #FN8;#]ZSLOT;#]ZDRIVE + _WAIT + FCAT #]ZSLOT;#]ZDRIVE + _WAIT +* +*``````````````````````````````* +* RWTS MACROS * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** WHILE THE RWTS ROUTINE HAS NOT BEEN ABSTRACTED +** TOO MUCH, THIS IS FOR GOOD REASON: OUR HEADER +** FILE IS GETTING TOO BIG FOR ITS BRITCHES! +* +** THE RWTS ROUTINE LETS YOU READ AND WRITE DIRECTLY +** TO A DISK, BYPASSING CONVENTIONS USED BY DOS. +** IT GOES WITHOUT SAYING THAT THIS IS DANGEROUS: THE +** SMALLEST ACCIDENT CAN DESTROY A DISK FOR GOOD, SO +** USE THIS AT YOUR OWN DISCRETION. +* +** THERE ARE TWO MAIN MACROS FOR USING RWTS: THE SRWTS +** MACRO ( (S)ET RWTS ) AND THE GRWTS MACRO. THE SRWTS +** MACRO MUST ALWAYS BE RUN BEFORE GRWTS, AS IT SETS +** THE VARIABLES NEEDED FOR RWTS TO WORK. SINCE THERE +** IS SUCH A THREAT OF DISK CORRUPTION, WE ARE ONLY +** SHOWING HOW TO USE RWTS TO READ THE DISK, NOT WRITE +** IT; HOWEVER, THE PROCESS IS THE SAME FOR BOTH. +* + JSR ]HOME + _PRN "THE RWTS MACROS",8D + _PRN "===============",8D8D + JSR ]LOCRPL + STY ]RWTSPTR + STA ]RWTSPTR+1 +* + SRWTS #]ZSLOT;#]ZDRIVE;#0;#$11;#0;#$9000;#1 + GRWTS + _PRN "HERE'S THE DISK VTOC:",8D8D + DUMP #$9000;#$F + DUMP #$9010;#$F + DUMP #$9020;#$F + DUMP #$9030;#$F + DUMP #$9040;#$F + DUMP #$9050;#$F + DUMP #$9060;#$F + DUMP #$9070;#$F + _PRN " ",8D + _PRN ".....",8D + _WAIT +* + JSR ]HOME + _PRN "FIN!",8D8D +* +EXIT + JMP $3D0 +* + PUT MIN.SUB.FBLOAD.ASM + PUT SUB.FBSAVE.ASM + PUT MIN.LIB.REQUIRED.ASM +* +FN3 STR "RENAMED" +FN5 STR "WRITERANGE" +FN6 STR "BINLOAD" +FN7 STR "BINSAVE" +FN8 STR "TODELETE" +MEM40 HEX 000102030405060708091A1B1C1D1E1F + HEX 101112131415161718191A1B1C1D1E1F + HEX 202122232425262728292A2B2C2D2E2F + HEX 303132333435363738393A3B3C3D3E3F +RANGE STR "ONE RING TO RULE THEM ALL" +RDRANGE DS 25 +* +``` + + + +`Listing 6.71: DEMO.DOSMORE.ASM Source` + +```assembly +* +*``````````````````````````````* +* DEMO.DOSMORE.ASM * +* * +* A DEMO FILE FOR VARIOUS DOS * +* MACROS AND SUBROUTINES. * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 07-MAY-2021 * +* ASSEMBLER: MERLIN 8 PRO * +* OS: DOS 3.3 * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** ASSEMBLER DIRECTIVES +* + CYC AVE + EXP OFF + TR ON + DSK DEMO.DOSMORE + OBJ $BFE0 + ORG $6000 +* +*``````````````````````````````* +* TOP INCLUDES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* + PUT MIN.HEAD.REQUIRED.ASM + USE MIN.MAC.REQUIRED.ASM + USE MIN.MAC.DOSREQ.ASM + USE MIN.MAC.DOSMORE.ASM + PUT MIN.HEAD.DOS.ASM +]HOME EQU $FC58 +* +*``````````````````````````````* +* PROGRAM MAIN BODY * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +*``````````````````````````````* +* MISC. DOS MACROS * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +*``````````````````````````````* +* DVER * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE DVER MACRO TESTS WHICH DOS VERSION IS +** BEING USED. IT RETURNS #2 FOR DOS 3.2 AND #3 +** FOR DOS 3.X. +* + JSR ]HOME + _PRN "DOS VERSION MACRO (DVER)",8D + _PRN "========================",8D8D + DVER + CMP #3 + BEQ THREE + _PRN "USING DOS 2.X",8D + JMP NEXT1 +THREE _PRN "USING DOS 3.X",8D +NEXT1 + _WAIT +* +*``````````````````````````````* +* THE DOSIN MACRO * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE DOSIN MACRO CHECK WHETHER DOS IS LOADED. +** THIS IS NECESSARY TO CHECK BEFORE TRYING TO +** USE THE DOS FILE MANAGER OR THE RWTS ROUTINES. +* + JSR ]HOME + _PRN "THE DOSIN MACRO",8D + _PRN "===============",8D8D + DOSIN + BCC NODOS + _PRN "DOS IS CURRENTLY LOADED." + JMP NEXT2 +NODOS _PRN "DOS IS NOT LOADED." +NEXT2 + _WAIT +* +*``````````````````````````````* +* THE ABAS MACRO * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE ABAS MACRO TESTS WHICH VERSION OF +** BASIC IS LOADED: INTEGER OR APPLESOFT. +** IF APPLESOFT IS LOADED, THEN THE CARRY IS +** RETURNED AS TRUE; IF INTEGER BASIC IS LOADED, +** THEN THE CARRY IS RETURNED CLEAR. +* + JSR ]HOME + _PRN "THE ABAS MACRO",8D + _PRN "==============",8D8D + ABAS + BCC INTBAS + _PRN "APPLESOFT IS LOADED." + JMP NEXT3 +INTBAS _PRN "INTEGER BASIC IS LOADED." +NEXT3 _WAIT +* +*``````````````````````````````* +* THE IBEX MACRO * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE IBEX MACRO TESTS WHETHER INTEGER BASIC +** IS RUNNING. THE CARRY IS RETURNED AS TRUE IF SO, +** AND CLEAR IF NOT. NOTE THAT YOU SHOULD TEST IF +** INTEGER BASIC IS LOADED BEFOREHAND; OTHERWISE, +** A FALSE POSITIVE MAY RESULT. +* + JSR ]HOME + _PRN "THE IBEX MACRO",8D + _PRN "==============",8D8D + IBEX + BCC INTNORUN + _PRN "INTEGER BASIC IS RUNNING." + JMP NEXT4 +INTNORUN _PRN "INTEGER BASIC IS NOT RUNNING." +NEXT4 _WAIT +* +*``````````````````````````````* +* THE ABEX MACRO * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE ABEX MACRO TESTS WHETHER AN APPLESOFT +** PROGRAM IS CURRENTLY RUNNING. +* + JSR ]HOME + _PRN "THE ABEX MACRO",8D + _PRN "==============",8D8D + ABEX + BCC NOAPP + _PRN "APPLESOFT PROGRAM IS RUNNING." + JMP NEXT5 +NOAPP _PRN "APPLESOFT PROGRAM IS NOT RUNNING." +NEXT5 _WAIT +* + JSR ]HOME + _PRN "FIN!",8D8D +* +EXIT + JMP $3D0 +* + PUT MIN.LIB.REQUIRED.ASM +* +``` + + + + + diff --git a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/36.0 Detailed_Reference_D7_CONVERT.md b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/36.0 Detailed_Reference_D7_CONVERT.md new file mode 100644 index 0000000..db714a7 --- /dev/null +++ b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/36.0 Detailed_Reference_D7_CONVERT.md @@ -0,0 +1,1599 @@ +# Disk 7: Convert + + + +- [Part I: The Conversion Collection](#part-i-the-conversion-collection) + - [Conversion Components](#conversion-components) + - [Conversion Header File](#conversion-collection-header-file) + - [Conversion Macros and Subroutines](#conversion-macros-and-subroutines) + - [I2STR](#the-i2str-macro) + - [HEX2INTASC](#the-hex2intasc-subroutine) + - [STR2I](#the-str2i-macro) + - [INTASC2HEX](#the-intasc2hex-subroutine) + - [H2STR](#the-h2str-macro) + - [HEX2HEXASC](#the-hex2hexasc-subroutine) + - [STR2H](#the-str2h-macro) + - [HEXASC2HEX](#the-hexasc2hex-subroutine) + - [B2STR](#the-b2str-macro) + - [HEX2BINASC](#the-hex2binasc-subroutine) + - [STR2B](#the-str2b-macro) + - [BINASC2HEX](#the-binasc2hex-subroutine) +- [Part II: Conversion Collection Demo](#part-ii-conversion-collection-demo) + + + + + +--- + + + +## Part I: The Conversion Collection + + + +The seventh disk in the library is used for converting between different data types. Some of these are already available on the Apple II via ROM routines, but are included here to show how such conversions work. + +Currently, the Conversion Collection only includes macros and subroutines dedicated to converting strings representing different number bases to their equivalent numeric values, but more conversions will be available with more revision. + + + +--- + + + +## Conversion Components + + + +The Conversion Collection contains the following components: + +- A head file that includes a number of hooks, vectors and subroutines that are necessary for the operation of the whole collection. +- A macro library that includes all of the macros available in the Conversion Collection. +- Subroutines used by the macros; each macro uses a single subroutine for operation. +- A demonstration file that shows how each conversion macro works. + + + +--- + + + +## Conversion Collection Header File + + + +| Condition | Value | +| ------------- | ------------------------------------------------------------ | +| Name | File: HEAD.CONVERT.ASM | +| Type | Header File | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin 8 Pro | +| OS | Apple DOS 3.3 | +| Purpose | Provide appropriate hooks and routines for the Conversion Collection | +| Dependencies | none | +| Bytes | none | +| Notes | none | +| See Also | none | + +--- + +*DETAILS* + +The Conversion Header file currently includes very little, but is included as part of the collection to retain consistency with the rest of the library. + + + +`LISTING 7.00: HEAD.CONVERT.ASM Source` + +```assembly +*``````````````````````````````* +* HEAD.CONVERT.ASM * +* * +* HOOKS TO AID IN CONVERTING * +* STRINGS TO NUMBERS AND VICE * +* VERSA, AND ALSO IN BETWEEN. * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 13-MAY-2019 * +* ASSEMBLER: MERLIN 8 PRO * +* LICENSE: APACHE 2.0 * +* OS: DOS 3.3 * +* * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* + NOP ; OTHERWISE, MERLIN WILL CRASH + ; DUE TO EMPTY FILE + +``` + + + +--- + + + +## Conversion Macros and Subroutines + + + +The MAC.CONVERT.ASM file contains all of the macros associated with data type conversion in the AppleIIAsm library. + + + +`Figure 7.01: MAC.CONVERT.ASM Heading` + +```assembly +*``````````````````````````````* +* MAC.CONVERT.ASM * +* * +* THIS COLLECTION HOLDS MACROS * +* USED FOR CONVERTING VARIABLE * +* TYPES. * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 13-MAY-2021 * +* ASSEMBLER: MERLIN 8 PRO * +* OS: DOS 3.3 * +* * +* SUBROUTINE FILES NEEDED * +* * +* SUB.BINASC2HEX * +* SUB.HEX2BINASC * +* SUB.HEX2HEXASC * +* SUB.HEX2INTASC * +* SUB.HEXASC2HEX * +* SUB.INTASC2HEX * +* * +* LIST OF MACROS * +* * +* I2STR: INTEGER TO STRING * +* STR2I: STRING TO INTEGER * +* H2STR: HEXADECIMAL TO STRING * +* STR2H: STRING TO HEXADECIMAL * +* B2STR: BINARY TO STRING * +* STR2B: STRING TO BINARY * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +``` + + + +--- + + + +### THE I2STR MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ----------------------------------------- | +| Name | `I2STR` | +| Type | Macro | +| File | `MAC.CONVERT.ASM` | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | convert value to integer string | +| Input | ]1 = Value to convert or address of value | +| Output | none | +| Dependencies | `HEX2INTASC` | +| Flags Destroyed | NZCV | +| Cycles | 290+ | +| Bytes | 172 | +| Notes | none | +| See Also | `STR2I` `HEX2INTASC` | + +--- + +*DETAILS* + +The `I2STR` macro converts a numeric value into the integer form of its string equivalent. The resulting string is held in `RETURN`, with its length held in `RETLEN`. + + + +`LISTING 7.02: The I2STR Macro Source` + +```assembly +* +*``````````````````````````````* +* I2STR * +* * +* CONVERTS A 16BIT INTEGER TO * +* ITS STRING EQUIVALENT. * +* * +* PARAMETERS: * +* * +* ]1 = VALUE TO CONVERT * +* * +* CYCLES: 290+ * +* SIZE: 172 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +I2STR MAC + _MLIT ]1;WPAR1 ; {16C12B} + JSR HEX2INTASC ; {274C160B} + <<< +* +``` + + + +--- + + + +### THE HEX2INTASC SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | -------------------------------------------- | +| Name | `HEX2INTASC` | +| Type | Subroutine | +| File | SUB.HEX2INTASC.ASM | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Convert value to integer string | +| Input | WPAR1 = Value to convert or address of value | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 268+ | +| Bytes | 157 | +| Notes | none | +| See Also | `I2STR` | + +--- + +*DETAILS* + +The `HEX2INTASC` subroutine accepts a numeric value (or an address pointing to one) and converts it to its string equivalent in decimal base form. + + + +`LISTING 7.03: The HEX2INTASC Macro Source` + +``` +*``````````````````````````````* +* HEX2INTASC (NATHAN RIGGS) * +* * +* CONVERT A SIGNED HEXADECIMAL * +* VALUE TO AN INTEGER STRING. * +* * +* INPUT: * +* * +* WPAR1 = HEX TO CONVERT * +* * +* DESTROYS: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 268+ * +* SIZE: 157 * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]NGFLAG EQU VARTAB ; NEGATIVE FLAG +]VALSTR EQU WPAR1 ; HEXADECIMAL TO CONVERT +]MOD10 EQU VARTAB+2 ; VALUE MODULUS 10 +* +HEX2INTASC +* + LDA ]VALSTR+1 ; {4C3B} STORE VALUE HIGH BYTE + STA ]NGFLAG ; {4C3B} IN THE NEGATIVE FLAG + BPL :GETBP ; {3C2B} IF VALUE IS POSITIVE, BRANCH + LDA #0 ; {3C2B} ELSE SUBTRACT LOW BYTE + SEC ; {2C1B} + SBC ]VALSTR ; {4C3B} + STA ]VALSTR ; {4C3B} STORE AS NEW LOW BYTE + LDA #0 ; {3C2B} ADJUST HIGH BYTE + SBC ]VALSTR+1 ; {4C3B} + STA ]VALSTR+1 ; {4C3B} +:GETBP + LDA #0 ; {3C2B} SET BUFFER TO EMPTY + LDY #0 ; {3C2B} + STA RETLEN,Y ; {5C2B} BUFFER(0) = 0 +* +:CNVERT ; CONVERT VALUE TO STRING + LDA #0 ; {3C2B} RESET MODULUS + STA ]MOD10 ; {4C3B} + STA ]MOD10+1 ; {4C3B} + LDX #16 ; {4C3B} + CLC ; {2C1B} CLEAR CARRY +:DVLOOP + ROL ]VALSTR ; {7C3B} SHIFT CARRY INTO DIVBIT 0 + ROL ]VALSTR+1 ; {7C3B} WHICH WILL BE THE QUOTIENT + ROL ]MOD10 ; {7C3B} + SHIFT DIV AT SAME TIME + ROL ]MOD10+1 ; {7C3B} + SEC ; {2C1B} SET CARRY + LDA ]MOD10 ; {4C3B} SUBTRACT #10 (DECIMAL) FROM + SBC #10 ; {4C3B} MODULUS 10 + TAY ; {2C1B} SAVE LOW BYTE IN .Y + LDA ]MOD10+1 ; {4C3B} ADJUST HIGHBYTE + SBC #0 ; {4C3B} SUBTRACT CARRY + BCC :DECCNT ; {3C2B} IF DIVIDEND < DIVISOR, DEC CTR + STY ]MOD10 ; {4C3B} ELSE STORE RESULT IN MODULUS + STA ]MOD10+1 ; {4C3B} NEXT BIT OF QUOTIENT IS A 1, + ; DIVIDEND = DIVIDEND - DIVISOR +:DECCNT + DEX ; {2C1B} DECREASE .X COUNTER + BNE :DVLOOP ; {3C2B} IF NOT 0, CONTINUE DIVIDING + ROL ]VALSTR ; {7C3B} ELSE SHIFT IN LAST CARRY + ROL ]VALSTR+1 ; {7C3B} FOR QUOTIENT +:CONCH + LDA ]MOD10 ; {4C3B} + CLC ; {2C1B} CLEAR CARRY + ADC #$B0 ; {4C3B} ADD '0' CHARACTER TO VALUE + ; TO GET ACTUAL ASCII CHARACTER + JSR :CONCAT ; {6C3B} CONCATENATE TO STRING +* +** IF VALUE <> 0 THEN CONTINUE +* + LDA ]VALSTR ; {4C3B} IF VALUE STILL NOT 0, + ORA ]VALSTR+1 ; {7C3B} OR HIGH BIT, THEN KEEP DIVIDING + BNE :CNVERT ; {3C2B} +* +:EXIT + LDA ]NGFLAG ; {4C3B} IF NEGATIVE FLAG IS SET + BPL :POS ; {3C2B} TO ZERO, THEN NO SIGN NEEDED + LDA #173 ; {3C2B} ELSE PREPEND THE STRING + JSR :CONCAT ; {6C3B} WITH A MINUS SIGN +* +:POS ; VALUE IS POSITIVE + RTS ; {6C1B} RETLEN +* +:CONCAT ; STRING CONCATENATION SUBROUTINE + PHA ; {3C1B} SAVE CHAR ON STACK +* +** MOVE BUFFER RIGHT ONE CHAR +* + LDY #0 ; {3C2B} RESET INDEX + LDA RETLEN,Y ; {5C3B} GET CURRENT STRING LENGTH + TAY ; {2C1B} CURRENT LENGTH IS NOW INDEX + BEQ :EXITMR ; {3C2B} IF LENGTH = 0, EXIT CONCAT +* +:MVELP + LDA RETLEN,Y ; {5C3B} GET NEXT CHARACTER + INY ; {2C1B} INCREASE INDEX + STA RETLEN,Y ; {5C3B} STORE IT + DEY ; {2C1B} DECREASE INDEX BY 2 + DEY ; {2C1B} + BNE :MVELP ; {3C2B} LOOP UNTIL INDEX IS 0 +:EXITMR + PLA ; {3C1B} GET CHAR BACK FROM STACK + LDY #1 ; {4C3B} + STA RETLEN,Y ; {5C3B} STORE CHAR AS 1ST CHARACTER + LDY #0 ; {3C2B} RESET INDEX + LDA RETLEN,Y ; {5C3B} GET LENGTH BYTE + CLC ; {2C1B} CLEAR CARRY + ADC #1 ; {3C2B} INC LENGTH BY ONE + STA RETLEN,Y ; {5C3B} UPDATE LENGTH +* + LDA RETLEN ; {4C3B} + RTS ; {6C1B} +``` + + + +--- + + + +### THE STR2I MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | --------------------------------------- | +| Name | `STR2I` | +| Type | Macro | +| File | `MAC.CONVERT.ASM` | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | convert integer string to numeric value | +| Input | ]1 = string or its address | +| Output | none | +| Dependencies | `INTASC2HEX` | +| Flags Destroyed | NZCV | +| Cycles | 415+ | +| Bytes | 196 | +| Notes | none | +| See Also | `I2STR` `INTASC2HEX` | + +--- + +*DETAILS* + +The `STR2I` macro converts a string that contains a number in decimal form into its numeric equivalent. + + + +`LISTING 7.04: The STR2I Macro Source` + +```assembly +* +*``````````````````````````````* +* STR2I * +* * +* CONVERTS A STRING TO A 16BIT * +* NUMBER EQUIVALENT. * +* * +* PARAMETERS: * +* * +* ]1 = STRING OR ITS ADDRESS * +* * +* CYCLES: 415+ * +* SIZE: 196 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +STR2I MAC + _MSTR ]1;WPAR1 ; {16C12B} + JSR INTASC2HEX ; {399C184B} + <<< +* +``` + + + +--- + + + +### THE INTASC2HEX SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------------------- | +| Name | `INTASC2HEX` | +| Type | Subroutine | +| File | SUB.INTASC2HEX.ASM | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Convert decimal strings to numeric value | +| Input | WPAR1 = String or address of string | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 392+ | +| Bytes | 181 | +| Notes | none | +| See Also | `STR2I` | + +--- + +*DETAILS* + +The `INTASC2HEX` subroutine converts a string that represents a decimal integer and hands back its numeric value in `RETURN`, with the length of the number held in `RETLEN`. + + + +`LISTING 7.05: The INTASC2HEX Source` + +```assembly +*``````````````````````````````* +* INTASC2HEX (NATHAN RIGGS) * +* * +* INPUT: * +* * +* WPAR1 = STRING ADDRESS * +* * +* DESTROYS: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 392+ * +* SIZE: 181 SIZE * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]NACCUM EQU VARTAB +]SIGN EQU VARTAB+4 +]NINDEX EQU VARTAB+6 +]STR EQU WPAR1 +* +INTASC2HEX +* + LDY #0 ; {3C2B} INIT INDEX + LDA (]STR),Y ; {6C2B} GET STRING LENGTH + TAX ; {2C1B} TRANSFER TO .X + LDA #1 ; {3C2B} SET NINDEX TO 1 + STA ]NINDEX ; {4C3B} + LDA #0 ; {3C2B} INIT ]NACCUM LOW, HIGH + STA ]NACCUM ; {4C3B} ACCUM = 0 + STA ]NACCUM+1 ; {4C3B} + STA ]SIGN ; {4C3B} INIT SIGN TO 0 (POSITIVE) + TXA ; {2C1B} TRANSFER .X BACK TO .A + BNE :INIT1 ; {4C3B} IF .A != 0, CONTINUE INIT + JMP :EREXIT ; {3C3B} ELSE, EXIT WITH ERROR--NO STRING +:INIT1 + LDY ]NINDEX ; {4C3B} INITIALLY, SET TO 1 + LDA (]STR),Y ; {6C2B} LOAD FIRST CHARACTER + CMP #173 ; {3C2B} IF .A != "-" + BNE :PLUS ; {3C2B} THEN NUMBER IS POSITIVE + LDA #$0FF ; {3C2B} ELSE SET FLAG TO NEGATIVE + STA ]SIGN ; {4C3B} + INC ]NINDEX ; {5C2B} INCREASE INDEX + DEX ; {2C1B} DECREMENT LENGTH COUNT + BEQ :EREXIT ; {3C2B} EXIT WITH ERROR IF .X = 0 + JMP :CNVERT ; {3C3B} +:PLUS + CMP #'+' ; {3C2B} + BNE :CHKDIG ; {3C2B} START CONVERSION IF 1ST + ; CHARACTER IS NOT A + + INC ]NINDEX ; {5C2B} INCREASE NEW INDEX + DEX ; {2C1B} DEC COUNT; IGNORE + SIGN + BEQ :EREXIT ; {3C2B} ERROR EXIT IF ONLY + ; + IN THE BUFFER +:CNVERT + LDY ]NINDEX ; {4C3B} GET NEW INDEX + LDA (]STR),Y ; {6C2B} GET NEXT CHARACTER +:CHKDIG ; CHECK DIGIT + CMP #$B0 ; {3C2B} "0" + BMI :EREXIT ; {3C2B} ERROR IF NOT A NUMERAL + CMP #$BA ; {3C2B} '9'+1; TECHNICALLY : + BPL :EREXIT ; {3C2B} ERR IF > 9 (NOT NUMERAL) + PHA ; {3C1B} DIGIT TO STACK +* +** VALID DECIMAL DIGIT SO +** ACCUM = ACCUM * 10 +** = ACCUM * (8+2) +** = (ACCUM * 8) + (ACCUM * 2) +* + ASL ]NACCUM ; {6C3B} + ROL ]NACCUM+1 ; {7C3B} TIMES 2 + LDA ]NACCUM ; {4C3B} + LDY ]NACCUM+1 ; {4C3B} SAVE ACCUM * 2 + ASL ]NACCUM ; {6C3B} + ROL ]NACCUM+1 ; {7C3B} + ASL ]NACCUM ; {6C3B} + ROL ]NACCUM+1 ; {7C3B} TIMES 8 + CLC ; {2C1B} + ADC ]NACCUM ; {4C3B} SUM WITH * 2 + STA ]NACCUM ; {4C3B} + TYA ; {2C1B} + ADC ]NACCUM+1 ; {4C3B} + STA ]NACCUM+1 ; {4C3B} ACCUM=ACCUM * 10 +* + PLA ; {3C1B} GET THE DIGIT FROM STACK + SEC ; {2C1B} SET CARRY + SBC #$B0 ; {2C2B} SUBTRACT ASCII '0' + CLC ; {2C1B} CLEAR CARRY + ADC ]NACCUM ; {4C3B} ADD TO ACCUMULATION + STA ]NACCUM ; {4C3B} STORE IN ACCUMULATION + LDA #0 ; {3C2B} NOW ADJUST HIGH BYTE + ADC ]NACCUM+1 ; {4C3B} + STA ]NACCUM+1 ; {4C3B} + INC ]NINDEX ; {5C3B} INC TO NEXT CHARACTER + DEX ; {2C1B} DECREMENT .X COUNTER + BNE :CNVERT ; {3C2B} IF .X != 0, CONTINUE CONVERSION + LDA ]SIGN ; {4C3B} ELSE LOAD SIGN FLAG + BPL :OKEXIT ; {3C2B} IF POSITIVE, EXIT WITHOUT ERROR + LDA #0 ; {4C3B} ELSE SET THE VALUE TO NEGATIVE + SEC ; {2C1B} SET CARRY + SBC ]NACCUM ; {4C3B} 0 - ]NACCUM + STA ]NACCUM ; {4C3B} STORE AS ]NACCUM + LDA #0 ; {3C2B} ADJUST HIGHBYTE + SBC ]NACCUM+1 ; {4C3B} + STA ]NACCUM+1 ; {4C3B} +* +:OKEXIT + CLC ; {2C1B} CLEAR CARRY TO SIGNIFY NO ERRORS + BCC :EXIT ; {3C2B} +:EREXIT + SEC ; {2C1B} SET CARRY TO INIDICATE ERROR +:EXIT + LDA #2 ; {3C2B} BYTE LENGTH IS 2 + STA RETLEN ; {4C3B} + LDX ]NACCUM+1 ; {4C3B} LOAD HIGH BYTE INTO .X + LDA ]NACCUM ; {4C3B} AND LOW BYTE INTO .A + STA RETURN ; {4C3B} ALSO STORE RESULT IN RETURN + STX RETURN+1 ; {4C3B} + RTS ; {6C1B} +``` + + + +--- + + + +### THE H2STR MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `H2STR` | +| Type | Macro | +| File | `MAC.CONVERT.ASM` | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | convert numeric value into its hexadecimal string equivalent | +| Input | ]1 = value or its address | +| Output | none | +| Dependencies | `HEX2HEXASC` | +| Flags Destroyed | NZCV | +| Cycles | 97+ | +| Bytes | 59 | +| Notes | none | +| See Also | `STR2H` `HEX2HEXASC` | + +--- + +*DETAILS* + +The `H2STR` macro converts a numeric value into its string equivalent in a hexadecimal base. + + + +`LISTING 7.06: The H2STR Macro Source` + +```assembly +* +*``````````````````````````````* +* H2STR * +* * +* CONVERTS A HEX BYTE INTO AN * +* EQUIVALENT STRING IN HEX. * +* * +* PARAMETERS: * +* * +* ]1 = HEX VALUE TO CONVERT * +* OR THE ADDRESS * +* * +* CYCLES: 97+ * +* SIZE: 59 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +H2STR MAC + LDA ]1 ; {4C3B} + JSR HEX2HEXASC ; {93C56B} + <<< +* +``` + + + +--- + + + +### THE HEX2HEXASC SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------- | +| Name | `HEX2HEXASC` | +| Type | Subroutine | +| File | SUB.HEX2HEXASC.ASM | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Convert numeric value to hexadecimal string | +| Input | WPAR1 = Value or address to value | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 87+ | +| Bytes | 53 | +| Notes | none | +| See Also | `H2HSTR` | + +--- + +*DETAILS* + +The `HEX2HEXASC` subroutine takes a numeric value and returns its string equivalent in a hexadecimal base. + + + +`LISTING 7.07: The HEX2HEXASC Source` + +```assembly +*``````````````````````````````* +* HEX2HEXASC (NATHAN RIGGS) * +* * +* INPUT: * +* * +* .A = HEX TO CONVERT * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 87+ * +* SIZE: 53 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]LEFT EQU VARTAB ; LEFT NIBBLE +]RIGHT EQU VARTAB+2 ; RIGHT NIBBLE +]HBYTE EQU VARTAB+4 ; HEX BYTE TO CONVERT +]HEXTAB ASC "0123456789ABCDEF" ; HEX LOOKUP TABLE +* +HEX2HEXASC + STA ]HBYTE ; {4C3B} STORE HEX PASSED VIA .A + AND #$F0 ; {2C2B} MASK RIGHT + LSR ; {2C1B} + LSR ; {2C1B} + LSR ; {2C1B} + LSR ; {2C1B} + STA ]LEFT ; {4C3B} STORE LEFT NIBBLE + LDA ]HBYTE ; {4C3B} + AND #$0F ; {2C2B} MASK LEFT + STA ]RIGHT ; {4C3B} STORE RIGHT NIBBLE + LDX ]LEFT ; {4C3B} GET THE LEFT CHARACTER + LDA ]HEXTAB,X ; {6C2B} FROM LOOKUP TABLE + STA ]LEFT ; {4C3B} + LDX ]RIGHT ; {4C3B} GET THE RIGHT CHARACTER + LDA ]HEXTAB,X ; {6C2B} FROM LOOKUP TABLE + STA ]RIGHT ; {4C3B} + LDA ]LEFT ; {6C2B} STORE LEFT IN RETURN + STA RETURN ; {4C3B} + LDA ]RIGHT ; {4C3B} STORE RIGHT IN NEXT BYTE + STA RETURN+1 ; {4C3B} + LDA #2 ; {3C2B} LENGTH IN RETLEN AND .A + STA RETLEN ; {4C3B} + RTS ; {6C1B} +``` + + + +--- + + + +### THE STR2H MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `STR2H` | +| Type | Macro | +| File | `MAC.CONVERT.ASM` | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Convert a string representing a numeric value
in hexadecimal to its numeric form | +| Input | ]1 = String or its address | +| Output | none | +| Dependencies | `HEXASC2HEX` | +| Flags Destroyed | NZCV | +| Cycles | 116+ | +| Bytes | 71 | +| Notes | none | +| See Also | `H2STR` `HEXASC2HEX` | + +--- + +*DETAILS* + +The `STR2H` macro accepts a string containing characters that represent a hexadecimal number and passes back the equivalent numeric value in `RETURN` with the number's length in `RETLEN`. + + + +`LISTING 7.08: The STR2H Macro Source` + +```assembly +* +*``````````````````````````````* +* STR2H * +* * +* CONVERTS A HEX STRING TO ITS * +* EQUIVALENT HEX BYTE. * +* * +* PARAMETERS: * +* * +* ]1 = STRING OR ITS ADDRESS * +* * +* CYCLES: 116+ * +* SIZE: 71 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +STR2H MAC + _MSTR ]1;WPAR1 ; {16C12B} + JSR HEXASC2HEX ; {100C59B} + <<< +* +``` + + + +--- + + + +### THE HEXASC2HEX SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `HEXASC2HEX` | +| Type | Subroutine | +| File | SUB.HEXASC2HEX.ASM | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Convert a string representing a hexadecimal number
into its equivalent numeric form. | +| Input | WPAR11 =String or its address | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 94+ | +| Bytes | 56 | +| Notes | none | +| See Also | `STR2H` | + +--- + +*DETAILS* + +The `HEXASC2HEX` subroutine converts a string that represents a hexadecimal byte and converts it to its numeric value. This value is passed back in `RETURN`, with its length held in `RETLEN`. + + + +`LISTING 7.09: The HEXASC2HEX Source` + +```assembly +*``````````````````````````````* +* HEXASC2HEX * +* * +* INPUT: * +* * +* WPAR1 = HEX STRING ADDRESS * +* * +* DESTROYS: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 94+ * +* SIZE: 56 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]HI EQU VARTAB ; HIGH BYTE +]LO EQU VARTAB+2 ; LOW BYTE +]STR EQU WPAR1 ; ADDR OF STRING TO CONVERT +* +HEXASC2HEX + LDY #1 ; {3C2B} GET FIRST HEX CHARACTER + LDA (]STR),Y ; {6C2B} + STA ]HI ; {4C3B} STORE IN HIBYTE + INY ; {2C1B} INCREASE INDEX + LDA (]STR),Y ; {6C2B} TO GET SECOND HEX CHARACTER + STA ]LO ; {4C3B} AND STORE THAT IN LOW BYTE +* + SEC ; {2C1B} SET CARRY + SBC #'0' ; {3C2B} SUBTRACT '0' CHAR FROM ]LO CHAR + CMP #10 ; {3C2B} ASCII NUMERALS OFFSET + BCC :CONT ; {3C2B} IF NUMERAL, CONTINUE + SBC #7 ; {2C2B} OTHERWISE SUBTRACT LETTER OFFSET +:CONT + STA ]LO ; {4C3B} STORE VALUE INTO LOW BYTE + LDA ]HI ; {4C3B} NO WORK ON HIGH BYTE + SEC ; {2C1B} SET CARRY + SBC #'0' ; {2C2B} SUBTRACT '0' ASCII + CMP #10 ; {3C2B} IS NUMBER? + BCC :C2 ; {3C2B} THEN DONE + SBC #7 ; {2C2B} OTHERWISE LETTER OFFSET +:C2 + STA ]HI ; {4C3B} STORE HIGH BYTE VALUE + ASL ; {2C1B} CLEAR LOW BYTE OF ]HI + ASL ; {2C1B} + ASL ; {2C1B} + ASL ; {2C1B} + ORA ]LO ; {7C3B} OR OPERATION TO INSERT + ; LOW BYTE INTO RESULT + LDY #1 ; {3C2B} SET LENGTH OF RETURN + STY RETLEN ; {4C3B} + STA RETURN ; {4C3B} PASS BACK VIA RETURN AND .A + RTS ; {6C1B} + +``` + + + +--- + + + +### THE B2STR MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `B2STR` | +| Type | Macro | +| File | `MAC.CONVERT.ASM` | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Convert a numeric value into a string
representing the number's binary form. | +| Input | ]1 = Value or its address | +| Output | none | +| Dependencies | `HEX2BINASC` | +| Flags Destroyed | NZCV | +| Cycles | 262+ | +| Bytes | 167 | +| Notes | none | +| See Also | `STR2B` `HEX2BINASC` | + +--- + +*DETAILS* + +The `STR2H` macro accepts a string containing characters that represent a hexadecimal number and passes back the equivalent numeric value in `RETURN` with the number's length in `RETLEN`. + + + +`LISTING 7.10: The B2STR Macro Source` + +```assembly +* +*``````````````````````````````* +* B2STR * +* * +* CONVERTS A HEX VALUE TO ITS * +* EQUIVALENT BINARY STRING. * +* * +* PARAMETERS: * +* * +* ]1 = HEX VALUE OR ADDRESS * +* * +* CYCLES: 262+ * +* SIZE: 167 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +B2STR MAC + LDA ]1 ; {4C3B} + STA BPAR1 ; {3C2B} + JSR HEX2BINASC ; {255C162C} + <<< +* +``` + + + +--- + + + +### THE HEX2BINASC SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | --------------------------------------------------- | +| Name | `HEX2BINASC` | +| Type | Subroutine | +| File | SUB.HEX2BINASC.ASM | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Convert a numeric value to a string
in binary. | +| Input | BPAR1 =Value or its address | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 249+ | +| Bytes | 159 | +| Notes | none | +| See Also | `B2STR` | + +--- + +*DETAILS* + +The `HEX2BINASC` subroutine converts a given value to its string equivalent in binary form. The string is passed back in `RETURN` with its length held in `RETLEN`. + + + +`LISTING 7.11: The HEX2BINASC Source` + +```assembly +*``````````````````````````````* +* HEX2BINASC (NATHAN RIGGS) * +* * +* INPUT: * +* * +* BPAR1 = HEX BYTE TO CONVERT * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 249+ * +* SIZE: 159 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]BINTAB ASC "0000" ; 0 {0C4B} + ASC "0001" ; 1 {0C4B} + ASC "0010" ; 2 {0C4B} + ASC "0011" ; 3 {0C4B} + ASC "0100" ; 4 {0C4B} + ASC "0101" ; 5 {0C4B} + ASC "0110" ; 6 {0C4B} + ASC "0111" ; 7 {0C4B} + ASC "1000" ; 8 {0C4B} + ASC "1001" ; 9 {0C4B} + ASC "1010" ; A {0C4B} + ASC "1011" ; B {0C4B} + ASC "1100" ; C {0C4B} + ASC "1101" ; D {0C4B} + ASC "1110" ; E {0C4B} + ASC "1111" ; F {0C4B} +* +]LEFT EQU VARTAB ; LEFT NIBBLE +]RIGHT EQU VARTAB+2 ; RIGHT NIBBLE +]HBYTE EQU BPAR1 ; HEX BYTE +* +HEX2BINASC +* + LDA ]HBYTE ; {4C3B} + AND #$F0 ; {4C3B} FIRST, MASK THE RIGHT NIBBLE + LSR ; {2C1B} SHIFT RIGHT + LSR ; {2C1B} SHIFT RIGHT + LSR ; {2C1B} SHIFT RIGHT + LSR ; {2C1B} SHIFT RIGHT + STA ]LEFT ; {4C3B} STORE AS LEFT NIBBLE + LDA ]HBYTE ; {4C3B} + AND #$0F ; {4C3B} NOW MASK LEFT NIBBLE + STA ]RIGHT ; {4C3B} STORE AS RIGHT NIBBLE +* +** GET LEFT FROM LOOKUP TABLE +* + ASL ]LEFT ; {6C3B} MULTIPLY ]LEFT NIBBLE + ASL ]LEFT ; {6C3B} BY FOUR + LDX ]LEFT ; {4C3B} TO GET LOOKUP TABLE OFFSET + LDA ]BINTAB,X ; {5C3B} TRANSFER APPROPRIATE + STA RETURN ; {4C3B} PART OF THE TABLE TO RETURN + LDA ]BINTAB,X+1 ;{5C3B} + STA RETURN+1 ; {4C3B} + LDA ]BINTAB,X+2 ;{5C3B} + STA RETURN+2 ; {4C3B} + LDA ]BINTAB,X+3 ;{5C3B} + STA RETURN+3 ; {4C3B} +* +** NOW GET RIGHT +* + ASL ]RIGHT ; {6C3B} MULTIPLY ]RIGHT BY 4 + ASL ]RIGHT ; {6C3B} TO GET LOOKUP TABLE OFFSET + LDX ]RIGHT ; {4C3B} + LDA ]BINTAB,X ; {5C3B} AND TRANSFER APPROPRIATE + STA RETURN+4 ; {4C3B} STRING TO RETURN AFTER + LDA ]BINTAB,X+1 ;{5C3B} THE PREVIOUS NIBBLE + STA RETURN+5 ; {4C3B} + LDA ]BINTAB,X+2; {5C3B} + STA RETURN+6 ; {4C3B} + LDA ]BINTAB,X+3 ;{5C3B} + STA RETURN+7 ; {4C3B} +* + LDA #8 ; {3C2B} LENGTH IN .A AND RETLEN + STA RETLEN ; {4C3B} + RTS ; {6C1B} + +``` + + + +--- + + + +### THE STR2B MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------------------- | +| Name | `STR2B` | +| Type | Macro | +| File | `MAC.CONVERT.ASM` | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Convert a binary string to numeric value | +| Input | ]1 = String or its address | +| Output | none | +| Dependencies | `BINASC2HEX` | +| Flags Destroyed | NZCV | +| Cycles | 501+ | +| Bytes | 331 | +| Notes | none | +| See Also | `B2STR` `BINASC2HEX` | + +--- + +*DETAILS* + +The `STR2B` macro converts a string representing a binary number into its numeric equivalent, passing the value back in `RETURN`. + + + +`LISTING 7.12: The STR2B Macro Source` + +```assembly +* +*``````````````````````````````* +* STR2B * +* * +* CONVERTS A BINARY STRING TO * +* EQUIVALENT HEX VALUE. * +* * +* PARAMETERS: * +* * +* ]1 = STRING OR ITS ADDRESS * +* * +* CYCLES: 501+ * +* SIZE: 331 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +STR2B MAC + _MSTR ]1;WPAR1 ; {16C12B} + JSR BINASC2HEX ; {485C319B} + <<< +``` + + + +--- + + + +### THE BINASC2HEX SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------ | +| Name | `BINASC2HEX` | +| Type | Subroutine | +| File | SUB.BINASC2HEX.ASM | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Convert a binary string to a number. | +| Input | WPAR1 = String or its address | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 513+ | +| Bytes | 329 | +| Notes | none | +| See Also | `STR2B` | + +--- + +*DETAILS* + +The `BINASC2HEX` subroutine accepts a string that represents an 8-bit binary value and passes back its numeric equivalent in `RETURN`. Since this subroutine only accepts an 8-bit value (a byte), `RETLEN` always contains **#1** after the subroutine is called. + + + +`LISTING 7.13: The BINASC2HEX Source` + +```assembly +*``````````````````````````````* +* BINASC2HEX (NATHAN RIGGS) * +* * +* CONVERTS A STRING HOLDING * +* 8 CHARACTERS OF 0S AND 1S * +* THAT SIGNIFY A BYTE INTO THE * +* APPROPRIATE HEX VALUE. * +* * +* INPUT: * +* * +* WPAR1 = STRING ADDRESS PTR * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 513+ * +* SIZE: 329 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]HIGH EQU VARTAB +]LOW EQU VARTAB+2 +]NIB EQU VARTAB+4 +]STR EQU WPAR1 +* +BINASC2HEX +* + JSR :TESTNIB ; {6C3B} FIRST CHECK HIGH NIBBLE + LDA ]NIB ; {4C3B} (1ST 4 'BITS' IN THE STRING) + STA ]HIGH ; {4C3B} AND STORE HEX IN ]HIGH +* + CLC ; {2C1B} + ASL ]HIGH ; {6C3B} + ASL ]HIGH ; {6C3B} + ASL ]HIGH ; {6C3B} + ASL ]HIGH ; {6C3B} +* + LDA ]STR ; {3C2B} ADD 4 TO THE STRING ADDRESS + CLC ; {2C1B} TO GET THE LOW NIBBLE + ADC #4 ; {2C2B} STRING ADDRESS + STA ]STR ; {3C2B} + LDA ]STR+1 ; {3C2B} MAKE SURE TO ADJUST + ADC #0 ; {2C2B} THE HIGH BYTE + STA ]STR+1 ; {3C2B} + JSR :TESTNIB ; {6C3B} TEST LOW NIBBLE OF STRING + LDA ]NIB ; {4C3B} + STA ]LOW ; {4C3B} AND STORE THE LOW NIBBLE HEX +* + LDA #1 ; {3C2B} STORE BYTE LENGTH + STA RETLEN ; {4C3B} IN RETLEN + LDA ]HIGH ; {4C3B} LOAD HIGH NIBBLE AND + ORA ]LOW ; {4C3B} EXCLUSIVE-OR IT WITH LOW NIB + STA RETURN ; {4C3B} TO GET COMPLETE BYTE + JMP :EXIT ; {3C3B} +* +** THE :TESTNIB SUBROUTINE TRANSLATES +** A BINARY NIBBLE STRING REPRESENTATION INTO +** ITS EQUIVALENT HEXADECIMAL CODE +* +:TESTNIB + LDY #1 ; {3C2B} START AT FIRST BINARY DIGIT + LDA (]STR),Y ; {6C2B} GET EITHER 0 OR 1 CHARACTER + CMP #"0" ; {3C2B} IF = 0 + BEQ :_07 ; {3C2B} THEN NIB IS BETWEEN 0 AND 7 + JMP :_8F ; {3C2B} ELSE IT IS BETWEEN 8 AND F +:_07 + LDY #2 ; {3C2B} CHECK SECOND STRING DIGIT + LDA (]STR),Y ; {6C2B} AGAIN, GET 0 OR 1 + CMP #"0" ; {3C2B} IF = 0 + BEQ :_03 ; {3C2B} THEN NIBBLE BETWEEN 0 AND 3 + JMP :_47 ; {3C3B} ELSE IT IS BETWEEN 4 AND 7 +:_03 + LDY #3 ; {3C2B} THIRD DIGIT OF NIBBLE + LDA (]STR),Y ; {6C2B} GET 0 OR 1 FROM STRING + CMP #"0" ; {3C2B} IF = 0, + BEQ :_01 ; {3C2B} NIBBLE IS EITHER 0 OR 1 + JMP :_23 ; {3C3B} ELSE EITHER 2 OR 3 +:_01 + LDY #4 ; {3C2B} LAST BIT OF NIBBLE STRING + LDA (]STR),Y ; {6C2B} GET EITHER 0 OR 1 + CMP #"0" ; {3C2B} IF IT IS 0, + BEQ :_00 ; {3C2B} FIRST NIBBLE IS 0 + LDA #1 ; {3C2B} ELSE IT IS 1 + STA ]NIB ; {4C3B} STORE NIBBLE + JMP :EXIT ; {3C3B} +:_00 LDA #0 ; {3C2B} NIBBLE IS 0000 + STA ]NIB ; {4C3B} + JMP :EXIT ; {3C3B} +* +:_23 LDY #4 ; {3C2B} READ 4TH BIT IN NIBBLE + LDA (]STR),Y ; {6C2B} + CMP #"0" ; {3C2B} IF = "0", + BEQ :_02 ; {3C2B} THEN THE FIRST NIBBLE IS 2 + LDA #3 ; {3C2B} ELSE IT IS 3 + STA ]NIB ; {4C3B} + JMP :EXIT ; {3C3B} +:_02 LDA #$2 ; {3C2B} NIBBLE IS 2 + STA ]NIB ; {4C3B} + JMP :EXIT ; {3C3B} +:_47 + LDY #3 ; {3C2B} READ 3RD BIT FROM STRING + LDA (]STR),Y ; {6C2B} + CMP #"0" ; {3C2B} IF = "0", + BEQ :_45 ; {3C2B} THEN 1ST NIBBLE IS 4 OR 5 + JMP :_67 ; {3C3B} ELSE IT IS 6 OR 7 +:_45 + LDY #3 ; {3C2B} CHECK 4TH BIT OF BIN STRING + LDA (]STR),Y ; {6C2B} + CMP #"0" ; {3C2B} IF = "0", + BEQ :_4 ; {3C2B} THEN FIRST NIB IS 4 + LDA #$5 ; {3C2B} ELSE IT IS 5 + STA ]NIB ; {4C3B} + JMP :EXIT ; {3C3B} +:_4 LDA #$4 ; {3C2B} NIBBLE = 4 + STA ]NIB ; {4C3B} + JMP :EXIT ; {3C3B} +:_67 + LDY #4 ; {3C2B} CHECK 4TH BIT IN STRING + LDA (]STR),Y ; {6C2B} + CMP #"0" ; {3C2B} IF = "0" + BEQ :_6 ; {3C2B} THEN THE FIRST NIB IS 6 + LDA #$7 ; {3C2B} ELSE IT IS 7 + STA ]NIB ; {4C3B} + JMP :EXIT ; {3C3B} +:_6 LDA #$6 ; {4C3B} NIBBLE = 6 + STA ]NIB ; {4C3B} + JMP :EXIT ; {3C3B} +* +:_8F ; CHECK VALUE BETWEEN 8 AND F + LDY #2 ; {3C2B} CHECK SECOND BIT + LDA (]STR),Y ; {6C2B} + CMP #"0" ; {3C2B} IF = "0", + BEQ :_8B ; {3C2B} THEN NIB IS BETWEEN 8 AND B + JMP :_CF ; {3C3B} OTHERWISE BETWEEN C AND F +:_8B ; CHECK VALUES 8-B + LDY #3 ; {3C2B} CHECK 3RD BIT + LDA (]STR),Y ; {6C2B} + CMP #"0" ; {3C2B} IF = "0", + BEQ :_89 ; {3C2B} NIBBLE IS EITHER 8 OR 9 + JMP :_AB ; {3C3B} ELSE IT IS BETWEEN A AND B +:_89 ; TEST WHETHER 8 OR 9 + LDY #4 ; {3C2B} CHECK 4TH BIT + LDA (]STR),Y ; {6C2B} + CMP #"0" ; {3C2B} IF = "0", + BEQ :_8 ; {3C2B} THEN NIBBLE IS 8 + LDA #9 ; {3C2B} ELSE, IS 9 + STA ]NIB ; {4C3B} + JMP :EXIT ; {3C3B} +:_8 LDA #$8 ; {3C2B} NIBBLE = 8 + STA ]NIB ; {3C2B} + JMP :EXIT ; {3C3B} +:_AB ; NIBBLE IS EITHER A OR B + LDY #4 ; {3C2B} CHECK 4TH BIT + LDA (]STR),Y ; {6C2B} + CMP #"0" ; {3C2B} IF = "0" + BEQ :_A ; {3C2B} THEN NIBBLE IS A + LDA #$B ; {3C2B} OTHERWISE, IT'S B + STA ]NIB ; {4C3B} + JMP :EXIT ; {3C3B} +:_A LDA #$A ; {3C2B} NIBBLE IS A + STA ]NIB ; {4C3B} + JMP :EXIT ; {3C3B} +:_CF ; NIBBLE IS BETWEEN C AND F + LDY #3 ; {3C2B} CHECK 3RD BIT + LDA (]STR),Y ; {6C2B} + CMP #"0" ; {3C2B} IF = "0", + BEQ :_CD ; {3C2B} THEN IT IS EITHER C AND D + JMP :_EF ; {3C3B} OTHERWISE, BETWEEN E AND F +:_CD ; NIBBLE IS EITHER C OR D + LDY #4 ; {3C2B} CHECK 4TH BIT + LDA (]STR),Y ; {6C2B} + CMP #"0" ; {3C2B} IF IT IS "0", + BEQ :_C ; {3C2B} THEN NIBBLE IS C + LDA #$D ; {3C2B} OTHERWISE, IT'S D + STA ]NIB ; {4C3B} + JMP :EXIT ; {3C3B} +:_C LDA #$C ; {4C3B} NIBBLE IS C + STA ]NIB ; {4C3B} + JMP :EXIT ; {3C3B} +:_EF ; NIBBLE IS EITHER E OR F + LDY #4 ; {3C2B} CHECK 4TH BIT + LDA (]STR),Y ; {6C2B} + CMP #"0" ; {3C2B} IF IT IS "0", + BEQ :_E ; {3C2B} THEN NIBBLE IS E + LDA #$F ; {3C2B} OTHERWISE, F + STA ]NIB ; {4C3B} + JMP :EXIT ; {3C3B} +:_E LDA #$E ; {4C3B} SET TO E + STA ]NIB ; {4C3B} +:EXIT + RTS ; {6C1B} +``` + + + +--- + + + +## Part II: Conversion Collection Demo + + + +Listing 7.14 illustrates how each of the macros in the Conversion Collection is used. + + + +`LISTING 7.14: DEMO.CONVERT.ASM Source` + +```assembly +* +*``````````````````````````````* +* DEMO.CONVERT.ASM * +* * +* A DEMO OF THE CONVERSION * +* MACROS. * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 13-MAY-2021 * +* ASSEMBLER: MERLIN 8 PRO * +* OS: DOS 3.3 * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** ASSEMBLER DIRECTIVES +* + CYC AVE + EXP OFF + TR ON + DSK DEMO.CONVERT + OBJ $BFE0 + ORG $6000 +* +*``````````````````````````````* +* TOP INCLUDES (PUTS, MACROS) * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* + PUT MIN.HEAD.REQUIRED.ASM + USE MIN.MAC.REQUIRED.ASM + USE MIN.MAC.CONVERT.ASM + PUT MIN.HEAD.CONVERT.ASM +* +*``````````````````````````````* +* PROGRAM MAIN BODY * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]HOME EQU $FC58 +]XCOUT EQU $FDF0 +* +*``````````````````````````````* +* INTEGER TO STRING * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE I2STR MACRO, WHICH STANDS FOR INTEGER TO STRING, +** CONVERTS A NUMERIC VALUE INTO ITS INTEGER STRING +** EQUIVALENT. THIS STRING IS THEN STORED IN RETURN. +* + JSR ]HOME + _PRN "I2STR MACRO",8D + _PRN "===========",8D8D + I2STR #5309 + _PRN "I2STR #5309 = " + _WAIT + LDA RETURN + JSR ]XCOUT + LDA RETURN+1 + JSR ]XCOUT + LDA RETURN+2 + JSR ]XCOUT + LDA RETURN+3 + JSR ]XCOUT + _WAIT +* +*``````````````````````````````* +* STRING TO INTEGER * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE STR2I (STRING TO INTEGER) MACRO TAKES A STRING +** THAT REPRESENTS AN INTEGER AND RETURNS ITS EQUIVALENT +** NUMERIC VALUE. +* + JSR ]HOME + _PRN "THE STR2I MACRO",8D + _PRN "===============",8D8D + _PRN "STR2I '255' = " + STR2I "255" + _WAIT + DUMP #RETURN;#2 + _WAIT +* +*``````````````````````````````* +* HEXADECIMAL TO STRING * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE H2STR (HEXADECIMAL TO STRING) MACRO CONVERTS +** A HEXADECIMAL VALUE TO ITS STRING EQUIVALENT. +* + JSR ]HOME + _PRN "HEXADECIMAL TO STRING",8D + _PRN "=====================",8D8D + _PRN "H2STR #$FF = " + _WAIT + H2STR #$FF + LDA RETURN + JSR ]XCOUT + LDA RETURN+1 + JSR ]XCOUT + _WAIT +* +*``````````````````````````````* +* STRING TO HEXADECIMAL * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE STR2H (STRING TO HEXADECIMAL) MACRO CONVERTS +** A STRING INTO ITS HEXADECIMAL EQUIVALENT. +* + JSR ]HOME + _PRN "STRING TO HEXADECIMAL",8D + _PRN "=====================",8D8D + _PRN "STR2H 'FF' = " + _WAIT + STR2H "FF" + DUMP #RETURN;#1 + _WAIT +* +*``````````````````````````````* +* STRING TO BINARY * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE STR2B (STRING TO BINARY) MACRO CONVERTS A +** STRING OF ONES AND ZEROES TO ITS EQUIVALENT +** NUMERIC VALUE. +* + JSR ]HOME + _PRN "BINARY TO STRING",8D + _PRN "================",8D8D + _PRN "STR2B '00110011' =" + _WAIT + STR2B #BIN + DUMP #RETURN;#1 + _WAIT +* +*``````````````````````````````* +* BINARY TO STRING * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE B2STR (BINARY TO STRING) MACRO CONVERTS A +** NUMERIC VALUE INTO AN EQUIVALENT STRING IN BINARY +** FORM. +* + JSR ]HOME + _PRN "BINARY TO STRING",8D + _PRN "===============",8D8D + _PRN "B2STR #$FF = " + _WAIT + B2STR #$FF + LDA RETURN + JSR ]XCOUT + LDA RETURN+1 + JSR ]XCOUT + LDA RETURN+2 + JSR ]XCOUT + LDA RETURN+3 + JSR ]XCOUT + LDA RETURN+4 + JSR ]XCOUT + LDA RETURN+5 + JSR ]XCOUT + LDA RETURN+6 + JSR ]XCOUT + LDA RETURN+7 + JSR ]XCOUT + _WAIT +* + JSR ]HOME + _PRN "FIN.",8D8D8D +* + JMP REENTRY +* +*``````````````````````````````* +* BOTTOM INCLUDES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** BOTTOM INCLUDES +* + PUT MIN.LIB.REQUIRED.ASM +* +** INDIVIDUAL SUBROUTINE INCLUDES +* + PUT MIN.SUB.HEX2INTASC.ASM + PUT MIN.SUB.INTASC2HEX.ASM + PUT MIN.SUB.HEX2BINASC.ASM + PUT MIN.SUB.BINASC2HEX.ASM + PUT MIN.SUB.HEX2HEXASC.ASM + PUT MIN.SUB.HEXASC2HEX.ASM +* +BIN STR "00110011" +* +``` + + + diff --git a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/37.0 Detailed_Reference_D8_LORES.md b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/37.0 Detailed_Reference_D8_LORES.md new file mode 100644 index 0000000..c62c393 --- /dev/null +++ b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/37.0 Detailed_Reference_D8_LORES.md @@ -0,0 +1,3350 @@ +# Disk 7: LoRes + + + +- [Part I: The LoRes Collection](#part-i-the-lores-collection) + - [LoRes Components](#lores-components) + - [LoRes Header File](#lores-collection-header-file) + - [LOCPLOT](#the-locplot-subroutine) + - [LoRes Macros and Subroutines](#lores-macros-and-subroutines) + - [LWORKPG](#the-lworkpg-macro) + - [LVIEWPG](#the-lviewpg-macro) + - [LRGF](#the-lrgf-macro) + - [LRGP](#the-lrgp-macro) + - [LFCLR](#the-lfclr-macro) + - [LRGFCLR](#the-lrgfclr-subroutine) + - [LPCLR](#the-lpclr-macro) + - [LRGPCLR](#the-lrgpclr-subroutine) + - [LPLOT](#the-lplot-macro) + - [LRPLOT](#the-lrplot-subroutine) + - [LLINE](#the-lline-macro) + - [LRBLINE](#the-lrbline-subroutine) + - [LCIRC](#the-lcirc-macro) + - [LRCIRCLE](#the-lrcircle-subroutine) + - [LVLIN](#the-lvlin-macro) + - [LRVLINE](#the-lrvline-subroutine) + - [LHLIN](#the-lhlin-macro) + - [LRHLINE](#the-lrhline-subroutine) + - [LRGET](#the-lrget-macro) + - [LRGETPIX](#the-lrgetpix-subroutine) + - [LCHAR](#the-lchar-macro) + - [LRCHAR](#the-lrchar-subroutine) +- [Part II: LoRes Collection Demo](#part-ii-lores-collection-demo) + + + + + +--- + + + +## Part I: The LoRes Collection + + + +The eighth disk in the AppleIIAsm Library consists of macros and subroutines used in low-resolution graphics mode. This includes procedures for managing viewing and working pages, plotting "pixels," lines, shapes and characters, and retrieving the state of a pixel at its given coordinates. + + + +--- + + + +## LoRes Components + + + +The LoRes Collection contains the following components: + +- A header file that includes number of hooks and vectors used for low resolution graphics, as well as a plotting routine that is used by the rest of the collection. +- A macro library that includes all of the macros used for low-resolution graphics. +- Subroutines used by the macros. +- A demonstration file that illustrates how each of the macros work. + + + +--- + + + +## LoRes Collection Header File + + + +| Condition | Value | +| ------------- | ------------------------------------------------------------ | +| Name | File: HEAD.LORES.ASM | +| Type | Header File | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin 8 Pro | +| OS | Apple DOS 3.3 | +| Purpose | Provide appropriate hooks and routines for the LoRes Collection | +| Dependencies | none | +| Bytes | 169+ | +| Notes | none | +| See Also | none | + +--- + +*DETAILS* + +The LoRes Header file includes a number of hooks and vectors used for low resolution graphics as well as a subroutine used for plotting to the screen, which is shared by the rest of the collection to function properly. + + + +`LISTING 8.00: HEAD.LORES.ASM Heading Source` + +```assembly +*``````````````````````````````* +* HOOKS.LORES * +* * +* THIS FILE INCLUDES HOODS AND * +* A FEW SUBROUTINES AND TABLES * +* USED BY THE REST OF THE LOW * +* RESOLUTION SUBROUTINES. * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 11-MAY-2021 * +* ASSEMBLER: MERLIN 8 PRO * +* LICENSE: APACHE 2.0 * +* OS: DOS 3.3 * +* * +* SIZE: 169 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +TEXTOFF EQU $C050 ; TURN ON GRAPHICS MODE +TEXTON EQU $C051 ; TURN ON TEXT MODE +MIXEDOFF EQU $C052 ; SET FULLSCREEN MODE FOR GRAPHICS +MIXEDON EQU $C053 ; SET MIXED MODE FOR GRAPHICS +LORES EQU $C056 ; SOFT SWITCH FOR USING LORES GRAPHICS +HIRES EQU $C057 ; SOFT SWITCH TO SPECIFY HIRES GRAPHICS +VPG1 EQU $C054 ; SET THE VIEWING PAGE TO PAGE 1 +VPG2 EQU $C055 ; SET THE VIEWING PAGE TO PAGE 2 +LRGBCALC EQU $F847 ; FOR CALCULATING LORES COORDINATES +GBASLO EQU $26 ; LOW BYTE OF A PIXEL ADDRESS +GBASHI EQU $27 ; HIGHT BYTE OF PIXEL ADDRESS +* +]BLACK EQU $00 ; LORES COLOR BLACK +]MAGENTA EQU $01 ; LORES COLOR MAGENTA +]DBLUE EQU $02 ; LORES DARK BLUE +]PURPLE EQU $03 ; LORES COLOR PURPLE +]DGREEN EQU $04 ; LORES COLOR DARK GREEN +]GREY1 EQU $05 ; LORES COLOR FIRST GREY SHADE +]MBLUE EQU $06 ; LORES COLOR MEDIUM BLUE +]LBLUE EQU $07 ; LORES COLOR LIGHT BLUE +]BROWN EQU $08 ; LORES COLOR BROWN +]ORANGE EQU $09 ; LORES COLOR ORANGE +]GREY2 EQU $0A ; LORES COLORE GREY SHADE 2 +]PINK EQU $0B ; LORES COLOR PINK +MGREEN EQU $0C ; LORES COLOR MEDIUM GREEN +]YELLOW EQU $0D ; LORES COLOR YELLOW +]AQUA EQU $0E ; LORES COLOR AQUAMARINE +]WHITE EQU $0F ; LORES COLOR WHITE +* +** THE LOCPLOT SUBROUTINE IS IN THE HOOKS FILE +** BECAUSE IT IS USED, IN SOME CAPACITY, BY THE +** REST OF THE LORES LIBRARY. +* + JMP ]EOF {3C3B} +* +]LOCCOL DS 1 ; {0C1B} LOCAL PLOT COLOR +]MASK DS 2 ; {0C2B} MASK FOR ISOLATING NIBBLE +]COLMASK DS 2 ; {0C2B} COLOR MASK +]X DS 1 ; {OC1B} X COORDINATE FOR PLOT +]Y DS 1 ; {0C1B} Y COORDINATE FOR PLOT +]PAGEOFF DS 1 ; {0C1B} PAGE OFFSET +* +``` + + + +--- + + + +### THE LOCPLOT SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------ | +| Name | `LOCPLOT` | +| Type | Subroutine | +| File | HEAD.LORES.ASM | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | plot a low-resolution pixel | +| Input | .X = X position
.Y = Y position
.A = Color | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 168+ | +| Bytes | 109 | +| Notes | This shares the same source as the `LRPLOT` subroutine | +| See Also | `LRPLOT` | + +--- + +*DETAILS* + +The `LOCPLOT` subroutine accepts an X-coordinate, a Y-coordinate and a color code to plot a low-resolution pixel at the given coordinate and in the given color. This subroutine is used for most of the plotting subroutines and macros in the library. + + + +`LISTING 8.01: The HEAD.LORES.ASM LOCPLOT Subroutine Source` + +```assembly +* +*``````````````````````````````* +* LOCPLOT * +* * +* PLOT AT A GIVEN X,Y POINT IN * +* A GIVEN COLOR. * +* * +* INPUT * +* * +* .X = X POSITION * +* .Y = Y POSITION * +* .A = COLOR CODE * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 168+ * +* SIZE: 109 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +LOCPLOT +* + STY ]Y ; {4C3B} Y POSITION PASSED IN .Y + STX ]X ; {4C3B} X POSITION PASSED IN .X + STA ]LOCCOL ; {4C3B} COLOR PASSED IN .A + LDA ]LOCCOL ; {4C3B} TAKE THE COLOR SENT + ASL ; {2C1B} AND MOVE IT LEFT 4 BITS + ASL ; {2C1B} TO THE HIGH BYTE + ASL ; {2C1B} + ASL ; {2C1B} + CLC ; {2C1B} CLEAR CARRY + ADC ]LOCCOL ; {4C3B} NOW ADD THE LOW BYTE, MEANING + STA ]LOCCOL ; {4C3B} COLOR WILL BE REPEATING NIBBLES + LDA LWP ; {4C3B} LOAD THE WORKING PAGE FLAG + CMP #2 ; {3C2B} IF WORKING PAGE IS NOT PAGE 2, + BNE :PG1 ; {3C2B} THEN ASSUME IT'S PAGE 1. + LDA #4 ; {3C2B} ELSE, SET OFFSET FOR PAGE 2 + STA ]PAGEOFF ; {4C3B} STORE IN THE PAGE OFFSET + JMP :CNT ; {3C3B} SKIP TO CONTINUE ROUTINE +:PG1 + LDA #0 ; {3C2B} OTHERWISE, IT'S PAGE ONE + STA ]PAGEOFF ; {4C3B} SO THERE IS NO PAGE OFFSET +:CNT + LDA #0 ; {3C2B} + LDY #0 ; {3C2B} + LDX #0 ; {3C2B} + LDA ]Y ; {4C3B} GET Y COORDINATE + LSR ; {2C1B} SHIFT BOTTOM BIT TO CARRY + BCC :EVEN ; {3C2B} IF CARRY = 0, THEN ROW IS EVEN + LDX #$F0 ; {3C2B} OTHERWISE, IT IS ODD; SO MASK + ; THE LEFT NIBBLE + BCS :LPLOT ; {3C2B} IF CARRY SET, BR TO PLOTTING +:EVEN + LDX #$0F ; {3C2B} EVEN, SO MASK LOW BYTE +:LPLOT + STX ]MASK ; {3C2B} STORE THE EVEN OR ODD MASK + ASL ; {2C1B} SHIFT CARRY BACK INTO BYTE + TAY ; {2C1B} HOLD VALUE INTO .Y + LDA LROFF,Y ; {5C3B} GET LORES MEMORY ADDRESS + CLC ; {2C1B} CLEAR THE CARRY + ADC ]X ; {4C3B} ADD THE X COORDINATE + STA GBASLO ; {4C3B} STORE LOW BYTE FOR GBASCALC + INY ; {2C1B} INCREASE Y OFFSET + LDA LROFF,Y ; {5C3B} GET LORESS MEMORY ADDRESS + ADC ]PAGEOFF ; {4C3B} ADJUST FOR PAGE AND CARRY HIGH + STA GBASHI ; {4C3B} STORE HIGH BYTE FOR GBASCALC + LDY #0 ; {3C2B} + LDA ]MASK ; {4C3B} RELOAD THE MASK + EOR #$FF ; {2C2B} EXCLUSIVE OR THE MASK + AND (GBASLO),Y ; {6C2B} AND THE LOW FOR GBAS + STA ]COLMASK ; {4C3B} STORE THE COLOR MASK + LDA ]LOCCOL ; {4C3B} LOAD THE COLOR + AND ]MASK ; {4C3B} AND THE MASK + ORA ]COLMASK ; {4C3B} OR WITH THE COLOR MASK + STA (GBASLO),Y ; {6C2B} STORE INTO GBAS LOW BYTE + RTS ; {6C1B} +* +LWP DS 1,1 ; {OC1B} BYTE TO DETERMIN WORK PAGE +* +** THE FOLLOWING TABLE HELPS WITH FASTER PLOTTING TO THE +** LOW RESOLUTION SCREEN. +* +LROFF DW $400,$480,$500,$580,$600,$680,$700,$780 ; {0C16B} + DW $428,$4A8,$528,$5A8,$628,$6A8,$728,$7A8 ; {0C16B} + DW $450,$4D0,$550,$5D0,$650,$6D0,$750,$7D0 ; {0C16B} +* +]EOF +``` + + + +--- + + + +## LoRes Macros and Subroutines + + + +The MAC.LORES.ASM file contains all of the macros used for manipulating low-resolution graphics. All subroutines, save for `LOCPLOT` in the collection header file, are located in their own respective files. + + + +`LISTING 8.02: The MAC.LORES.ASM Heading` + +```assembly +*``````````````````````````````* +* MAC.LORES * +* * +* THIS IS A MACRO LIBRARY FOR * +* LOW RESOLUTION GRAPHICS. * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 12-MAY-2021 * +* ASSEMBLER: MERLIN 8 PRO * +* OS: DOS 3.3 * +* * +* SUBROUTINE FILES NEEDED * +* * +* SUB.LRBLINE * +* SUB.LRCHAR * +* SUB.LRCIRCLE * +* SUB.LRGETPIX * +* SUB.LRGFCLR * +* SUB.LRGPCLR * +* SUB.LRHLINE * +* SUB.LRPLOT * +* SUB.LRVLINE * +* * +* LIST OF MACROS * +* * +* LWORKPG : SET DRAWING PAGE * +* LVIEWPG : SET VIEWING PAGE * +* LRGF : INIT FULL SCREEN * +* LRGP : INIT PART SCREEN * +* LFCLR : CLEAR FULL SCREEN * +* LPCLR : CLEAR MIXED SCREEN * +* LPLOT : PLT TO SCREEN * +* LLINE : DIAGONAL LINE * +* LCIRC : LORES CIRCLE * +* LVLIN : LORES VERT LINE * +* LHLIN : LORES HORIZ LINE * +* LRGET : GET COLOR OF PIXEL * +* LCHAR : OUTPUT LORES TEXT * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +``` + + + +--- + + + +### THE LWORKPG MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ----------------------------- | +| Name | `LWORKPG` | +| Type | Macro | +| File | `MAC.LORES.ASM` | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | set the current plotting page | +| Input | ]1 = page number (#1 or #2) | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 27+ | +| Bytes | 20 | +| Notes | none | +| See Also | `LVIEWPG` | + +--- + +*DETAILS* + +The `LWORKPG` macro allows the user to set which low-resolution graphics page to plot on; this is limited to page **#1** or page **#2**. If the work page is set to the same page as the viewing page (see `LVIEWPG`), then plotting will happen on screen in the order a plotting routine is called. Likewise, if the work page is different from the viewing page, the plotting will not be immediately visible; the viewing page must be switched to the same as the working page in order for the result of plotting to be seen. This can be useful for animation, or holding a static page in memory that is easily flipped to. + + + +`LISTING 8.03: The LWORKPG Macro Source` + +```assembly +* +*``````````````````````````````* +* LWORKPG * +* * +* SET THE WORKING PAGE TO * +* EITHER PAGE 1 OR PAGE 2. * +* THIS MEANS THAT ALL COMMANDS * +* IN THE LORES LIBRARY WILL * +* PLOT TO THIS PAGE, NO MATTER * +* WHICH PAGE IS BEING VIEWED. * +* * +* PARAMETERS: * +* * +* ]1 = PAGE NUMBER * +* * +* CYCLES: 27+ * +* SIZE: 20 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +LWORKPG MAC + LDA ]1 ; {4C3B} LOAD PAGE NUMBER + CMP #2 ; {3C2B} IF IT IS NOT PAGE 2, + BNE ]__P1 ; {3C2B} THEN ASSUME PAGE 1 + LDA #2 ; {3C2B} ELSE SET WORKING PAGE + STA LWP ; {4C3B} TO PAGE 2 + JMP ]EXIT ; {3C3B} +]__P1 + LDA #1 ; {3C2B} SET WORKING PAGE TO PAGE 1 + STA LWP ; {4C3B} +]EXIT + <<< +* +``` + + + +--- + + + +### THE LVIEWPG MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------- | +| Name | `LVIEWPG` | +| Type | Macro | +| File | `MAC.LORES.ASM` | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | set the current viewing page | +| Input | ]1 = page number (#1 or #2) | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 21+ | +| Bytes | 16 | +| Notes | none | +| See Also | `LWORKPG` | + +--- + +*DETAILS* + +The `LVIEWPG` macro sets the current viewing page, either page 1 (**#1** being passed) or page 2 (**#2** being passed). The viewing page is the page that the end user sees; thus, if the working page is page 1, and the viewing page is also page 1, then the user will see plotting to the screen as it happens. In most cases, this is not an issue, as the speed of the subroutines are adequate enough to make most low-resolution operations seem nearly instantaneous. However, in cases like heavy animation, it would be better to set the working page to a different page than the viewing page, then flipping the viewing page once all of the plotting has finished on the working page. This leads to smoother animation, and the plotting is, essentially, truly instantaneous to the viewer. + + + +`LISTING 8.04: The LVIEWPG Macro Source` + +```assembly +* +*``````````````````````````````* +* LVIEWPG * +* * +* SET THE VIEWING PAGE FOR LOW * +* RSEOLUTION GRAPHICS. * +* * +* PARAMETERS: * +* * +* ]1 = PAGE NUMBER * +* * +* CYCLES: 21+ * +* SIZE: 16 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +LVIEWPG MAC + LDA ]1 ; {4C3B} GET PAGE NUMBER PASSED + CMP #2 ; {3C2B} IF IT ISN'T 2, THEN + BNE ]P1 ; {3C2B} ASSUME VIEWING PAGE 2 + BIT VPG2 ; {4C3B} SET VIEWING PAGE TO PAGE 2 + JMP ]EXIT ; {3C3B} +]P1 + BIT VPG1 ; {4C3B} SET VIEW PAGE TO PAGE 1 +]EXIT + <<< +* +``` + + + +--- + + + +### THE LRGF MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ----------------------------------- | +| Name | `LRGF` | +| Type | Macro | +| File | `MAC.LORES.ASM` | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | set full screen low resolution mode | +| Input | none | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 12+ | +| Bytes | 9 | +| Notes | none | +| See Also | `LRGP` | + +--- + +*DETAILS* + +The `LRGF` macro sets the graphics to full-screen, low resolution mode, with columns numbering 0..39 and rows numbering 0..47. + + + +`LISTING 8.05: The LRGF Macro Source` + +```assembly +* +*``````````````````````````````* +* LRGF * +* * +* SET LOW-RESOLUTION MODE WITH * +* FULL-SCREEN 40X48 RESOLUTION * +* * +* PARAMETERS: * +* * +* NONE * +* * +* CYCLES: 12 * +* SIZE: 9 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +LRGF MAC + BIT LORES ; {4C3B} + BIT MIXEDOFF ; {4C3B} + BIT TEXTOFF ; {4C3B} GRAPHICS SOFT SWITCH + <<< +* +``` + + + +--- + + + +### THE LRGP MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------- | +| Name | `LRGP` | +| Type | Macro | +| File | `MAC.LORES.ASM` | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | set partial/mixed low resolution mode | +| Input | none | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 12+ | +| Bytes | 9 | +| Notes | none | +| See Also | `LRGF` | + +--- + +*DETAILS* + +The `LRGF` macro sets the graphics to partial, low resolution mode (mixed mode), with columns numbering 0..39 and rows numbering 0..39. This mode allows for four lines of text at the bottom of the screen. + + + +`LISTING 8.06: The LRGP Macro Source` + +```assembly +* +*``````````````````````````````* +* LRGP * +* * +* SETS THE GRAPHICS MODE TO * +* LORES WITH FOR BOTTOM LINES * +* OF TEXT. * +* * +* PARAMETERS: * +* * +* NONE * +* * +* CYCLES: 12 * +* SIZE: 9 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +LRGP MAC + BIT LORES ; {4C3B} + BIT MIXEDON ; {4C3B} + BIT TEXTOFF ; {4C3B} GRAPHICS SOFT SWITCH + <<< +* +``` + + + +--- + + + +### THE LFCLR MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------------- | +| Name | `LFCLR` | +| Type | Macro | +| File | `MAC.LORES.ASM` | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | fill the screen with a given color | +| Input | ]1 = Color code | +| Output | none | +| Dependencies | `LRGFCLR` | +| Flags Destroyed | NZCV | +| Cycles | 139+ | +| Bytes | 92 | +| Notes | none | +| See Also | `LPCLR` `LRGFCLR` `LRGF` | + +--- + +*DETAILS* + +The `LFCLR` macro clears the screen in full screen mode, filling it with the specified color in the parameter. Note that this should only be used when the low resolution graphics mode has been set to full screen, not mixed mode. For the latter, see `LPCLR`. + + + +`LISTING 8.07: The LFCLR Macro Source` + +``` +* +*``````````````````````````````* +* LFCLR * +* * +* CLEAR THE LOW RESOLUTION * +* SCREEN IN FULL SCREEN MODE * +* WITH A GIVEN COLOR. * +* * +* PARAMETERS: * +* * +* ]1 = FILL COLOR * +* * +* CYCLES: 135+ * +* SIZE: 92 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +LFCLR MAC + LDA ]1 ; {4C3B} + JSR LRGFCLR ; {131C89B} + <<< +* +``` + + + +--- + + + +### THE LRGFCLR SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------------- | +| Name | `LRGFCLR` | +| Type | Subroutine | +| File | SUB.LRGFCLR.ASM | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | fill the screen with a given color | +| Input | .A = Color code | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 125+ | +| Bytes | 86 | +| Notes | none | +| See Also | `LPCLR` `LRGFCLR` `LRGF` | + +--- + +*DETAILS* + +The `LRGFCLR` subroutine accepts a color code passed in the .A register and fill the entire screen (or page) with that color. This should be used only when the graphics mode is set to low resolution and full screen, as set by the `LRGF` macro. + + + +`LISTING 8.08: The LRGFCLR Subroutine Source` + +```assembly +*``````````````````````````````* +* LRGFCLR (NATHAN RIGGS) * +* * +* FILLS THE LORES SCREEN WITH * +* THE SPECIFIED COLOR. * +* * +* INPUT: * +* * +* .A = BACKGROUND COLOR * +* * +* NONE * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 125+ * +* SIZE: 86 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]C EQU VARTAB ; TEMPORARY COLOR STORAGE +* +LRGFCLR + STA ]C ; {4C3B} STORE COLOR + ASL ; {2C1B} SHIFT LOW BYTE OF COLOR + ASL ; {2C1B} INTO THE HIGH BYTE, SINCE + ASL ; {2C1B} THE HIGH BYTE IS ALWAYS 0 + ASL ; {2C1B} + CLC ; {2C1B} CLEAR CARRY + ADC ]C ; {4C3B} ADD LOW BYTE TO NEW HIGH + STA ]C ; {4C3B} SO IT'S A REPEATING NIBBLE + LDY #$78 ; {3C2B} BYTE LENGTH FOR EACH LINE + ; THIS ALLOWS FOR FILLING EVERY PIXEL + LDX LWP ; {4C3B} CHECK WHICH PAGE TO CLEAR + CPX #2 ; {3C2B} IS IT PAGE 2? + BEQ :P2 ; {3C2B} IF SO, CLEAR PAGE 2 + ; OTHERWISE, ASSUME PAGE 1 +:LP1 + STA $400,Y ; {4C3B} PLOT FIRST SECTION + STA $480,Y ; {4C3B} PLOT SECOND + STA $500,Y ; {4C3B} THIRD + STA $580,Y ; {4C3B} FOURTH + STA $600,Y ; {4C3B} FIFTH + STA $680,Y ; {4C3B} SIXTH + STA $700,Y ; {4C3B} SEVENTH + STA $780,Y ; {4C3B} EIGHTH + DEY ; {2C1B} DECREASE OFFSET COUNTER + BPL :LP1 ; {3C2B} IF NOT NEGATIVE, KEEP LOOPING + JMP :EXIT ; {3C3B} +:P2 + LDA ]C ; {4C3B} + LDY #$78 ; {3C2B} +:LP2 + STA $800,Y ; {4C3B} PLOT FIRST SECTION + STA $880,Y ; {4C3B} PLOT SECOND + STA $900,Y ; {4C3B} THIRD + STA $980,Y ; {4C3B} FOURTH + STA $0A00,Y ; {4C3B} FIFTH + STA $0A80,Y ; {4C3B} SIXTH + STA $0B00,Y ; {4C3B} SEVENTH + STA $0B80,Y ; {4C3B} EIGHTH + DEY ; {2C1B} DECREASE OFFSET COUNTER + BPL :LP2 ; {3C2B} IF NOT NEGATIVE, KEEP LOOPING +:EXIT + RTS ; {6C1B} +``` + + + +--- + + + +### THE LPCLR MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------------- | +| Name | `LPCLR` | +| Type | Macro | +| File | `MAC.LORES.ASM` | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | fill the screen with a given color | +| Input | ]1 = Color code | +| Output | none | +| Dependencies | `LRGPCLR` | +| Flags Destroyed | NZCV | +| Cycles | 147+ | +| Bytes | 100 | +| Notes | none | +| See Also | `LFCLR` `LRGPCLR` `LRGP` | + +--- + +*DETAILS* + +The `LPCLR` macro clears the screen in partial screen mode (mixed mode), filling it with the specified color in the parameter. Note that this should only be used when the low resolution graphics mode has been set to partial screen, not full screen. For the latter, see `LPCLR`. + + + +`LISTING 8.09: The LPCLR Macro Source` + +``` +* +*``````````````````````````````* +* LPCLR * +* * +* CLEAR A PARTIAL LORES SCREEN * +* WITH A GIVEN COLOR. * +* * +* PARAMETERS: * +* * +* ]1 = FILL COLOR * +* * +* CYCLES: 147+ * +* SIZE: 100 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +LPCLR MAC + LDA ]1 ; {4C3B} + JSR LRGPCLR ; {143C97B} + <<< +* +``` + + + +--- + + + +### THE LRGPCLR SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | --------------------------------------------- | +| Name | `LRGPCLR` | +| Type | Subroutine | +| File | SUB.LRGPCLR.ASM | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | fill the mixed mode screen with a given color | +| Input | .A = Color code | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 137+ | +| Bytes | 94 | +| Notes | none | +| See Also | `LPCLR` `LRGFCLR` `LRGP` | + +--- + +*DETAILS* + +The `LRGPCLR`subroutine is used to clear a mixed mode, or partial, low resolution graphics page. The screen is filled with the color passed via the **.A** register except for the four text lines at the bottom of the screen, which remain untouched. This subroutine should be used only when mixed mode is in use, as initialized by the `LRGP` macro. + +`LISTING 8.10: The LRGPCLR Subroutine Source` + +```assembly +*``````````````````````````````* +* LRGPCLR (NATHAN RIGGS) * +* * +* FILLS THE LORES SCREEN WITH * +* THE SPECIFIED COLOR. * +* * +* INPUT: * +* * +* .A = BACKGROUND COLOR * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 137+ * +* SIZE: 94 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]C EQU VARTAB ; TEMPORARY COLOR STORAGE +* +LRGPCLR + STA ]C ; {4C3B} STORE COLOR + ASL ; {2C1B} SHIFT LOW BYTE OF COLOR + ASL ; {2C1B} INTO THE HIGH BYTE, SINCE + ASL ; {2C1B} THE HIGH BYTE IS ALWAYS 0 + ASL ; {2C1B} + CLC ; {2C1B} CLEAR CARRY + ADC ]C ; {4C3B} ADD LOW BYTE TO NEW HIGH + STA ]C ; {4C3B} SO IT'S A REPEATING NIBBLE + LDY #$78 ; {3C2B} BYTE LENGTH FOR EACH LINE + ; THIS ALLOWS FOR FILLING EVERY PIXEL +* + LDX LWP ; {4C3B} CHECK WHICH PAGE TO CLEAR + CPX #2 ; {3C2B} IS IT PAGE 2? + BEQ :P2 ; {3C2B} IF SO, CLEAR PAGE 2 + ; OTHERWISE, ASSUME PAGE 1 +* +:LP1 + STA $400,Y ; {4C3B} PLOT FIRST SECTION + STA $480,Y ; {4C3B} PLOT SECOND + STA $500,Y ; {4C3B} THIRD + STA $580,Y ; {4C3B} FOURTH + CPY #80 ; {3C2B} + BPL :NDB ; {3C2B} + STA $600,Y ; {4C3B} FIFTH + STA $680,Y ; {4C3B} SIXTH + STA $700,Y ; {4C3B} SEVENTH + STA $780,Y ; {4C3B} EIGHTH +:NDB + DEY ; {2C1B} DECREASE OFFSET COUNTER + BPL :LP1 ; {3C2B} IF NOT NEGATIVE, KEEP LOOPING + JMP :EXIT ; {3C3B} +* +:P2 + LDA ]C ; {4C3B} + LDY #$78 ; {3C2B} +:LP2 + STA $800,Y ; {4C3B} PLOT FIRST SECTION + STA $880,Y ; {4C3B} PLOT SECOND + STA $900,Y ; {4C3B} THIRD + STA $980,Y ; {4C3B} FOURTH + CPY #80 ; {3C2B} + BPL :NDB2 ; {3C2B} + STA $0A00,Y ; {4C3B} FIFTH + STA $0A80,Y ; {4C3B} SIXTH + STA $0B00,Y ; {4C3B} SEVENTH + STA $0B80,Y ; {4C3B} EIGHTH +:NDB2 + DEY ; {2C1B} DECREASE OFFSET COUNTER + BPL :LP2 ; {3C2B} IF NOT NEG, KEEP LOOPING +:EXIT + RTS ; {6C1B} +``` + + + +--- + + + +### THE LPLOT MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `LPLOT` | +| Type | Macro | +| File | `MAC.LORES.ASM` | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | plot a point on low resolution screen | +| Input | ]1 = X-coordinate
]2 = Y-coordinate
]3 = Color code | +| Output | none | +| Dependencies | `LRPLOT` | +| Flags Destroyed | NZCV | +| Cycles | 148+ | +| Bytes | 107 | +| Notes | none | +| See Also | none | + +--- + +*DETAILS* + +The `LPLOT` macro plots a low-resolution pixel at the given X,Y coordinate in the specified color. + + + +`LISTING 8.11: The LPLOT Macro Source` + +```assembly +* +*``````````````````````````````* +* LPLOT * +* * +* PLOT A PIXEL TO THE LORES * +* SCREEN IN THE GIVEN COLOR AT * +* THE GIVEN COORDINATES. * +* * +* PARAMETERS: * +* * +* ]1 = X COORDINATE * +* ]2 = Y COORDINATE * +* ]3 = COLOR * +* * +* CYCLES: 148+ * +* SIZE: 107 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +LPLOT MAC + LDX ]1 ; {4C3B} + LDY ]2 ; {4C3B} + LDA ]3 ; {4C3B} + JSR LRPLOT ; {136C98B} + <<< +* +``` + + + +--- + + + +### THE LRPLOT SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `LRPLOT` | +| Type | Subroutine | +| File | SUB.LRPLOT.ASM | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | Plot a point to the low resolution screen | +| Input | BPAR1 = Color
BPAR2 = X-coordinate
BPAR3 = Y-coordinate | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 130+ | +| Bytes | 95 | +| Notes | none | +| See Also | `LPLOT` | + +--- + +*DETAILS* + +The `LRPLOT` subroutine plots a point in a given color at the provided X, Y coordinate. + + + +`LISTING 8.12: The LRPLOT Subroutine Source` + +```assembly +*``````````````````````````````* +* LRPLOT (NATHAN RIGGS) * +* * +* PLOTS A LOW RESOLUTION * +* PIXEL AT THE GIVEN COLOR AND * +* COORDINATES. * +* * +* INPUT: * +* * +* BPAR1 = COLOR * +* BPAR2 = X COORDINATE * +* BPAR3 = Y COORDINATE * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 130+ * +* SIZE: 95 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]COLOR EQU BPAR1 ; PLOT COLOR +]X EQU BPAR2 ; X COORDINATE +]Y EQU BPAR3 ; Y COORDINATE +]MASK EQU VARTAB ; MASK FOR ISOLATING NIBBLE +]COLMASK EQU VARTAB+2 ; COLOR MASK +]PAGEOFF EQU VARTAB+4 ; PAGE OFFSET +* +LRPLOT + STY ]Y ; {3C2B} Y POSITION PASSED IN .Y + STX ]X ; {3C2B} X POSITION PASSED IN .X + STA ]COLOR ; {3C2B} COLOR PASSED IN .A + LDA ]COLOR ; {3C2B} TAKE THE COLOR SENT + ASL ; {2C1B} AND MOVE IT LEFT 4 BITS + ASL ; {2C1B} TO THE HIGH BYTE + ASL ; {2C1B} + ASL ; {2C1B} + CLC ; {2C1B} CLEAR CARRY + ADC ]COLOR ; {3C2B} NOW ADD LOW BYTE BACK, MEANING + STA ]COLOR ; {3C2B} THE COLOR WILL REPEAT NIBBLES + LDA LWP ; {4C3B} TEST WHICH PAGE IS THE WORKING + CMP #2 ; {3C2B} PAGE--IF NOT #2, ASSUME PAGE 1 + BNE :PG1 ; {3C2B} + LDA #4 ; {3C2B} ADD TO BASE TO PLOT TO PAGE 2 + STA ]PAGEOFF ; {4C3B} STORE AS PAGE OFFSET + JMP :CNT ; {3C3B} +:PG1 + LDA #0 ; {3C2B} PAGE 1 HAS NO OFFSET, SO #0 + STA ]PAGEOFF ; {4C3B} +:CNT + LDA ]Y ; {3C2B} GET Y COORDINATE + LSR ; {2C1B} SHIFT BOTTOM BIT TO CARRY + BCC :EVEN ; {3C2B} IF CARRY = 0, ROW IS EVEN + LDX #$F0 ; {3C2B} ELSE, IT IS ODD; SO MASK + ; THE LEFT NIBBLE + BCS :PLOT ; {3C2B} IF CARRY SET, BR TO PLOTTING +:EVEN + LDX #$0F ; {3C2B} EVEN, SO MASK LOW BYTE +:PLOT + STX ]MASK ; {4C3B} STORE THE EVEN OR ODD MASK + ASL ; {2C1B} SHIFT CARRY BACK INTO BYTE + TAY ; {2C1B} HOLD VALUE INTO .Y + LDA LROFF,Y ; {5C3B} GET LORES MEMORY ADDRESS + CLC ; {2C1B} CLEAR THE CARRY + ADC ]X ; {3C2B} ADD THE X COORDINATE + STA GBASLO ; {4C3B} STORE LOW BYTE FOR GBASCALC + INY ; {2C1B} INCREASE Y OFFSET + LDA LROFF,Y ; {5C3B} GET LORESS MEMORY ADDRESS + ADC ]PAGEOFF ; {4C3B} ADJUST FOR PAGE & CARRY HIGH + STA GBASHI ; {4C3B} STORE HIGH BYTE FOR GBASCALC + LDY #0 ; {3C2B} + LDA ]MASK ; {4C3B} RELOAD THE MASK + EOR #$FF ; {2C2B} EXCLUSIVE OR THE MASK + AND (GBASLO),Y ; {6C2B} AND THE LOW FOR GBAS + STA ]COLMASK ; {4C3B} STORE THE COLOR MASK + LDA ]COLOR ; {3C2B} LOAD THE COLOR + AND ]MASK ; {4C3B} AND THE MASK + ORA ]COLMASK ; {4C3B} OR WITH THE COLOR MASK + STA (GBASLO),Y ; {6C2B} STORE INTO GBAS LOW BYTE + RTS ; {6C1B} +``` + + + +--- + + + +### THE LLINE MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `LLINE` | +| Type | Macro | +| File | `MAC.LORES.ASM` | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | plot a diagonal line on low resolution screen | +| Input | ]1 = X-origin
]2 = X-destination
]3 = Y-origin
]4 = Y-destination
]5 = color | +| Output | none | +| Dependencies | `LRBLINE` | +| Flags Destroyed | NZCV | +| Cycles | 441+ | +| Bytes | 297 | +| Notes | none | +| See Also | `LRBLINE` | + +--- + +*DETAILS* + +The `LLINE` macro creates a line with an arbitrary slope in low-resolution graphics mode. + + + +`LISTING 8.13: The LLINE Macro Source` + +```assembly +* +*``````````````````````````````* +* LLINE * +* * +* CREATES A DIAGONAL LINE IN * +* LORES GRAPHICS MODE VIA THE * +* BRESSANHAM LINE ALGORITHM. * +* * +* PARAMETERS: * +* * +* ]1 = X ORIGIN * +* ]2 = X DESTINATION * +* ]3 = Y ORIGIN * +* ]4 = Y DESTINATION * +* ]5 = COLOR * +* * +* CYCLES: 441+ * +* SIZE: 297 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +LLINE MAC + LDA ]1 ; {4C3B} + STA WPAR1 ; {3C2B} + LDA ]2 ; {4C3B} + STA WPAR1+1 ; {3C2B} + LDA ]3 ; {4C3B} + STA WPAR2 ; {3C2B} + LDA ]4 ; {4C3B} + STA WPAR2+1 ; {3C2B} + LDA ]5 ; {4C3B} + STA BPAR1 ; {3C2B} + JSR LRBLINE ; {400C152B} + <<< +* +``` + + + +--- + + + +### THE LRBLINE SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `LRBLINE` | +| Type | Subroutine | +| File | SUB.LRBLINE.ASM | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | plot a diagonal low resolution line | +| Input | WPAR1 = X-origin
WPAR1+1 = Y-origin
WPAR2 = X-destination
WPAR2+1 = Y-destination
BPAR1 = Color | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 400+ | +| Bytes | 152 | +| Notes | none | +| See Also | `LLINE` | + +--- + +*DETAILS* + +The `LRBLINE` subroutine uses Bressenham's line algorithm to create a line with an arbitrary slope on the low-resolution screen. + + + +`LISTING 8.14: The LRBLINE Subroutine Source` + +```assembly +*``````````````````````````````* +* LRBLINE (NATHAN RIGGS) * +* * +* THIS SUBROUTINE USES THE * +* BRESSENHAM LINE ALGORITHM TO * +* DRAW A DIAGONAL LINE FROM * +* PONT X1,Y1 TO X2,Y2 IN THE * +* SPECIFIED COLOR. * +* * +* INPUT: * +* * +* WPAR1 = X START POSITION * +* WPAR1+1 = Y START POSITION * +* WPAR2 = X ENDING POINT * +* WPAR2+1 = Y ENDING POINT * +* BPAR1 = COLOR OF LINE * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 400+ * +* SIZE: 152 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]X0 EQU WPAR1 ; STARTING X POINT +]Y0 EQU WPAR1+1 ; STARTING Y POINT +]X1 EQU WPAR2 ; ENDING X POINT +]Y1 EQU WPAR2+1 ; ENDING Y POINT +]COLOR EQU BPAR1 ; COLOR OF LINE +* +]DX EQU ADDR1 ; CHANGE IN X +]DY EQU ADDR1+1 ; CHANGE IN Y +]SX EQU ADDR2 ; X POSITION STEP +]SY EQU ADDR2+1 ; Y POSITION STEP +]ERR EQU ADDR3 ; SLOPE ERROR +]ERRX2 EQU ADDR3+1 ; COMPARISON COPY OF ]ERR +* +LRBLINE + LDA ]X1 ; {3C2B} SUBTRACT X0 FROM X1 + SEC ; {2C1B} + SBC ]X0 ; {3C2B} + BPL :ABSF1 ; {3C2B} IF POS, SKIP ABSOLUTE VALUE + SEC ; {2C1B} SUBTRACT 1 AND EOR #$FF + SBC #1 ; {3C2B} TO GET THE ABSOLUTE VALUE + EOR #$FF ; {2C2B} +:ABSF1 + STA ]DX ; {3C2B} STORE VALUE AS CHANGE IN X +* + LDA ]Y1 ; {3C2B} SUBTRACT Y0 FROM Y1 + SEC ; {2C1B} + SBC ]Y0 ; {3C2B} + BPL :ABSF2 ; {3C2B} IF POSITIVE, SKIP ABS VALUE + SEC ; {2C1B} SUBTRACT 1 AND EOR #$FF + SBC #1 ; {3C2B} TO GET THE ABSOLUTE VALUE + EOR #$FF ; {2C2B} +:ABSF2 + STA ]DY ; {3C2B} STORE VALUE AS CHANGE IN Y +* + LDA ]DX ; {3C2B} ]ERR = DX - DY + SEC ; {2C1B} + SBC ]DY ; {3C2B} + STA ]ERR ; {3C2B} +* + LDX #$FF ; {3C2B} .X = -1 + LDA ]X0 ; {3C2B} IF X0 >= X1 + CMP ]X1 ; {3C2B} + BCS :NONEG ; {3C2B} THEN SKIP CHANGE IN .X + LDX #$01 ; {3C2B} ELSE, CHANGE .X TO +1 +:NONEG STX ]SX ; {3C2B} STORE EITHER -1 OR +1 IN SX +* + LDX #$FF ; {3C2B} .X = -1 + LDA ]Y0 ; {3C2B} IF Y0 >= Y1 + CMP ]Y1 ; {3C2B} + BCS :NONEG2 ; {3C2B} THEN SKIP CHANGE IN .X + LDX #$01 ; {3C2B} ELSE CHANGE .X TO +1 +:NONEG2 STX ]SY ; {3C2B} STORE EITHER -1 OR +1 IN SY +* +** MAIN LOOP +* +:LOOP + LDA ]COLOR ; {3C2B} .A = COLOR TO PASS + LDY ]Y0 ; {3C2B} .Y = Y POS TO PASS + LDX ]X0 ; {3C2B} .X = X POS TO PASS + JSR LOCPLOT ; {168C109B} JUMP TO PLOTTING ROUTINE +* + LDA ]X0 ; {3C2B} IF X0 != X1, KEEP LOOPING + CMP ]X1 ; {3C2B} + BNE :CONT ; {3C2B} + LDA ]Y0 ; {3C2B} IF Y0 != Y1, KEEP LOOPING + CMP ]Y1 ; {3C2B} + BNE :CONT ; {3C2B} + JMP TBLEXIT ; {3C3B} ELSE, EXIT LOOP +:CONT +* + LDA ]ERR ; {3C2B} ]ERR = ]ERR * 2 + ASL ; {2C1B} + STA ]ERRX2 ; {3C2B} +* + LDA ]DY ; {3C2B} NEGATE ]DY + EOR #$FF ; {3C2B} + CLC ; {2C1B} + ADC #1 ; {3C2B} + SEC ; {2C1B} USE SBC FOR SIGNED COMPARE + SBC ]ERRX2 ; {3C2B} + BMI :NFSETX ; {3C2B} IF N FLAG SET, GO CHECK V FLAG + BVC :GEX ; {3C2B} IF V = 0 & N = 0, VAL >= .A REG +:LTX ; N = 0 AND V = 1, SO LESS THAN + LDA ]ERR ; {3C2B} ]ERR = ]ERR - ]DY + SEC ; {2C1B} + SBC ]DY ; {3C2B} + STA ]ERR ; {3C2B} + LDA ]X0 ; {3C2B} X0 = X0 + SX + CLC ; {2C1B} + ADC ]SX ; {3C2B} + STA ]X0 ; {3C2B} + JMP :GEX ; {3C3B} +:NFSETX BVC :LTX ; {3C2B} IF N = 1 & V = 0, VAL < .A REG +:GEX ; N = 1 & V = 1, SO VAL >= .A REG +* + LDA ]ERRX2 ; {3C2B} IF ER * 2 < DX, GOTO :LTY + SEC ; {2C1B} + SBC ]DX ; {3C2B} + BMI :SKIPY ; {3C2B} IF N FLAG = 1, GO CHECK V FLAG + BVC :GEY ; {3C2B} N = 0 & V = 0, SO VAL >= .A REG +:LTY LDA ]ERR ; {3C2B} N = 0 AND V = 1, SO LESS THAN + CLC ; {2C1B} + ADC ]DX ; {3C2B} ]ERR = ]ERR + ]DX + STA ]ERR ; {3C2B} + LDA ]Y0 ; {3C2B} ]Y0 = ]Y0 + ]SY + CLC ; {2C1B} + ADC ]SY ; {3C2B} + STA ]Y0 ; {3C2B} + JMP :GEY ; {3C3B} +:SKIPY BVC :LTY ; {3C2B} IF N = 1 & V = 0, VAL < .A REG +:GEY ; {3C2B} N = 1 & V = 1, SO VAL >= .A REG +* + JMP :LOOP ; {3C3B} +TBLEXIT + RTS ; {6C1B} + +``` + + + +--- + + + +### THE LCIRC MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `LCIRC` | +| Type | Macro | +| File | `MAC.LORES.ASM` | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | plot a circle on low resolution screen | +| Input | ]1 = Center X-position
]2 = Center Y-position
]3 = Circle radius
]4 = Color | +| Output | none | +| Dependencies | `LRCIRCLE` | +| Flags Destroyed | NZCV | +| Cycles | 2437+ | +| Bytes | 520 | +| Notes | none | +| See Also | `LRCIRCLE` | + +--- + +*DETAILS* + +The `LCIRC` macro draws a circle on the low resolution screen with a center X, Y position and a given radius, plotted in the specified color. + + + +`LISTING 8.15: The LCIRC Macro Source` + +```assembly +* +*``````````````````````````````* +* LCIRC * +* * +* CREATE A CIRCLE IN LORES * +* GRAPHICS MODE AT THE CENTER * +* COORDINATES AND RADIUS GIVEN * +* AS WELL AS THE COLOR. BASED * +* ON BRESSENHAM'S CIRCLE ALGO. * +* * +* PARAMETERS: * +* * +* ]1 = CENTER X POSITION * +* ]2 = CENTER Y POSITION * +* ]3 = CIRCLE RADIUS * +* ]4 = COLOR * +* * +* CYCLES: 2437+ * +* SIZE: 520 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +LCIRC MAC + LDA ]1 ; {4C3B} + STA WPAR1 ; {3C2B} + LDA ]2 ; {4C3B} + STA WPAR2 ; {3C2B} + LDA ]3 ; {4C3B} + STA WPAR3 ; {3C2B} + LDA ]4 ; {4C3B} + STA BPAR2 ; {3C2B} + JSR LRCIRCLE ; {2410C500B} + <<< +* +``` + + + +--- + + + +### THE LRCIRCLE SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `LRCIRCLE` | +| Type | Subroutine | +| File | SUB.LRCIRCLE.ASM | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | plot a low resolution circle | +| Input | WPAR1 = X-center
WPAR2 = Y-center
WPAR3 = Radius
BPAR1 = Color | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 2404+ | +| Bytes | 457 | +| Notes | none | +| See Also | `LCIRC` | + +--- + +*DETAILS* + +The `LRCIRCLE` subroutine uses Bressenham's circle algorithm to create a circle with an X, Y center and a given radius and color. + + + +`LISTING 8.16: The LRCIRCLE Subroutine Source` + +```assembly +*``````````````````````````````* +* LRCIRCLE (NATHAN RIGGS) * +* * +* THIS SUBROUTINE DRAWS A * +* CIRCLE ON THE CURRENT WORK * +* PAGE AT THE GIVEN COORDINATE * +* AND COLOR. * +* * +* INPUT: * +* * +* WPAR1 = CIRCLE X CENTER * +* WPAR2 = CIRCLE Y CENTER * +* WPAR3 = CIRCLE RADIUS * +* BPAR1 = CIRCLE COLOR * +* * +* OUTPUT: * +* * +* NONE * +* * +* DESTROY: AXYNVBDIZCMS * +* ^^^^^ ^^^ * +* * +* CYCLES: 2404+ * +* SIZE: 457 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]XC EQU WPAR1 ; CIRCLE X CENTER +]YC EQU WPAR2 ; CIRCLE Y CENTER +]R EQU WPAR3 ; RADIUS +]COLOR EQU BPAR2 ; COLOR +* +]YY EQU VARTAB+5 ; WORKING Y POSITION +]XX EQU VARTAB+7 ; WORKING X POSITION +]DX EQU VARTAB+9 ; CHANGE IN X +]DY EQU VARTAB+11 ; CHANGE IN Y +]ERR EQU VARTAB+13 ; ERROR POSSIBILITY +]DIAM EQU VARTAB+15 ; CIRCLE DIAMETER +]XT EQU VARTAB+17 ; NEGATIVE OF X +]YT EQU VARTAB+19 ; NEGATIVE OF Y +* +LRCIRCLE +* +** FIRST, INITIALIZE VARIABLES +* + LDA #0 ; {3C2B} CLEAR YPOS + STA ]YY ; {4C3B} + LDA ]R ; {4C3B} LOAD RADIUS + STA ]XX ; {4C3B} X = RADIUS + STA ]ERR ; {4C3B} ERROR = RADIUS + ASL ; {2C1B} R * 2 + STA ]DIAM ; {4C3B} STORE DIAMETER +* +** NOW DRAW FIRST PART OF CIRCLE +* +** CALCULATE -X AND -Y +* + LDA ]XX ; {4C3B} GET XPOS + EOR #$FF ; {2C2B} NEGATE + CLC ; {2C1B} + ADC #1 ; {3C2B} + STA ]XT ; {4C3B} STORE NEGATED IN XT + LDA ]YY ; {4C3B} GET YPOS + EOR #$FF ; {2C2B} NEGATE + CLC ; {2C1B} + ADC #1 ; {3C2B} + STA ]YT ; {4C3B} STORE NEGATED IN YT +* +** PLOT XC+X,YC +* + LDA ]XC ; {4C3B} LOAD CIRCLE CENTER XPOS + CLC ; {2C1B} CLEAR CARRY + ADC ]XX ; {4C3B} ADD CURRENT XPOS + TAX ; {2C1B} + TAY ; {2C1B} TRANSER TO .Y + LDA ]YC ; {4C3B} LOAD CIRCLE CENTER YPOS + TAY ; {2C1B} + LDA ]COLOR ; {3C2B} + JSR LOCPLOT ; {168C109B} +* +** PLOT XC-X,YC +* + LDA ]XC ; {4C3B} LOAD CIRCLE CENTER XPOS + CLC ; {2C1B} CLEAR CARRY + ADC ]XT ; {4C3B} ADD NEGATED CURRENT XPOS + TAX ; {2C1B} + TAY ; {2C1B} AND .Y + LDA ]YC ; {4C3B} LOAD CIRCLE CENTER YPOS + TAY ; {2C1B} + LDA ]COLOR ; {3C2B} + JSR LOCPLOT ; {168C0B} +* +** PLOT XC,YC+X +* + LDA ]XC ; {4C3B} LOAD CIRCLE CENTER XPOS + TAY ; {2C1B} TRANSFER TO .Y + TAX ; {2C1B} AND .X + LDA ]YC ; {4C3B} LOAD CIRCLE CENTER YPOS + CLC ; {2C1B} CLEAR CARRY + ADC ]XX ; {4C3B} ADD CURRENT XPOS + TAY ; {2C1B} + LDA ]COLOR ; {3C2B} + JSR LOCPLOT ; {168C0B} +* +** PLOT XC,YC-X +* + LDA ]XC ; {4C3B} LOAD CIRCLE CENTER XPOS + TAY ; {2C1B} TRANSFER TO .Y + TAX ; {2C1B} AND .X + LDA ]YC ; {4C3B} LOAD CIRCLE CENTER YPOS + CLC ; {2C1B} CLEAR CARRY + ADC ]XT ; {4C3B} ADD NEGATED CURRENT XPOS + TAY ; {2C1B} + LDA ]COLOR ; {3C2B} + JSR LOCPLOT ; {168C0B} +* +** NOW LOOP UNTIL CIRCLE IS FINISHED +* +:LOOP +* +** CHECK IF CIRCLE FINISHED +* + LDA ]YY ; {4C3B} IF Y > X + CMP ]XX ; {3C2B} + BCC :LPCONT ; {3C2B} CONTINUE LOOPING + JMP :EXIT ; {3C3B} OTHERWISE, CIRCLE DONE +:LPCONT +:STEPY ; STEP THE Y POSITION + LDA ]YY ; {4C3B} LOAD YPOS + ASL ; {2C2B} MULTIPLY BY 2 +*CLC + ADC #1 ; {3C2B} ADD +1 + STA ]DY ; {4C3B} STORE CHANGE OF Y + INC ]YY ; {6C3B} INCREASE YPOS + LDA ]DY ; {4C3B} NEGATE + EOR #$FF ; {3C2B} + CLC ; {2C1B} + ADC #1 ; {3C2B} + ADC ]ERR ; {4C3B} ADD ERR + STA ]ERR ; {4C3B} ERR = ERR - DY + BPL :PLOT ; {3C2B} IF ERR IS +, SKIP TO PLOT +:STEPX + LDA ]XX ; {4C3B} LOAD XPOS + ASL ; {2C2B} MULTIPLY BY 2 + EOR #$FF ; {2C2B} NEGATE + CLC ; {2C1B} + ADC #1 ; {3C2B} + ADC #1 ; {3C2B} (X*2) + 1 + STA ]DX ; {4C3B} STORE CHANGE OF X + DEC ]XX ; {6C3B} DECREASE YPOS + LDA ]DX ; {4C3B} NEGATE + EOR #$FF ; {2C2B} + CLC ; {2C1B} + ADC #1 ; {3C2B} + ADC ]ERR ; {4C3B} ADD ERR + STA ]ERR ; {4C3B} ERR = ERR - DX +* +:PLOT +* +** NOW CALCULATE -X AND -Y +* + LDA ]XX ; {4C3B} + EOR #$FF ; {2C2B} NEGATE + CLC ; {2C1B} + ADC #1 ; {3C2B} + STA ]XT ; {4C3B} + LDA ]YY ; {4C3B} + EOR #$FF ; {2C2B} NEGATE + CLC ; {2C1B} + ADC #1 ; {3C2B} + STA ]YT ; {4C3B} +* +** NOW PLOT CIRCLE OCTANTS +* +** PLOT XC+X,YC+Y +* + LDA ]XC ; {4C3B} LOAD CIRCLE CENTER XPOS + CLC ; {2C1B} CLEAR CARRY + ADC ]XX ; {4C3B} ADD CURRENT XPOS + TAY ; {2C1B} TRANSFER TO .Y + TAX ; {2C1B} AND .X + LDA ]YC ; {4C3B} LOAD CIRCLE CENTER YPOS + CLC ; {2C1B} CLEAR CARRY + ADC ]YY ; {4C3B} ADD CURRENT YPOS + TAY ; {2C1B} + LDA ]COLOR ; {3C2B} + JSR LOCPLOT ; {168C0B} +* +** PLOT XC-X,YC+Y +* + LDA ]XC ; {4C3B} LOAD CIRCLE CENTER XPOS + CLC ; {2C1B} CLEAR CARRY + ADC ]XT ; {4C3B} ADD NEGATED CURRENT XPOS + TAY ; {2C1B} TRANSFER TO .Y + TAX ; {2C1B} AND TO .X + LDA ]YC ; {4C3B} LOAD CIRCLE CENTER YPOS + CLC ; {2C1B} CLEAR CARRY + ADC ]YY ; {4C3B} ADD CURRENT YPOS + TAY ; {2C1B} + LDA ]COLOR ; {3C2B} + JSR LOCPLOT ; {168C0B} +* +** PLOT XC-X,YC-Y +* + LDA ]XC ; {4C3B} LOAD CIRCLE CENTER XPOS + CLC ; {2C1B} CLEAR CARRY + ADC ]XT ; {3C2B} ADD NEGATED CURRENT XPOS + TAY ; {2C1B} TRANSFER TO .Y + TAX ; {2C1B} AND .X + LDA ]YC ; {4C3B} LOAD CIRCLE CENTER YPOS + CLC ; {2C1B} CLEAR CARRY + ADC ]YT ; {4C3B} ADD NEGATED CURRENT YPOS + TAY ; {2C1B} + LDA ]COLOR ; {3C2B} + JSR LOCPLOT ; {168C0B} +* +** PLOT XC+X,YC-Y +* + LDA ]XC ; {4C3B} LOAD CIRCLE CENTER XPOS + CLC ; {2C1B} CLEAR CARRY + ADC ]XX ; {4C3B} ADD CURRENT XPOS + TAY ; {2C1B} TRANSFER TO .Y + TAX ; {2C1B} AND .X + LDA ]YC ; {4C3B} LOAD CIRCLE CENTER YPOS + CLC ; {2C1B} CLEAR CARRY + ADC ]YT ; {4C3B} ADD NEGATE CURRENT YPOS + TAY ; {2C1B} + LDA ]COLOR ; {3C2B} + JSR LOCPLOT ; {168C0B} +* +** PLOT XC+Y,YC+X +* + LDA ]XC ; {4C3B} LOAD CIRCLE CENTER XPOS + CLC ; {2C1B} CLEAR CARRY + ADC ]YY ; {4C3B} ADD CURRENT YPOS + TAX ; {2C1B} TRANSFER TO .X + TAY ; {2C1B} AND .Y + LDA ]YC ; {4C3B} LOAD CIRCLE CENTER YPOS + CLC ; {2C1B} CLEAR CARRY + ADC ]XX ; {4C3B} ADD CURRENT XPOS + TAY ; {2C1B} + LDA ]COLOR ; {3C2B} + JSR LOCPLOT ; {168C0B} +* +** PLOT XC-Y,YC+X +* + LDA ]XC ; {4C3B} LOAD CIRCLE CENTER XPOS + CLC ; {2C1B} CLEAR CARRY + ADC ]YT ; {4C3B} ADD NEGATED CURRENT YPOS + TAX ; {2C1B} TRANSFER TO .X + TAY ; {2C1B} AND .Y + LDA ]YC ; {4C3B} LOAD CIRCLE CENTER YPOS + CLC ; {2C1B} CLEAR CARRY + ADC ]XX ; {4C3B} ADD CURRENT XPOS + TAY ; {2C1B} + LDA ]COLOR ; {3C2B} + JSR LOCPLOT ; {168C0B} +* +** PLOT XC-Y,YC-X +* + LDA ]XC ; {4C3B} LOAD CIRCLE CENTER XPOS + CLC ; {2C1B} CLEAR CARRY + ADC ]YT ; {4C3B} ADD NEGATED CURRENT YPOS + TAX ; {2C1B} TRANSFER TO .X + TAY ; {2C1B} AND .Y + LDA ]YC ; {4C3B} LOAD CIRCLE CENTER YPOS + CLC ; {2C1B} CLEAR CARRY + ADC ]XT ; {4C3B} ADD NEGATED CURRENT XPOS + TAY ; {2C1B} + LDA ]COLOR ; {3C2B} + JSR LOCPLOT ; {168C0B} +* +** PLOT XC+Y,YC-X +* + LDA ]XC ; {4C3B} LOAD CIRCLE CENTER XPOS + CLC ; {2C1B} CLEAR CARRY + ADC ]YY ; {4C3B} ADD CURRENT YPOS + TAY ; {2C1B} TRANSFER TO .Y + TAX ; {2C1B} AND .X + LDA ]YC ; {4C3B} LOAD CIRCLE CENTER YPOS + CLC ; {2C1B} + ADC ]XT ; {4C3B} ADD NEGATED CURRENT XPOS + TAY ; {2C1B} + LDA ]COLOR ; {3C2B} + JSR LOCPLOT ; {168C0B} + JMP :LOOP ; {3C3B} +:EXIT + RTS ; {6C1B} +``` + + + +--- + + + +### THE LVLIN MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `LVLIN` | +| Type | Macro | +| File | `MAC.LORES.ASM` | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | plot a vertical line to the low resolution screen | +| Input | ]1 = Y-origin
]2 = Y-destination
]3 = X-coordinate
]4 = color | +| Output | none | +| Dependencies | `LRVLINE` | +| Flags Destroyed | NZCV | +| Cycles | 250+ | +| Bytes | 164 | +| Notes | none | +| See Also | `LRVLINE` | + +--- + +*DETAILS* + +The `LVLIN` macro creates a vertical line from a Y-origin to a Y-destination at a given X-coordinate on the low resolution screen. When it is known that a line will be perfectly vertical, this macro should be used instead of the more cycle-expensive `LLINE` macro. + + + +`LISTING 8.17: The LVLIN Macro Source` + +```assembly +* +*``````````````````````````````* +* LVLIN * +* * +* CREATE A LORES VERTICAL LINE * +* FROM A Y ORIGIN TO DEST IN * +* THE GIVEN COLOR. * +* * +* PARAMETERS: * +* * +* ]1 = Y ORIGIN * +* ]2 = Y DESTINATION * +* ]3 = X COORDINATE * +* ]4 = COLOR * +* * +* CYCLES: 250+ * +* SIZE: 164 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +LVLIN MAC + LDA ]1 ; {4C3B} + STA WPAR1 ; {3C2B} + LDA ]2 ; {4C3B} + STA WPAR1+1 ; {3C2B} + LDA ]3 ; {4C3B} + STA BPAR1 ; {3C2B} + LDA ]4 ; {4C3B} + STA BPAR2 ; {3C2B} + JSR LRVLINE ; {222C144B} + <<< +* +``` + + + +--- + + + +### THE LRVLINE SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `LRCIRCLE` | +| Type | Subroutine | +| File | SUB.LRVLINE.ASM | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | plot a low resolution vertical line | +| Input | WPAR1 = Y-origin
WPAR1+1 = Y-destination
BPAR1 = X-position
BPAR2 = Color | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 216 | +| Bytes | 141 | +| Notes | none | +| See Also | `LVLIN` | + +--- + +*DETAILS* + +The `LRVLINE` subroutine plots a vertical line from an origin to a destination at the given column. When possible, this should be used instead of the `LLINE` macro, as this macro costs significantly fewer cycles and bytes. + + + +`LISTING 8.18: The LRVLINE Subroutine Source` + +```assembly +*``````````````````````````````* +* LRVLINE (NATHAN RIGGS) * +* * +* PLOT A VERTICAL LINE IN LOW * +* RESOLUTION GRAPHICS MODE. * +* * +* INPUT: * +* * +* WPAR1 = Y COORDINATE ORIGIN * +* WPAR1+1 = Y DESTINATION * +* BPAR1 = X POSITION * +* BPAR2 = COLOR TO PLOT * +* * +* OUTPUT: * +* * +* NONE * +* * +* DESTROY: AXYNVBDIZCMS * +* ^^^^^ ^^^ * +* * +* CYCLES: 216+ * +* SIZE: 141 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]Y1 EQU WPAR1 ; Y COORDINATE ORIGIN +]Y2 EQU WPAR1+1 ; Y COORDINATE DESTINATION +]X1 EQU BPAR1 ; X COORDINATE +]COLOR EQU BPAR2 ; COLOR OF LINE +]X EQU VARTAB+6 ; WORKING XPOS +]Y EQU VARTAB+8 ; WORKING YPOS +* +LRVLINE +* + LDA ]X1 ; {4C3B} LOAD ROW + STA ]X ; {4C3B} AND STORE IN WORKING VARIABLE + LDY ]Y1 ; {4C3B} LOAD Y START POS + STY ]Y ; {4C3B} STORING IN WORKING Y VAR +:LOOP + LDA ]COLOR ; {4C3B} LOAD COLOR + LDX ]X ; {4C3B} LOAD X INTO .X + LDY ]Y ; {4C3B} LOAD Y INTO .Y + JSR LOCPLOT ; {168C109B} GOSUB PLOTTING SUBROUTINE + INC ]Y ; {5C2B} INCREASE Y INDEX + LDY ]Y ; {4C3B} LOAD Y FOR COMPARISON + CPY ]Y2 ; {3C2B} IF Y < Y2 + BNE :LOOP ; {3C2B} LOOP; ELSE, CONTINUE +:EXIT + RTS ; {6C1B} +``` + + + +--- + + + +### THE LHLIN MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `LHLIN` | +| Type | Macro | +| File | `MAC.LORES.ASM` | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | plot a horizontal line to the low resolution screen | +| Input | ]1 = X-origin
]2 = X-destination
]3 = Y-coordinate
]4 = color | +| Output | none | +| Dependencies | `LRHLINE` | +| Flags Destroyed | NZCV | +| Cycles | 246+ | +| Bytes | 161 | +| Notes | none | +| See Also | `LRHLINE` | + +--- + +*DETAILS* + +The `LHLIN` macro creates a horizontal line from an X-origin to an X-destination at a given Y-coordinate on the low resolution screen. When it is known that a line will be perfectly horizonal, this macro should be used instead of the more cycle-expensive `LLINE` macro. + + + +`LISTING 8.19: The LHLIN Macro Source` + +```assembly +* +*``````````````````````````````* +* LHLIN * +* * +* CREATE A HORIZONTAL LINE IN * +* LORES MODE FROMA GIVEN X * +* ORIGIN TO DESTINATION AT A * +* Y COORDINATE, IN GIVEN COLOR * +* * +* PARAMETERS: * +* * +* ]1 = X ORIGIN * +* ]2 = X DESTINATION * +* ]3 = Y COORDINATE * +* ]4 = COLOR * +* * +* CYCLES: 246+ * +* SIZE: 161 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +LHLIN MAC + LDA ]1 ; {4C3B} + STA WPAR1 ; {3C2B} + LDA ]2 ; {4C3B} + STA WPAR1+1 ; {3C2B} + LDA ]3 ; {4C3B} + STA BPAR1 ; {3C2B} + LDA ]4 ; {4C3B} + STA BPAR2 ; {3C2B} + JSR LRHLINE ; {218C141B} + <<< +* +``` + + + +--- + + + +### THE LRHLINE SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `LRHLINE` | +| Type | Subroutine | +| File | SUB.LRHLINE.ASM | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | plot a low resolution horizontal line | +| Input | WPAR1 = X-origin
WPAR1+1 = X-destination
BPAR1 = Y-position
BPAR2 = Color | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 218+ | +| Bytes | 141 | +| Notes | none | +| See Also | `LHLIN` | + +--- + +*DETAILS* + +The `LRHLINE` subroutine plots a horizontal line from an origin to a destination at the given row. When possible, this should be used instead of the `LLINE` macro, as this macro costs significantly fewer cycles and bytes. + + + +`LISTING 8.20: The LRHLINE Subroutine Source` + +```assembly +*``````````````````````````````* +* LRHLINE (NATHAN RIGGS) * +* * +* CREATES A HORIZONTAL LINE IN * +* LOW RESOLUTION MODE FROM AN * +* X ORIGIN TO X DESTINATION AT * +* A CONSTANT Y POSITION IN THE * +* GIVEN COLOR. * +* * +* INPUT: * +* * +* WPAR1 = X ORIGIN * +* WPAR1+1 = X DESTINATION * +* BPAR1 = Y POSITION * +* BPAR2 = COLOR * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 218+ * +* SIZE: 141 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]X1 EQU WPAR1 ; X COORD SOURCE +]X2 EQU WPAR1+1 ; X COORD DESTINATION +]Y1 EQU BPAR1 ; STATIC Y POSITION +]COLOR EQU BPAR2 ; LINE COLOR +]X EQU VARTAB ; WORKING X VALUE +]Y EQU VARTAB+1 ; WORKING Y VALUE +* +LRHLINE +* + LDA ]Y1 ; {4C3B} LOAD Y COORDINATE + STA ]Y ; {4C3B} TRANSFER TO WORKING VARIABLE + LDY ]X1 ; {4C3B} LOAD X ORIGIN COORDINATE + STY ]X ; {4C3B} STORE IN WORKING VARIABLE +:LOOP + LDA ]COLOR ; {4C3B} LOAD THE PLOTTING COLOR + LDX ]X ; {4C3B} GET CURRENT X COORDINATE + LDY ]Y ; {4C3B} GET CURRENT Y COORDINATE + JSR LOCPLOT ; {168C109B} CALL PLOTTING ROUTINE + INC ]X ; {5C2B} INCREASE CURRENT X COORDINATE + LDY ]X ; {4C3B} LOAD FOR COMPARISON + CPY ]X2 ; {4C3B} IF LESS THAN X DESTINATION + BNE :LOOP ; {3C2B} REPEAT UNTIL DONE +:EXIT + RTS ; {6C1B} + +``` + + + +--- + + + +### THE LRGET MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------------------- | +| Name | `LRGET` | +| Type | Macro | +| File | `MAC.LORES.ASM` | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | get the color at an X, Y coordinate | +| Input | ]1 = X-coordinate
]2 = Y-coordinate | +| Output | none | +| Dependencies | `LRGETPIX` | +| Flags Destroyed | NZCV | +| Cycles | 110+ | +| Bytes | 71 | +| Notes | none | +| See Also | `LRGETPIX` | + +--- + +*DETAILS* + +The `LRGET` macro retrieves the color of a low-resolution pixel at the given X,Y coordinates and returns the color code in **.A**. Currently, this only works for retrieving a value from the viewing page, but this will likely change in the future. + + + +`LISTING 8.21: The LRGET Macro Source` + +```assembly +* +*``````````````````````````````* +* LRGET * +* * +* GET THE COLOR OF THE LORES * +* PIXEL AT THE GIVEN COORDS. * +* * +* PARAMETERS: * +* * +* ]1 = X COORDINATE * +* ]2 = Y COORDINATE * +* * +* CYCLES: 110+ * +* SIZE: 71 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +LRGET MAC + LDA ]1 ; {4C3B} + STA BPAR1 ; {3C2B} + LDA ]2 ; {4C3B} + STA BPAR2 ; {3C2B} + JSR LRGETPIX ; {96C61B} + <<< +* +``` + + + +--- + + + +### THE LRGETPIX SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ---------------------------------------------- | +| Name | `LRGETPIX` | +| Type | Subroutine | +| File | SUB.LRGETPIX.ASM | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | get the color of a pixel at X,Y | +| Input | BPAR1 = X-coordinate
BPAR2 = Y-coordinate | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 90+ | +| Bytes | 58 | +| Notes | none | +| See Also | `LRGET` | + +--- + +*DETAILS* + +The `LRGETPIX` subroutine retrieves the color value from a low resolution pixel at the given X, Y coordinate, passing back the color code in the .A register. Note that this currently works only for the viewing page, but this may change in future revisions. + + + +`LISTING 8.22: The LRGETPIX Subroutine Source` + +```assembly +* LRGETPIX (NATHAN RIGGS) * +* * +* THIS SUBROUTINE RETURNS THE * +* COLOR CODE OF A GIVEN LORES * +* PIXEL AT THE X,Y COORDINATE. * +* NOTE THAT IF THE ROW IS EVEN * +* THE THE COLOR CODE IS PASSED * +* BACK VIA THE HIGH BYTE, AND * +* IF THE ROW IS ODD THEN THE * +* COLOR CODE IS PASSED IN THE * +* LOW BYTE. THE UNUSED BYTE * +* FOR EACH WILL ALWAYS BE 0. * +* * +* INPUT: * +* * +* BPAR1 = X COORDINATE * +* BPAR2 = Y COORDINATE * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 90+ * +* SIZE: 58 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]HALFX EQU VARTAB ; X COORD / 2 FOR GBASCALC +]FULLX EQU VARTAB+1 ; ORIGINAL X COORD +]FULLY EQU VARTAB+2 ; ORIGINAL Y COORD +]MASK EQU VARTAB+3 ; MASK FOR DETERMINING COLOR CODE +]FULLCHAR EQU VARTAB+4 ; THE FULL CHAR A POS HALFX,Y +* +LRGETPIX +* + LDY BPAR1 ; {3C2B} LOAD X POSITION + STY ]FULLX ; {4C3B} + TYA ; {2C1B} TRANSFER T .A + ASL ; {2C1B} SHIFT LEFT TO DIVIDE IN HALF + STA ]HALFX ; {4C3B} STORE AS HALFX + LDA BPAR2 ; {3C2B} GET Y POSITION + STA ]FULLY ; {4C3B} + LDY ]HALFX ; {4C3B} LOAD HALF-X POSITION + LDA ]FULLY ; {4C3B} LOAD Y POSITION + JSR LRGBCALC ; {?C?B} GET CHARACTER AT COORDINATES + LDA (GBASLO),Y ; {6C2B} GET FULL CHAR FROM SCREEN ADDR + STA ]FULLCHAR ; {4C3B} AND STORE IN FULLCHAR + LDA ]FULLX ; {4C3B} LOAD THE LORES FULL X COORD + LSR ; {2C1B} SHIFT LEAST BYTE INTO CARRY + BCC :EVEN ; {3C2B} IF THAT BYTE IS 0, THEN GOTO EVEN + LDX #$F0 ; {3C2B} ELSE IT'S ODD; MASK IT + BCS :EXIT ; {3C2B} JUMP TO EXIT +:EVEN + LDX #$0F ; {3C2B} MASK COLOR RETURNED +:EXIT + STX ]MASK ; {4C3B} STORE THE MASK TO ]MASK + LDA ]FULLCHAR ; {4C3B} LOAD FULL CHARACTER + AND ]MASK ; {3C2B} MASK APPROPRIATE NIBBLE + STA RETURN ; {4C3B} STRE NEW BYTE IN RETURN + LDA #1 ; {3C2B} + STA RETLEN ; {4C3B} + LDA RETURN ; {4C3B} RETURN NEW COLOR BYTE IN .A + RTS ; {6C1B} +``` + + + +--- + + + +### THE LCHAR MACRO + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `LCHAR` | +| Type | Macro | +| File | `MAC.LORES.ASM` | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | plot a character to the low resolution screen | +| Input | ]1 = X-coordinate
]2 = Y-coordinate
]3 = address of 3 byte character
]4 = character color | +| Output | none | +| Dependencies | `LRCHAR` | +| Flags Destroyed | NZCV | +| Cycles | 441+ | +| Bytes | 439 | +| Notes | none | +| See Also | `LRCHAR` | + +--- + +*DETAILS* + +The `LCHAR` macro plots a character to the low resolution graphics screen, with a width of 4 pixels and a height of 6 pixels, at the specified color. + + + +`LISTING 8.23: The LCHAR Macro Source` + +```assembly +* +*``````````````````````````````* +* LCHAR * +* * +* PRINT A LORES CHARACTER TO * +* LORES SCREEN AT A GIVE COLOR * +* AT THE GIVEN COORDINATES. * +* * +* PARAMETERS: * +* * +* ]1 = X COORDINATE * +* ]2 = Y COORDINATE * +* ]3 = ADDRESS OF 3-BYTE CHAR * +* ]4 = CHARACTER COLOR * +* * +* CYCLES: 441+ * +* SIZE: 439 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +LCHAR MAC + LDA ]1 ; {4C3B} + STA BPAR1 ; {3C2B} + LDA ]2 ; {4C3B} + STA BPAR2 ; {3C2B} +*LDA #>]3 +*STA WPAR1+1 +*LDA #<]3 +*STA WPAR1 + _MLIT ]3;WPAR1 ; {16C12B} + LDA ]4 ; {4C3B} + STA BPAR3 ; {3C2B} + JSR LRCHAR ; {403C412B} + <<< +``` + + + +--- + + + +### THE LRCHAR SUBROUTINE + +_SUMMARY_ + +| Condition | Value | +| --------------- | ------------------------------------------------------------ | +| Name | `LRCHAR` | +| Type | Subroutine | +| File | SUB.LRCHAR.ASM | +| Author | Nathan Riggs | +| Last Revision | 13-MAY-2021 | +| Assembler | Merlin Pro 8 | +| OS | Apple DOS 3.3 | +| Purpose | get the color of a pixel at X,Y | +| Input | BPAR1 = X-coordinate
BPAR2 = Y-coordinate
WPAR1 = Address of Char definition | +| Output | none | +| Dependencies | none | +| Flags Destroyed | NZCV | +| Cycles | 90+ | +| Bytes | 58 | +| Notes | none | +| See Also | `LCHAR` | + +--- + +*DETAILS* + +The `LRCHAR` subroutine plots a character to the low resolution screen at the given X,Y coordinate and color. Each character has a 4x6 aspect ratio, allowing for a total of 64 characters on a full low resolution screen (allowing for space between characters). + + + +`LISTING 8.24: The LRCHAR Subroutine Source` + +```assembly +*``````````````````````````````* +* LRCHAR (NATHAN RIGGS) * +* * +* THIS SUBROUTINE PLACES A * +* LORES CHARACTER AT A GIVEN * +* POSITION AND COLOR. ALL OF * +* THE CHARACTERS ARE 4*6 IN * +* ORDER TO ALLOW EIGHT LETTERS * +* BOTH HORIZONTALLY AND ALSO * +* VERTICALLY. * +* * +* INPUT: * +* * +* BPAR1 = X POSITION OF CHAR * +* BPAR2 = Y POSITION OF CHAR * +* WPAR1 = ADDRESS OF CHAR DEF * +* * +* DESTROY: NZCIDV * +* ^^^ ^ * +* * +* CYCLES: 397+ * +* SIZE: 409 BYTES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]ORGX EQU BPAR1 ; X POSITION OF CHAR +]ORGY EQU BPAR2 ; Y POSITION OF CHAR +]CADDR EQU WPAR1 ; ADDRESS OF 3-BYTE CHAR DEFINITION +]CBYTE1 EQU VARTAB ; COPY OF 1ST BYTE +]CBYTE2 EQU VARTAB+1 ; COPY OF SECOND +]CBYTE3 EQU VARTAB+2 ; COPY OF THIRD +]CX EQU ADDR1 ; PLOTTING X POSITION +]CY EQU ADDR2 ; PLOTTING Y POSITION +]CNT EQU VARTAB+5 ; COUNTER +]COLOR EQU BPAR3 ; CHARACTER COLOR +]TMPBYTE EQU VARTAB+11 ; WORKING BYTE TO READ +]OFFSET EQU VARTAB+12 ; CHARACTER BYTE OFFSET +]NIBBLE EQU VARTAB+13 ; FLAG TO INDICATE 1ST OR 2ND NIBBLE +* +LRCHAR + LDA #0 ; {4C3B} RESET NIBBLE AND OFFSET + STA ]NIBBLE ; {4C3B} + STA ]OFFSET ; {4C3B} +* + LDA ]ORGX ; {4C3B} COPY ORGX TO X TO START + STA ]CX ; {4C3B} PLOTTING CHAR FROM LEFT TOP + LDA ]ORGY ; {4C3B} DO THE SAME WITH ORGY AND Y + STA ]CY ; {4C3B} + LDA ]CX ; {4C3B} + LDY #0 ; {3C2B} RESET BYTE INDEX + LDA (]CADDR),Y ; {6C2B} GET APPROPRIATE BYTE + STA ]CBYTE1 ; {4C3B} STORE IN DEDICATED VAR + INY ; {2C1B} NOW DO SECOND BYTE + LDA (]CADDR),Y ; {6C2B} + STA ]CBYTE2 ; {4C3B} + INY ; {2C1B} AND THE THIRD + LDA (]CADDR),Y ; {6C2B} + STA ]CBYTE3 ; {4C3B} +* + LDA #0 ; {3C2B} RESET COUNTER + STA ]CNT ; {4C3B} FOR BITS +:PRELOOP + LDY ]OFFSET +*LDA ]OFFSET ; {4C3B} GET CHAR BYTE OFFSET + LDA ]CBYTE1,Y ; {5C3B} LOAD APPROPRIATE BYTE + STA ]TMPBYTE ; {4C3B} STORE IN WORKING EMORY +:LP1 + SEC ; {2C1B} SET CARRY + LDA ]TMPBYTE ; {4C3B} LOAD WORKING BYTE + ASL ; {2C1B} SHIFT LEFT BYTE INTO CARRY + STA ]TMPBYTE ; {4C3B} STORE NEW WORKING BYTE + BCC :NOPLOT ; {3C2B} IF CARRY HOLDS A 0, DON'T PLOT + LDA ]COLOR ; {4C3B} OTHERWISE, PLOT + LDY ]CY ; {4C3B} + LDX ]CX ; {4C3B} + JSR LOCPLOT ; {174C112B} +:NOPLOT + INC ]CX ; {5C2B} INCREASE X, WHETHER PLOTTED + LDA ]CNT ; {4C3B} OR NOT + CMP #3 ; {3C2B} IF # OF BITS = 4, THEN + BEQ :NEXTLP ; {3C2B} WE'RE DONE WITH THIS NIBBLE + INC ]CNT ; {5C2B} INCREASE THE BIT COUNTER + JMP :LP1 ; {3C3B} LOOP AGAIN UNTIL NIBBLE DONE +:NEXTLP + INC ]NIBBLE ; {5C2B} NOW INCREASE TO 2ND NIBBLE + INC ]CY ; {5C2B} INCREASE Y PLOT POS, SINCE + LDA #0 ; {3C2B} EACH LINE IS 4 BITS LONG + STA ]CNT ; {4C3B} RESET COUNTER + LDA ]ORGX ; {4C3B} RESET X POSITION + STA ]CX ; {4C3B} + LDA ]NIBBLE ; {4C3B} CHECK IF NIBBLE 2 IS DONE + CMP #2 ; {3C2B} AND IF SO, + BEQ :NEXTLP2 ; {3C2B} GET OUT OF ANOTHER LOOP + JMP :LP1 ; {3C3B} ELSE, LOOP FOR 2ND NIBBLE +:NEXTLP2 + INC ]OFFSET ; {5C2B} NOW INC CHAR BYTE OFFSET + LDA #0 ; {3C2B} RESET NIBBLE TO FIRST NIBBLE + STA ]NIBBLE ; {4C3B} + LDA ]ORGX ; {4C3B} RESET X POSITION + STA ]CX ; {4C3B} + LDA #0 ; {3C2B} RESET THE BIT COUNTER + STA ]CNT ; {4C3B} + LDA ]OFFSET ; {4C3B} IF OFFSET IS MORE THAN 2, + CMP #3 ; {3C2B} THEN DONE WITH THIS LOOP + BEQ :NEXT3 ; {3C2B} ELSE START OVER FOR NEXT BYTE + JMP :PRELOOP ; {3C3B} +* +:NEXT3 + RTS ; {6C1B} +* +** WHAT FOLLOWS ARE THE BINARY REPRESENTATIONS OF EACH +** CHARACTER AVAILABLE. +* +LR_A DFB %01101001 ; {OC1B} ".XX." + ; "X..X" + DFB %11111001 ; {OC1B} "XXXX" + ; "X..X" + DFB %10010000 ; {0C1B} "X..X" + ; "...." +LR_B + DFB %11101001 ; {0C1B} "XXX." + ; "X..X" + DFB %11101001 ; {0C1B} "XXX." + ; "X..X" + DFB %11100000 ; {0C1B} "XXX." + ; "...." +LR_C + DFB %11111000 ; {0C1B} "XXXX" + ; "X..." + DFB %10001000 ; {0C1B} "X..." + ; "X..." + DFB %11110000 ; {0C1B} "XXXX" + ; "...." +LR_D + DFB %11101001 ; {0C1B} "XXX." + ; "X..X" + DFB %10011001 ; {0C1B} "X..X" + ; "X..X" + DFB %11100000 ; {0C1B} "XXX." + ; "...." +LR_E + DFB %11111000 ; {0C1B} "XXXX" + ; "X..." + DFB %11101000 ; {0C1B} "XXX." + ; "X..." + DFB %11110000 ; {0C1B} "XXXX" + ; "...." +LR_F + DFB %11111000 ; {0C1B} "XXXX" + ; "X..." + DFB %11101000 ; {0C1B} "XXX." + ; "X..." + DFB %10000000 ; {0C1B} "X..." + ; "...." +LR_G + DFB %11111000 ; {0C1B} "XXXX" + ; "X..." + DFB %10111001 ; {0C1B} "X.XX" + ; "X..X" + DFB %11110000 ; {0C1B} "XXXX" + ; "...." +LR_H + DFB %10011001 ; {0C1B} "X..X" + ; "X..X" + DFB %11111001 ; {0C1B} "XXXX" + ; "X..X" + DFB %10010000 ; {0C1B} "X..X" + ; "...." +LR_I + DFB %11110110 ; {0C1B} "XXXX" + ; ".XX." + DFB %01100110 ; {0C1B} ".XX." + ; ".XX." + DFB %11110000 ; {0C1B} "XXXX" + ; "...." +LR_J + DFB %00010001 ; {0C1B} "...X" + ; "...X" + DFB %00011001 ; {0C1B} "...X" + ; "X..X" + DFB %01100000 ; {0C1B} ".XX." + ; "...." +LR_K + DFB %10011010 ; {0C1B} "X..X" + ; "X.X." + DFB %11001010 ; {0C1B} "XX.." + ; "X.X." + DFB %10010000 ; {0C1B} "X..X" + ; "...." +LR_L + DFB %10001000 ; {0C1B} "X..." + ; "X..." + DFB %10001000 ; {0C1B} "X..." + ; "X..." + DFB %11110000 ; {0C1B} "XXXX" + ; "...." +LR_M + DFB %10111101 ; {0C1B} "X.XX" + ; "XX.X" + DFB %11011001 ; {0C1B} "XX.X" + ; "X..X" + DFB %10010000 ; {0C1B} "X..X" + ; "...." +LR_N + DFB %10011101 ; {0C1B} "X..X" + ; "XX.X" + DFB %11011011 ; {0C1B} "XX.X" + ; "X.XX" + DFB %10010000 ; {0C1B} "X..X" + ; "...." +LR_O + DFB %01101001 ; {0C1B} ".XX." + ; "X..X" + DFB %10011001 ; {0C1B} "X..X" + ; "X..X" + DFB %01100000 ; {0C1B} ".XX." + ; "...." +LR_P + DFB %11101001 ; {0C1B} "XXX." + ; "X..X" + DFB %11101000 ; {0C1B} "XXX." + ; "X..." + DFB %10000000 ; {0C1B} "X..." + ; "...." +LR_Q + DFB %01101001 ; {0C1B} ".XX." + ; "X..X" + DFB %10011011 ; {0C1B} "X..X" + ; "X.XX" + DFB %01100001 ; {0C1B} ".XX." + ; "...X" +LR_R + DFB %11101001 ; {0C1B} "XXX." + ; "X..X" + DFB %11101010 ; {0C1B} "XXX." + ; "X.X." + DFB %10010000 ; {0C1B} "X..X" + ; "...." +LR_S + DFB %01111000 ; {0C1B} ".XXX" + ; "X..." + DFB %01100001 ; {0C1B} ".XX." + ; "...X" + DFB %11100000 ; {0C1B} "XXX." + ; "...." +LR_T + DFB %11110110 ; {0C1B} "XXXX" + ; ".XX." + DFB %01100110 ; {0C1B} ".XX." + ; ".XX." + DFB %01100000 ; {0C1B} ".XX." + ; "...." +LR_U + DFB %10011001 ; {0C1B} "X..X" + ; "X..X" + DFB %10011001 ; {0C1B} "X..X" + ; "X..X" + DFB %11110000 ; {0C1B} "XXXX" + ; "...." +LR_V + DFB %10011001 ; {0C1B} "X..X" + ; "X..X" + DFB %10101010 ; {0C1B} "X.X." + ; "X.X." + DFB %01000000 ; {0C1B} ".X.." + ; "...." +LR_W + DFB %10011001 ; {0C1B} "X..X" + ; "X..X" + DFB %10111011 ; {0C1B} "XX.X" + ; "XX.X" + DFB %11010000 ; {0C1B} "X.XX" + ; "...." +LR_X + DFB %10011001 ; {0C1B} "X..X" + ; "X..X" + DFB %01101001 ; {0C1B} ".XX." + ; "X..X" + DFB %10010000 ; {0C1B} "X..X" + ; "...." +LR_Y + DFB %10011001 ; {0C1B} "X..X" + ; "X..X" + DFB %01100110 ; {0C1B} ".XX." + ; ".XX." + DFB %01100000 ; {0C1B} ".XX." + ; "...." +LR_Z + DFB %11110001 ; {0C1B} "XXXX" + ; "...X" + DFB %01101000 ; {0C1B} "..X." + ; ".X.." + DFB %11110000 ; {0C1B} "XXXX" + ; "...." +LR_0 + DFB %11111001 ; {0C1B} "XXXX" + ; "X..X" + DFB %10011001 ; {0C1B} "X..X" + ; "X..X" + DFB %11110000 ; {0C1B} "XXXX" + ; "...." +LR_1 + DFB %01100110 ; {0C1B} ".XX." + ; ".XX." + DFB %01100110 ; {0C1B} ".XX." + ; ".XX." + DFB %01100000 ; {0C1B} ".XX." + ; "...." +LR_2 + DFB %01101001 ; {0C1B} ".XX." + ; "X..X" + DFB %00100100 ; {0C1B} "..X." + ; ".X.." + DFB %11110000 ; {0C1B} "XXXX" + ; "...." +LR_3 + DFB %11100001 ; {0C1B} "XXX." + ; "...X" + DFB %01100001 ; {0C1B} ".XX." + ; "...X" + DFB %11100000 ; {0C1B} "XXX." + ; "...." +LR_4 + DFB %10011001 ; {0C1B} "X..X" + ; "X..X" + DFB %11110001 ; {0C1B} "XXXX" + ; "...X" + DFB %00010000 ; {0C1B} "...X" + ; "...." +LR_5 + DFB %11111000 ; {0C1B} "XXXX" + ; "X..." + DFB %11110001 ; {0C1B} "XXXX" + ; "...X" + DFB %11110000 ; {0C1B} "XXXX" + ; "...." +LR_6 + DFB %01101000 ; {0C1B} ".XX." + ; "X..." + DFB %11101001 ; {0C1B} "XXX." + ; "X..X" + DFB %11110000 ; {0C1B} "XXXX" + ; "...." +LR_7 + DFB %11110001 ; {0C1B} "XXXX" + ; "...X" + DFB %00100100 ; {0C1B} "..X." + ; ".X.." + DFB %10000000 ; {0C1B} "X..." + ; "...." +LR_8 + DFB %01101001 ; {0C1B} ".XX." + ; "X..X" + DFB %01101001 ; {0C1B} ".XX." + ; "X..X" + DFB %01100000 ; {0C1B} ".XX." + ; "...." +LR_9 + DFB %01111001 ; {0C1B} ".XXX" + ; "X..X" + DFB %01110001 ; {0C1B} ".XXX" + ; "...X" + DFB %00010000 ; {0C1B} "...X" + ; "...." +LR_EXC + DFB %01100110 ; {0C1B} ".XX." + ; ".XX." + DFB %01100000 ; {0C1B} ".XX." + ; "...." + DFB %01100000 ; {0C1B} ".XX." + ; "...." +LR_QUEST + DFB %01101001 ; {0C1B} ".XX." + ; "X..X" + DFB %00100000 ; {0C1B} "..X." + ; "...." + DFB %00100000 ; {0C1B} "..X." + ; "...." +LR_PRD + DFB %00000000 ; {0C1B} "...." + ; "...." + DFB %00001100 ; {0C1B} "...." + ; "XX.." + DFB %11000000 ; {0C1B} "XX.." + ; "...." +LR_CMA + DFB %00000000 ; {0C1B} "...." + ; "...." + DFB %00000100 ; {0C1B} "...." + ; ".X.." + DFB %01001000 ; {0C1B} ".X.." + ; "X..." +LR_APOST + DFB %00010001 ; {0C1B} "...X" + ; "...X" + DFB %00100000 ; {0C1B} "..X." + ; "...." + DFB %00000000 ; {0C1B} "...." + ; "...." +LR_QUOT + DFB %10101010 ; {0C1B} "X.X." + ; "X.X." + DFB %00000000 ; {0C1B} "...." + ; "...." + DFB %00000000 ; {0C1B} "...." + ; "...." +LR_COLON + DFB %00000100 ; {0C1B} "...." + ; ".X.." + DFB %00000100 ; {0C1B} "...." + ; ".X.." + DFB %00000000 ; {OC1B} "...." + ; "...." +LR_SEMI + DFB %00000100 ; {0C1B} "...." + ; ".X.." + DFB %00000100 ; {0C1B} "...." + ; ".X.." + DFB %10000000 ; {0C1B} "X..." + ; "...." +LR_MINUS + DFB %00000000 ; {0C1B} "...." + ; "...." + DFB %11111111 ; {0C1B} "XXXX" + ; "XXXX" + DFB %00000000 ; {0C1B} "...." + ; "...." +LR_PLUS + DFB %00000110 ; {0C1B} "...." + ; ".XX." + DFB %11111111 ; {0C1B} "XXXX" + ; "XXXX" + DFB %01100000 ; {0C1B} ".XX." + ; "...." +LR_EQUAL + DFB %00001111 ; {0C1B} "...." + ; "XXXX" + DFB %00001111 ; {0C1B} "...." + ; "XXXX" + DFB %00000000 ; {0C1B} "...." + ; "...." +LR_FSLASH + DFB %00010010 ; {0C1B} "...X" + ; "..X." + DFB %00100100 ; {0C1B} "..X." + ; ".X.." + DFB %01001000 ; {0C1B} ".X.." + ; "X..." +LR_BSLASH + DFB %10000100 ; {0C1B} "X..." + ; ".X.." + DFB %01000010 ; {0C1B} ".X.." + ; "..X." + DFB %00100001 ; {0C1B} "..X." + ; "...X" +LR_LPAR + DFB %00010010 ; {0C1B} "...X" + ; "..X." + DFB %01000100 ; {0C1B} ".X.." + ; ".X.." + DFB %00100001 ; {0C1B} "..X." + ; "...X" +LR_RPAR + DFB %10000100 ; {0C1B} "X..." + ; ".X.." + DFB %00100010 ; {0C1B} "..X." + ; "..X." + DFB %01001000 ; {0C1B} ".X.." + ; "X..." + +``` + + + +--- + + + +## LoRes Collection Demo + + + +Listing 8.25 contains demonstrations of how the low resolution macro collection works. + + + +`LISTING 8.25: The DEMO.LORES.ASM Source` + +```assembly +* +*``````````````````````````````* +* DEMO.LORES * +* * +* A DEMO OF THE MACROS AND * +* SUBROUTINES FOR USING LORES * +* GRAPHICS. * +* * +* AUTHOR: NATHAN RIGGS * +* CONTACT: NATHAN.RIGGS@ * +* OUTLOOK.COM * +* * +* DATE: 03-OCT-2019 * +* ASSEMBLER: MERLIN 8 PRO * +* OS: DOS 3.3 * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** ASSEMBLER DIRECTIVES +* + CYC AVE + EXP OFF + TR ON + DSK DEMO.LORES + OBJ $BFE0 + ORG $6000 +* +*``````````````````````````````* +* TOP INCLUDES (PUTS, MACROS) * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* + PUT MIN.HEAD.REQUIRED.ASM + PUT MIN.HEAD.LORES.ASM + USE MIN.MAC.REQUIRED.ASM + USE MIN.MAC.LORES.ASM +* +*``````````````````````````````* +* PROGRAM MAIN BODY * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +]COLOR EQU VARTAB+16 +]HOME EQU $FC58 +* +*``````````````````````````````* +* LO-RES GRAPHICS COLLECTION * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THIS DEMO ILLUSTRATES HOW TO USE THE MACROS DEDICATED +** TO CREATING LOW RESOLUTION GRAPHICS. THESE MACROS +** LARGELY CONSIST OF THE MOST BASIC FUNCTIONS NECESSARY +** TO BUILD MORE COMPLICATED ROUTINES, SUCH AS ANIMATION, +** SPRITES, AND SO ON. CURRENTLY, THE MACROS AVAILABLE ARE: +* +** - LVIEWPG : SET THE VIEWING PAGE +** - LWORKPG : SET THE WORKING (PLOTTING) PAGE +** - LRGF : INIT LOW RESOLUTION FULL SCREEN MODE +** - LRGP : INIT LOW RESOLUTION PARTIAL SCREEN MODE +** - LFCLR : CLEAR FULL SCREEN TO SPECIFIED COLOR +** - LPCLR : CLEAR PARTIAL SCREEN TO SPECIFIED COLOR +** - LPLOT : PLOT A COLORED PIXEL AT A GIVEN X,Y COORDINATE +** - LLINE : PLOT LINE FROM X1,Y1 TO X2,Y2 +** - LCIRC : PLOT A CIRCLE WITH A GIVEN CENTER AND RADIUS +** - LVLIN : PLOT A VERTICAL LINE +** - LHLIN : PLOT A HORIZONTAL LINE +** - LRGET : GET COLOR OF A LOW RESOLUTION PIXEL AT X,Y +** - LRCHAR : PRINT A LOW RESOLUTION CHARACTER AT X,Y +* +*``````````````````````````````* +* WORKING AND VIEWING PAGES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** WHILE USING THE LORES COLLECTION, THERE ARE TWO MODES +** TO CONSIDER WHEN PLOTTING: THE WORKING PAGE, WHICH IS +** THE PAGE BEING PLOTTED TO AT A GIVEN MOMENT, AND THE +** VIEWING PAGE, WHICH IS THE PAGE THAT IS CURRENTLY +** VISIBLE ON THE SCREEN. THESE DO NOT HAVE TO BE THE SAME +** PAGE; YOU CAN PLOT TO THE WORKING PAGE WHILE SHOWING THE +** VIEWING PAGE ON SCREEN, AND YOU CAN FLIP THE VIEWING PAGE +** TO THE WORKING PAGE AFTER THE DESIRED PLOTTING IS +** FINISHED. THIS IS MOSTLY HELPFUL FOR ANIMATION, BUT OTHER +** USES ARE BOUND TO BE FOUND. ONE OTHER SUCH USE IS HOLDING +** A STATIC PAGE IN MEMORY TO BE FLIPPED ON OR OFF DEPENDING +** ON THE STATE OF A PROGRAM. +* +** UNFORTUNATELY, MERLIN 8 PRO USES THE SECOND PAGE FOR +** STORING DATA OF ITS OWN, SO ITS USE CANNOT BE SHOWN HERE +** WITHOUT POSSIBLY DESTROYING DATA IN RAM OR PERHAPS EVEN +** ON THE DISK. THUS WE ARE LEFT TO ONLY SHOW THE FORMAT OF +** THE COMMANDS USED. +* +** TO SET THE WORKING PAGE, YOU WOULD USE THE +** LWORKPG MACRO, AS SUCH: +* +** LWORKPG #2 +* +** WHICH SETS THE WORKING PAGE TO PAGE 2. TO SET +** THE VIEWING PAGE, YOU WOULD USE THE LVIEWPG MACRO: +* +** LVIEWPG #1 +* +** WHICH WOULD SET THE VIEWING PAGE TO PAGE 1. NOTE THAT +** BOTH THE WORKING PAGE AND THE VIEWING PAGE ALWAYS +** DEFAULT TO PAGE 1. +* +*```````````````````````````````* +* LORES GRAPHICS INITIALIZATION * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THERE ARE TWO MACROS FOR INITIALIZING LORES +** GRAPHICS MODE: LRFGR AND LRPGR. THE LRFGR MACRO, +** WHICH STANDS FOR ^LORES FULL GRAPHICS^, INITIALIZES +** THE FULL SCREEN GRAPHICS MODE IN LOW RESOLUTION, +** WHEREAS THE LRPGR MACRO, WHICH STANDS FOR LORES +** PARTIAL GRAPHICS, INITIALIZES THE PARTIAL (OR MIXED) +** GRAPHICS MODE, WHICH RESERVES FOR ROWS OF TEXT SPACE +** AT THE BOTTOM OF THE SCREEN. +* +** NEITHER OF THESE MACROS HAVE ANY PARAMETERS. THE +** LRFGR MACRO CREATES A 40 X 48 LORES SCREEN, AND THE +** LRPGR MACRO CREATES A 40 X 40 MATRIX LORES SCREEN. +* +*``````````````````````````````* +* CLEARING THE LORES SCREEN * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** LIKE WITH INTIALIZING THE GRAPHICS MODE, THERE ARE +** TWO MACROS DEDICATED TO CLEARING THE SCREEN: LRFCLR +** AND LRPCLR. BOTH OF THESE FUNCTION THE SAME, WITH +** THE EXCEPTION THAT THE LRFCLR MACRO CLEARS THE WHOLE +** SCREEN AND THE LRPCLR MACRO CLEARS ALL BUT THE +** BOTTOM FOUR ROWS (MIXED MODE). IN THIS DEMO, WE WILL +** BE DEALING EXCLUSIVELY WITH FULL SCREEN MODE. +* +** BOTH OF THE SCREEN CLEARING MACROS ACCEPT A PARAMETER +** THAT INDICATES THE COLOR TO FILL THE SCREEN WITH. +* +** NOW WE CAN UTILIZE THE MACROS ABOVE TO SHOW HOW THE +** SCREEN CAN BE CLEARED WITH EVERY COLOR AVAILABLE +** IN LOW RESOLUTION GRAPHICS MODE. +* + JSR ]HOME + LWORKPG #1 ; SET WORKING PAGE AND + LVIEWPG #1 ; TO PAGE 1. PAGE 2 WILL NOT WORK + LRGF ; WITH MERLIN LOADED; THEN, INIT + LDY #$FF ; LORES GRAPHICS FULL SCREEN MODE + STY ]COLOR ; MAC COLOR - 1 +:LP1 + INC ]COLOR ; INCREASE THE COLOR + LFCLR ]COLOR ; CLEAR FULL SCREEN WITH COLOR SELECTED + _WAIT ; WAIT UNTIL KEYPRESS + LDA ]COLOR ; LOAD COLOR AGAIN + CMP #$F ; IF IT'S $F (15), THEN + BNE :LP1 ; WE ARE DONE DEMOING SCREEN FILLS +* +*``````````````````````````````* +* RE-ENTERING TEXT MODE * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** A SOFT SWITCH IS USED TO RE-ENTER TEXT MODE. ANY +** INSTRUCTION CAN BE USED ON THE ^TEXTON^ MEMORY +** ADDRESS IN ORDER TO DO THIS; I USUALLY USE THE BIT +** INSTRUCTION SINCE I RARELY USE IT FOR OTHER +** PURPOSES. +* + BIT TEXTON + JSR ]HOME + _PRN "LOOK MA, TEXT MODE!",8D8D + _WAIT +* +*``````````````````````````````* +* PLOTTING MACROS * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THERE ARE A NUMBER OF MACROS DEDICATED TO PLOTTING +** POINTS ONTO THE LOW RESOLUTION SCREEN. THESE ARE +** LPLOT, LHLIN, LVLIN, LLINE AND LCIRC. THESE WILL ALL +** BE SHOWN ON THE SAME GRAPHICS SCREEN, BUT EACH +** DESERVES A SHORT EXPLANATION. +* +*``````````````````````````````* +* THE LPLOT MACRO * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE LPLOT MACRO ACCEPTS AN X-COORDINATE AND A +** Y-COORDINATE, AS WELL AS A COLOR FOR THE LORES +** PIXEL TO BE PLOTTED. +* +*``````````````````````````````* +* THE LHLIN MACRO * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE LHLIN MACRO ACCEPTS A STARTING X POINT AND AN +** ENDING X-COORDINATE, FOLLOWED BY A Y-COORDINATE THAT +** THE LINE WILL APPEAR ON. OBVIOUSLY, THIS CREATES A +** HORIZONTAL LINE ON THE SCREEN. THE LAST PARAMETER +** SIGNIFIES THE COLOR OF THE LINE. +* +*``````````````````````````````* +* THE LVLIN MACRO * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE LVLIN MACRO WORKS MUCH THE SAME AS THE LHLIN +** MACRO, EXCEPT IT PRINTS A VERTICAL LINE. THE FIRST +** PARAMETER IS A STARTING Y-COORDINATE, FOLLOWED BY +** AN ENDING Y-COORDINATE. THE THIRD PARAMETER IS THE +** X-COORDINATE OF THE LINE, AND THE FOURTH PARAMETER +** IS THE COLOR TO BE USED FOR THE LINE. +* +*``````````````````````````````* +* THE LLINE MACRO * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE LLINE MACRO USES BRESSENHAM'S LINE ALGORITHM +** TO PLOT A LINE WITH AN ARBITRARY ANGLE TO THE +** LOW RESOLUTION SCREEN. ITS FORMAT IS AS FOLLOWS: +* +** LLINE X1;Y1;X2;Y2;COLOR +* +** NOTE THAT IF YOU KNOW BEFOREHAND THAT THE LINE WILL +** BE PERFECTLY VERTICAL OR HORIZONTAL, LHLIN OR LVLIN +** SHOULD ALWAYS BE USED INSTEAD IN ORDER TO SAVE +** CYCLES. +* +*``````````````````````````````* +* LCIRC * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** FINALLY, THE LCIRC MACRO PLOTS A CIRCLE TO THE LOW +** RESOLUTION SCREEN AT A GIVEN CENTER COORDINATE WITH +** A GIVEN RADIUS AND COLOR, AS SUCH: +* +** LCIRC X;Y;CENTER;COLOR +* +** THIS MACRO/SUBROUTINE USES BRESSENHAM'S CIRCLE +** ALGORITHM, WHICH SACRIFICES SOME ACCURACY IN THE +** NAME OF SPEED. EVEN IN HIGH RESOLUTION GRAPHICS ON +** THE APPLE II, THE POTENTIAL FOR ERROR IS NEGLIGIBLE +** DUE TO THE LIMITED RESOLUTION THAT THE MACHINE IS +** CAPABLE OF DISPLAYING. +* + JSR ]HOME + LRGF + LFCLR #]WHITE + LPLOT #10;#10;#]MAGENTA ; PLOT MAGENTA PIXEL AT 10,10 + LPLOT #11;#11;#]PINK ; PLOT PINK PIXEL AT 11,11 + _WAIT +* + LHLIN #15;#30;#2;#]PURPLE ; NOW DRAW A HORIZONTAL + _WAIT +* + LVLIN #2;#17;#30;#]YELLOW ; NOW CREATE A YELLOW VERTICAL LINE + _WAIT + LLINE #1;#1;#20;#30;#]ORANGE ; NOW PLOT ORGANGE DIAGONAL + _WAIT +* + LCIRC #10;#10;#10;#]LBLUE ; NOW DRAW A LIGHT BLUE CIRCLE + _WAIT +* +*``````````````````````````````* +* THE LRGET MACRO * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE LRGET MACRO RETRIEVES THE COLOR VALUE OF A +** LOW RESOLUTION PIXEL AT THE GIVEN COORDINATES, AS +** SUCH: +* +** LRGET X;Y +* + LRGET #38;#38 ; NOW GET THE COLOR OF A GIVE PIXEL + BIT TEXTON + JSR ]HOME + DUMP #RETURN;#1 + _WAIT +* +*``````````````````````````````* +* THE LCHAR MACRO * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE LCHAR MACRO PRINTS A 4 X 6 CHARACTER TO THE +** LOW RESOLUTION SCREEN AT THE SPECIFIED COLOR. THIS +** MEANS THAT A THE SCREEN CAN HOLD A TOTAL OF 8 X 8 +** CHARACTERS ON THE SCREEN AT ONCE, WITH A SINGLE +** BLANK PIXEL BETWEEN EACH CHARACTER. +* + LRGF ; GO BACK TO FULL SCREEN LORES GRAPHICS + LFCLR #]WHITE ; CLEAR THE BACKGROUND TO WHITE + LCHAR #0;#0;#LR_A;#0 + LCHAR #5;#0;#LR_B;#1 + LCHAR #10;#0;#LR_C;#2 + LCHAR #15;#0;#LR_D;#3 + LCHAR #20;#0;#LR_E;#4 + LCHAR #25;#0;#LR_F;#5 + LCHAR #30;#0;#LR_G;#6 + LCHAR #35;#0;#LR_H;#7 + LCHAR #0;#6;#LR_I;#8 + LCHAR #5;#6;#LR_J;#9 + LCHAR #10;#6;#LR_K;#10 + LCHAR #15;#6;#LR_L;#11 + LCHAR #20;#6;#LR_M;#12 + LCHAR #25;#6;#LR_N;#13 + LCHAR #30;#6;#LR_O;#14 + LCHAR #35;#6;#LR_P;#0 + LCHAR #0;#12;#LR_Q;#1 + LCHAR #5;#12;#LR_R;#2 + LCHAR #10;#12;#LR_S;#3 + LCHAR #15;#12;#LR_T;#4 + LCHAR #20;#12;#LR_U;#5 + LCHAR #25;#12;#LR_V;#6 + LCHAR #30;#12;#LR_W;#7 + LCHAR #35;#12;#LR_X;#8 + LCHAR #0;#18;#LR_Y;#9 + LCHAR #5;#18;#LR_Z;#10 + LCHAR #10;#18;#LR_0;#11 + LCHAR #15;#18;#LR_1;#12 + LCHAR #20;#18;#LR_2;#13 + LCHAR #25;#18;#LR_3;#14 + LCHAR #30;#18;#LR_4;#0 + LCHAR #35;#18;#LR_5;#1 + LCHAR #0;#24;#LR_6;#2 + LCHAR #5;#24;#LR_7;#3 + LCHAR #10;#24;#LR_8;#4 + LCHAR #15;#24;#LR_9;#5 + LCHAR #20;#24;#LR_EXC;#6 + LCHAR #25;#24;#LR_QUEST;#7 + LCHAR #30;#24;#LR_PRD;#8 + LCHAR #35;#24;#LR_CMA;#9 + LCHAR #0;#30;#LR_APOST;#10 + LCHAR #5;#30;#LR_QUOT;#11 + LCHAR #10;#30;#LR_COLON;#12 + LCHAR #15;#30;#LR_SEMI;#13 + LCHAR #20;#30;#LR_MINUS;#14 + LCHAR #25;#30;#LR_PLUS;#0 + LCHAR #30;#30;#LR_EQUAL;#1 + LCHAR #35;#30;#LR_FSLASH;#8 + LCHAR #05;#36;#LR_BSLASH;#9 + LCHAR #10;#36;#LR_LPAR;#10 + LCHAR #15;#36;#LR_RPAR;#11 + _WAIT +* +*``````````````````````````````* +* YEET * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** THE FOLLOWING SHOWS A QUICK EXAMPLE OF MANY +** OF THESE MACROS ACTING TOGETHER. THIS IS NOTHING +** TERRIBLY IMPRESSIVE, BUT IT DOES DEMONSTRATE SOME +** OF WHAT THE LIBRARY DOES. +* + LRGF + LFCLR #]BLACK + LDA #1 + STA ]CC1 +:RELOOP + LVLIN #0;#47;]HH;]CC1 + INC ]HH + INC ]CC1 + LDY ]HH + CPY #39 + BNE :CONTLP0 + LDA #0 + STA ]HH +* +:CONTLP0 + LCHAR #10;#37;#LR_K;#0 + LCHAR #15;#38;#LR_I;#0 + LCHAR #20;#39;#LR_L;#0 + LCHAR #25;#40;#LR_L;#0 + INC ]CC1 + LDY ]CC1 + CPY #15 + BNE :CONTLP + LDY #1 + STY ]CC1 +:CONTLP + LDA #1 + STA ]CC + STA ]RR +:LPC + LCIRC #19;#19;]RR;]CC + INC ]RR + INC ]CC + LDY ]RR + CPY #15 + BEQ :QLPC + JMP :LPC +:QLPC + LDA #1 + STA ]CC +:LPC2 + LCIRC #19;#19;]RR;]CC + DEC ]RR + INC ]CC + LDY ]RR + CPY #1 + BNE :LPC2 + LDA $C000 ; CHECK FOR KEYPRESS + BPL :LPLP ; IF NONE, KEEP LOOPING + AND #$7F ; SET HIGH BIT + STA $C010 ; RESET KEYBOARD STROBE + JMP EXIT +*JMP :RELOOP +* +:LPLP + JMP :RELOOP +EXIT + BIT TEXTON + JSR ]HOME + _PRN "FIN!",8D8D + LDA TEXTON + JMP $3D0 +* +*``````````````````````````````* +* BOTTOM INCLUDES * +*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* +* +** BOTTOM INCLUDES +* + PUT MIN.LIB.REQUIRED.ASM +* +** INDIVIDUAL SUBROUTINE INCLUDES +* + PUT MIN.SUB.LRGFCLR.ASM + PUT MIN.SUB.LRGPCLR.ASM + PUT MIN.SUB.LRPLOT.ASM + PUT MIN.SUB.LRHLINE.ASM + PUT MIN.SUB.LRVLINE.ASM + PUT MIN.SUB.LRBLINE.ASM + PUT MIN.SUB.LRCIRCLE.ASM + PUT MIN.SUB.LRGETPIX.ASM + PUT MIN.SUB.LRCHAR.ASM +* +]RR DS 1 +]CC DS 1 +]HH DS 1 +]CC1 DS 1 +``` + diff --git a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/4.0 Software_Architecture.md b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/4.0 Software_Architecture.md index de617ff..10f3401 100644 --- a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/4.0 Software_Architecture.md +++ b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/4.0 Software_Architecture.md @@ -108,7 +108,7 @@ After necessary assembler directives, files should be loaded in the following or **_Subroutine Independence_** -Beyond needing the core required library files as well as the hook files for the library category in question, a subroutine should be able to operate independently of other subroutines in the library. This will generally mean some wasted bytes here and there, but this can be remedied by the end programmer if code size is a major concern. +Beyond needing the core required library files as well as the header files for the library collection in question, a subroutine should be able to operate independently of other subroutines in the library. This will generally mean some wasted bytes here and there, but this can be remedied by the end programmer if code size is a major concern. **_Control Structures_** @@ -116,4 +116,15 @@ While a number of helpful, higher-level control structures are included as part **_ASM Suffix_** -The .ASM suffix on most files in the collection are mainly used for GitHub and modern operating systems to associate the dumped text version of the source with a filetype. \ No newline at end of file +The .ASM suffix on most files in the collection are mainly used for GitHub and modern operating systems to associate the dumped text version of the source with a filetype. + + + +--- + + + +[Return to Table of Contents](0.0%20Title_to_TOC) + +[Quick Reference Introduction](5.0%20Quick_Reference_TOC.md) + diff --git a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/5.0 Quick_Reference_TOC.md b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/5.0 Quick_Reference_TOC.md index 3df2cf5..0f86900 100644 --- a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/5.0 Quick_Reference_TOC.md +++ b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/5.0 Quick_Reference_TOC.md @@ -1,5 +1,21 @@ # Quick Reference: Macros -- [Disk 1: REQUIRED Library Macros](./6.0%20Quick_Reference_D1_MAC.REQUIRED.md) -- [Disk 2: STDIO Library Macros](./7.0%20Quick_Reference_D2_MAC.STDIO.md) -- [Disk 3: Arrays Library Macros](./8.0%20Quick_Reference_D3_MAC.ARRAYS.md) +- [Disk 1: REQUIRED Collection Macros](./6.0%20Quick_Reference_D1_MAC.REQUIRED.md) +- [Disk 2: STDIO Collection Macros](./7.0%20Quick_Reference_D2_MAC.STDIO.md) +- [Disk 3: Arrays Collection Macros](./8.0%20Quick_Reference_D3_MAC.ARRAYS.md) +- [Disk 4: Math Collection Macros](./9.0%20Quick_Reference_D4_MAC.MATH.md) +- [Disk 5: Strings Collection Macros](./10.0%20Quick_Reference_D5_MAC.STRINGS.md) + + + + + + + +--- + + + +[Return to Table of Contents](0.0%20Title_to_TOC) + +[Disk 1 -- REQUIRED Macros and Subroutines](6.0%20Quick_Reference_D1_MAC.REQUIRED.md) \ No newline at end of file diff --git a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/6.0 Quick_Reference_D1_MAC.REQUIRED.md b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/6.0 Quick_Reference_D1_MAC.REQUIRED.md index e6a79ff..70d9be5 100644 --- a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/6.0 Quick_Reference_D1_MAC.REQUIRED.md +++ b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/6.0 Quick_Reference_D1_MAC.REQUIRED.md @@ -1,4 +1,4 @@ -## Disk 1 Macros: The Required Library +## Disk 1 Macros: The Required Collection Disk 1: REQUIRED contains three main macro files, unlike most disks which only contain a single macro file. They are as follows: @@ -66,6 +66,10 @@ Be sure to consult the detailed reference for Disk 1: The Required Library for f +--- + + + ### MAC.ALIAS.8080.ASM | MACRO | DEPENDENCIES | PARAMETERS | ACTION | CYCLES | BYTE | @@ -98,6 +102,10 @@ Be sure to consult the detailed reference for Disk 1: The Required Library for f +--- + + + ### MAC.ALIAS.Z80.ASM | MACRO | DEPENDENCIES | PARAMETERS | ACTION | CYCLES | BYTE | @@ -111,3 +119,13 @@ Be sure to consult the detailed reference for Disk 1: The Required Library for f |`SCF`|none|none| `SEC` equivalent|2|1| + + + +--- + + + +[Return to Table of Contents](0.0%20Title_to_TOC) + +[Disk 2 Quick Reference -- STDIO Macros and Subroutines](7.0%20Quick_Reference_D2_MAC.STDIO.md) \ No newline at end of file diff --git a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/7.0 Quick_Reference_D2_MAC.STDIO.md b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/7.0 Quick_Reference_D2_MAC.STDIO.md index e69de29..61390cd 100644 --- a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/7.0 Quick_Reference_D2_MAC.STDIO.md +++ b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/7.0 Quick_Reference_D2_MAC.STDIO.md @@ -0,0 +1,114 @@ +## Disk 2 Macros: The STDIO Collection + +Disk 2: STDIO contains four macro files, each dedicated to different aspects of standard input and output. They are: + +- [MAC.COUT.STDOUT.ASM](#mac.cout.stdout.asm) + - This package contains macros that use the Apple II's COUT functionality. This can range from simple printing to the screen and moving the cursor position to word-wrapping a null-terminated string to the screen until the entire string is read. +- [MAC.SCRMEM.STDIO.ASM](#mac.scrmeme.stdio.asm) + - This package contains macros that deal directly with screen memory. This means that the macros tend to be **fast**, at least compared to COUT; thus, most of the macros found here have to do with plotting more complicated shapes to the screen rather than focus on simple text output alone. It should be noted that because these macros do not inform COUT of any screen memory changes, they do not exactly play well with using COUT. +- [MAC.STDIN.ASM](#mac.stdin.asm) + - These macros focus on input from the keyboard and paddles. More unique input devices, like a mouse, may be supported in the future. +- [MAC.MISC.STDIO.ASM](#mac.misc.stdio.asm) + - This package contains macros that do not easily fit in with any of the preceding three groupings. Some of these macros may move to different packages in the future, as their relevance to one set of functionalities rather than another becomes more apparent. + + + + + + + + + + + + + + + + + + + + + +### MAC.COUT.STDOUT.ASM + +| MACRO | DEPENDENCY | PARAMETERS | ACTION | DESTROYS | CYCLES | BYTES | +| ------- | ---------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -------- | ------ | ----- | +| `CURB` | none | ]1 = # of spaces | Move cursor backward | NZCV | 10+ | 7 | +| `CURD` | none | ]1 = # of spaces | Move cursor Downward | NZCV | 10+ | 7 | +| `CURF` | none | ]1 = # of spaces | Move cursor forward | NZCV | 10+ | 7 | +| `CURU` | none | ]1 = # of spaces | Move cursor upward | NZCV | 10+ | 7 | +| `PRN` | `DPRINT`
`XPRINT` | ]1 = Memory address or literal string | Print a literal string or a null-terminated string to the display | NZCV | 159+ | 34 | +| `SCPOS` | none | ]1 = Column
]2 = Row | Set Cursor Position | NZCV | 10+ | 8 | +| `SETCX` | none | ]1 = Column | Set Cursor Column | | 10+ | 8 | +| `SETCY` | none | ]1 = Row | Set Cursor Row | NZCV | 4+ | 5 | +| `SPRN` | `PRNSTR` | ]1 = memory address of string | Display a regular string | NZCV | 147+ | 35 | +| `TMORE` | `TXTMORE` | ]1 = String Address
]2 = Line Length
]3 = Pause Interval | Word-wrap a null-terminated string to the display, pausing either at a regular line interval or every time a linefeed is encountered. | NZCV | 482+ | 472 | + + + +--- + + + +### MAC.SCRMEM.STDIO.ASM + +| MACRO | DEPENDENCY | PARAMETERS | ACTION | DESTROYS | CYCLES | BYTES | +| ------- | ---------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -------- | ------ | ----- | +| `CPUT` | `TXTPUT` | ]1 = X-pos
]2 = Y-pos
]3 = character | Plot a character at the given X,Y coordinate | NZCV | 91+ | 31 | +| `RCPOS` | none | ]1 = Column
]2 = Row | Return a character located on screen at given coordinate | NZCV | 10+ | 6 | +| `SPUT` | `STRPUT` | ]1 = X-pos
]2 = Y-pos
]3 = String address | Plot a string at the given X,Y coordinate | NZCV | 124+ | 50 | +| `TCIRC` | `TCIRCLE` | ]1 = Center X-position
]2 = Center Y-position
]3 = Radius
]4 = Fill value | Create a circle with a text character and display it at given screen coordinates | NZCV | 627+ | 290 | +| `TCLR` | `TXTCLR` | ]1 = Fill Value | Fill the text screen with a given character | NZCV | 50+ | 42 | +| `THLIN` | `THLINE` | ]1 = X-pos origin
]2 = X-pos destination
]3 = Y-position
]4 = Fill value | Create a horizontal line on the screen made of a single fill character | NZCV | 107+ | 38 | +| `TVLIN` | `TVLINE` | ]1 = Y-pos origin
]2 = Y-pos destination
]3 = X-position
]4 = Fill value | Create a vertical line on the screen made of a single fill character | NZCV | 104+ | 45 | +| `TLINE` | `TBLINE` | ]1 = X origin
]2 = Y origin
]3 = X dest
]4 = Y dest
]5 = fill value | Create a diagonal line an X,Y origin to an X,Y destination with a single fill character | NZCV | 305+ | 198 | +| `TREC` | `TRECT` | ]1 = X Origin
]2 = Y Origin
]3 = X Destination
]4 = Y Destination
]5 = Fill Value | Create an unfilled rectangle on the text screen that is composed of a single character | NZCV | 362+ | 115 | +| `TRECF` | `TRECTF` | ]1 = X origin
]2 = Y origin
]3 = X dest
]4 = Y dest
]5 = Fill vallue | Create a filled rectangle on the screen made of a single fill character | NZCV | 157+ | 77 | + + + +--- + + + +### MAC.STDIN.ASM + +| MACRO | DEPENDENCY | PARAMETERS | ACTION | DESTROYS | CYCLES | BYTES | +| ------ | ---------- | ------------------- | ---------------------------------------------------- | -------- | ------ | ----- | +| `GKEY` | none | none | Wait for a keypress and hold the key value in **.A** | NZCV | 11+ | 7 | +| `INP` | `SINPUT` | none | input a line of text from the keyboard | NZCV | 64+ | 41 | +| `PBX` | none | ]1 = paddle button | Read paddle button state | NZCV | 8+ | 8 | +| `PDL` | none | ]1 = paddle address | Read the specified paddle value (0..255) | NZCV | 8+ | 5 | +| `WAIT` | none | none | wait for a keypress | NZCV | 9+ | 8 | + + + +--- + + + +### MAC.MISC.STDIO.ASM + +| MACRO | DEPENDENCY | PARAMETERS | ACTION | DESTROYS | CYCLES | BYTES | +| ------- | ---------- | -------------------------------------- | ------------------------------- | -------- | ------ | ----- | +| `COL40` | none | none | Enter 40-column mode | NZCV | 8+ | 5 | +| `COL80` | none | none | Enter 80-column mode | NZCV | 8+ | 5 | +| `DIE80` | none | none | End 80-column mode | NZCV | 8+ | 5 | +| `TCTR` | `TXTCENT` | ]1 = short string
]2 = long string | Center a string in a given line | NZCV | 47+ | 27 | + + + +--- + + + +[Return to Table of Contents](0.0%20Title_to_TOC) + +[Disk 3 Quick Reference -- Array Macros and Subroutines](8.0%20Quick_Reference_D3_MAC.ARRAYS) + + + + + diff --git a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/8.0 Quick_Reference_D3_MAC.ARRAYS.md b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/8.0 Quick_Reference_D3_MAC.ARRAYS.md index e69de29..91654d4 100644 --- a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/8.0 Quick_Reference_D3_MAC.ARRAYS.md +++ b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/8.0 Quick_Reference_D3_MAC.ARRAYS.md @@ -0,0 +1,79 @@ +## Disk 3 Macros: The Array Collection + +Disk 3: ARRAYS contains four macro files that have the same kinds of macros for different kinds of arrays; that is, each macro file is dedicated to an array of 8-bits or 16-bits and one or two dimensions, and contains the functionality to initialize an array, get data from it and put data into it. The are as follows: + +- [MAC.ARR8B1D.ASM](#mac.arr8b1d.asm) + - This packages contains `DIM`, `GET` and `PUT` macros for eight-bit, one-dimensional arrays. +- [MAC.ARR8B2D.ASM](#mac.arr8b2d.asm) + - This packages contains `DIM`, `GET` and `PUT` macros for eight-bit, two-dimensional arrays. +- [MAC.ARR16B1D.ASM](#mac.arr16b1d.asm) + - This packages contains `DIM`, `GET` and `PUT` macros for sixteen-bit, one-dimensional arrays. +- [MAC.ARR16B2S.ASM](#mac.arr16b2d.asm) + - This packages contains `DIM`, `GET` and `PUT` macros for sixteen-bit, two-dimensional arrays. + + + + + + + + + +### MAC.ARR8B1D.ASM + +| MACRO | DEPENDENCY | PARAMETERS | ACTION | DESTROYS | CYCLES | BYTES | +| ------- | ---------- | ------------------------------------------------------------ | ------------------------------------------------- | -------- | ------ | ----- | +| `DIM81` | `ADIM81` | ]1 = Array addr
]2 = # of elements
]3 = element size
]4 = fill value | Initialize a 1D, 8-bit array | NZCV | 234+ | 146 | +| `GET81` | `AGET81` | ]1 = Array addr
]2 = Element Index | Get a value from an element in a 1D, 8-bit array. | NZCV | 193+ | 125 | +| `PUT81` | `APUT81` | ]1 = Source addr
]2 = Array addr
]3 = Element Index | Put a value into an element in a 1D, 8-bit array. | NZCV | 216+ | 131 | + + + +--- + + + +### MAC.ARR8B2D.ASM + +| MACRO | DEPENDENCY | PARAMETERS | ACTION | DESTROYS | CYCLES | BYTES | +| ------- | ---------- | ------------------------------------------------------------ | ------------------------------------------------- | -------- | ------ | ----- | +| `DIM82` | `ADIM82` | ]1 = Array addr
]2 = 1st dimension length
]3 = 2nd dimension length
]4 = fill value | Initialize a 2D, 8-bit array | NZCV | 355+ | 228 | +| `GET82` | `AGET82` | ]1 = Array addr
]2 = 1st Dimension Index
]3 = 2nd Dimension Index | Get a value from an element in a 2D, 8-bit array. | NZCV | 340+ | 255 | +| `PUT82` | `APUT82` | ]1 = Source addr
]2 = Destination addr
]3 = 1st Dimension Index
4] = 2nd Dimension Index | Put a value into an element in a 2D, 8-bit array. | NZCV | 352+ | 223 | + + + +--- + + + +### MAC.ARR16B1D.ASM + +| MACRO | DEPENDENCY | PARAMETERS | ACTION | DESTROYS | CYCLES | BYTES | +| -------- | ---------- | ------------------------------------------------------------ | -------------------------------------------------- | -------- | ------ | ----- | +| `DIM161` | `ADIM161` | ]1 = Array Addr
]2 = # of elements
]3 = Element length
]4 = Fill Value | Initialize a 1D, 16-bit array | NZCV | 271+ | 165 | +| `GET161` | `AGET161` | ]1 = Array addr
]2 = Element Index | Get a value from an element in a 1D, 16-bit array. | NZCV | 223+ | 130 | +| `PUT161` | `APUT161` | ]1 = Source addr
]2 = Array addr
]3 = Element index | Put a value into an element in a 1D, 16-bit array. | NZCV | 238+ | 148 | + + + +---- + + + +### MAC.ARR16B2D.ASM + +| MACRO | DEPENDENCY | PARAMETERS | ACTION | DESTROYS | CYCLES | BYTES | +| -------- | ---------- | ------------------------------------------------------------ | -------------------------------------------------- | -------- | ------ | ----- | +| `DIM162` | `ADIM162` | ]1 = Array Address
]2 = First Dimension Element Count
]3 = Second Dimension Element Count
]4 = Byte size of Elements
]5 = Default Fill Value | Initialize a 2D, 16-bit array | NZCV | 464+ | 373 | +| `GET162` | `AGET162` | ]1 = Array Address
]2 = First Dimension Element Address
]3 = Second Dimension Element Address | Get a value from an element in a 2D, 16-bit array. | NZCV | 436+ | 263 | +| `PUT162` | `APUT162` | ]1 = Source Address
]2 = Destination Array Address
]3 = First Dimension Element Address
]4 = Second Dimension Element Address | Put a value into an element in a 2D, 16-bit array. | NZCV | 352+ | 223 | + + + +--- + + + +[Return to Table of Contents](0.0%20Title_to_TOC) + diff --git a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/9.0 Quick_Reference_D4_MAC.MATH.md b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/9.0 Quick_Reference_D4_MAC.MATH.md index e69de29..6286af5 100644 --- a/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/9.0 Quick_Reference_D4_MAC.MATH.md +++ b/documentation/AppleIIAsm Library Collection Technical Manual/0.6.0/9.0 Quick_Reference_D4_MAC.MATH.md @@ -0,0 +1,125 @@ +# Disk 4: Basic Math and Pseudorandom Numbers + + + +The Basic Math collection contains macros and subroutines dedicated to addition, subtraction, multiplication and division operations, as well as a 16-bit comparison routine and several macros for generating pseudorandom numbers. These are grouped into several macro files, as such: + + + +- [MAC.D8BY.ASM](#mac.d8by.asm) + - This group contains macros that are used to divide an 8-bit number by a fixed constant between 2 and 10. +- [MAC.M8BY.ASM](#mac.m8by.asm) + - This group contains macros that are used to multiply an 8-bit number by a fixed constant between 2 and 10. +- [MAC.M16BY.ASM](#mac.m16by.asm) + - This group contains macros that are used to multiply a 16-bit number by a fixed constant between 2 and 10, as well as by 100. +- [MAC.MATH8.ASM](#mac.math8.asm) + - This group contains macros for adding, subtracting, multiplying and dividing 8-bit numbers. +- [MAC.MATH16.ASM](#mac.math16.asm) + - This group contains macros for adding, subtracting, multiplying and dividing 16-bit numbers. It also includes a macro for 16-bit comparison. +- [MAC.MATHRND.ASM](#mac.mathrnd.asm) + - This group contains macros for managing and generating pseudorandom numbers. + + + + + + + + + + + + + + + + + + + + + + + +### MAC.D8BY.ASM + +| MACRO | DEPENDENCY | PARAMETERS | ACTION | DESTROYS | CYCLES | BYTES | +| -------- | ---------- | ---------- | ------------------------------- | -------- | ------ | ----- | +| `D8BY2` | none | none | Divide an 8-bit number by two | NZCV | 6 | 4 | +| `D8BY3` | none | none | Divide an 8-bit number by three | NZCV | 29 | 19 | +| `D8BY4` | none | none | Divide an 8-bit number by four | NZCV | 8 | 5 | +| `D8BY5` | none | none | Divide an 8-bit number by five | NZCV | 34 | 21 | +| `D8BY6` | none | none | Divide an 8-bit number by six | NZCV | 36 | 21 | +| `D8BY7` | none | none | Divide an 8-bit number by seven | NZCV | 33 | 19 | +| `D8BY8` | none | none | Divide an 8-bit number by eight | NZCV | 10 | 6 | +| `D8BY9` | none | none | Divide an 8-bit number by nine | NZCV | 36 | 21 | +| `D8BY10` | none | none | Divide an 8-bit number by ten | NZCV | 36 | 21 | + + + +### MAC.M8BY.ASM + +| MACRO | DEPENDENCY | PARAMETERS | ACTION | DESTROYS | CYCLES | BYTES | +| -------- | ---------- | ---------- | --------------------------------- | -------- | ------ | ----- | +| `M8BY2` | none | none | Multiply an 8-bit number by two | NZCV | 6 | 4 | +| `M8BY3` | none | none | Multiply an 8-bit number by three | NZCV | 6 | 4 | +| `M8BY4` | none | none | Multiply an 8-bit number by four | NZCV | 8 | 5 | +| `M8BY5` | none | none | Multiply an 8-bit number by five | NZCV | 15 | 10 | +| `M8BY6` | none | none | Multiply an 8-bit number by six | NZCV | 15 | 10 | +| `M8BY7` | none | none | Multiply an 8-bit number by seven | NZCV | 33 | 20 | +| `M8BY8` | none | none | Multiply an 8-bit number by eight | NZCV | 10 | 6 | +| `M8BY9` | none | none | Multiply an 8-bit number by nine | NZCV | 18 | 11 | +| `M8BY10` | none | none | Multiply an 8-bit number by ten | NZCV | 18 | 11 | + + + +### MAC.M16BY.ASM + +| MACRO | DEPENDENCY | PARAMETERS | ACTION | DESTROYS | CYCLES | BYTES | +| --------- | ---------- | ---------- | --------------------------------------- | -------- | ------ | ----- | +| `M16BY2` | none | none | Multiply a 16-bit number by two | NZCV | 20 | 13 | +| `M16BY3` | none | none | Multiply a 16-bit number by three | NZCV | 47 | 32 | +| `M16BY4` | none | none | Multiply a 16-bit number by four | NZCV | 37 | 24 | +| `M16BY5` | none | none | Multiply a 16-bit number by five | NZCV | 46 | 30 | +| `M16BY6` | none | none | Multiply a 16-bit number by six | NZCV | 58 | 38 | +| `M16BY7` | none | none | Multiply a 16-bit number by seven | NZCV | 72 | 47 | +| `M16BY8` | none | none | Multiply a 16-bit number by eight | NZCV | 34 | 21 | +| `M16BY9` | none | none | Multiply a 16-bit number by nine | NZCV | 62 | 36 | +| `M16BY10` | none | none | Multiply a 16-bit number by ten | NZCV | 66 | 39 | +| `M16BY1H` | none | none | Multiply a 16-bit number by one hundred | NZCV | 124 | 70 | + + + +### MAC.MATH8.ASM + +| MACRO | DEPENDENCY | PARAMETERS | ACTION | DESTROYS | CYCLES | BYTES | +| ------ | ---------- | ------------------------------------------- | -------------------------------------- | -------- | ------ | ----- | +| `ADD8` | none | ]1 = number to add
]2 = number to add | Add two 8-bit numbers | NZCV | 21 | 15 | +| `SUB8` | none | ]1 = Minuend
]2 = Subtrahend | Subtract one 8-bit number from another | NZCV | 21 | 15 | +| `MUL8` | `MULTU8` | ]1 = Multiplicand
]2 = Multiplier | Multiply two 8-bit numbers | NZCV | 97 | 56 | +| `DIV8` | `DIVDU8` | ]1 = Dividend
]2 = Divisor | Divide one 8-bit number by another | NZCV | 75 | 43 | + + + +### MAC.MATH16.ASM + +| MACRO | DEPENDENCY | PARAMETERS | ACTION | DESTROYS | CYCLES | BYTES | +| ------- | ---------- | ------------------------------------------------------------ | --------------------------------------- | -------- | ------ | ----- | +| `ADD16` | `ADDIT16` | ]1 = First number to add
]2 = Second number to add | Add two 16-bit numbers | NZCV | 78 | 51 | +| `SUB16` | `SUBT16` | ]1 = Minuend
]2 = Subtrahend | Subtract one 16-bit number from another | NZCV | 79 | 51 | +| `MUL16` | `MULTU16` | ]1 = Multiplier
]2 = Multiplicand | Multiply two 16-bit numbers | NZCV | 144 | 91 | +| `DIV16` | `DIVDU16` | ]1 = Dividend
]2 = Divisor | Divide one 16-bit number by another | NZCV | 133 | 81 | +| `CMP16` | `COMP16` | ]1 = First Comparison Number
]2 = Second Comparison Number | Compare one 16-bit number to another | NZCV | 100 | 53 | + + + +### MAC.MATHRND.ASM + +| MACRO | DEPENDENCY | PARAMETERS | ACTION | DESTROYS | CYCLES | BYTES | +| -------- | ---------- | ------------------------------------- | --------------------------------------------------------- | -------- | ------ | ----- | +| `RNDEOR` | none | ]1 = Seed
]2 = EOR Index | Set the seed and magic number value | NZCV | 30 | 22 | +| `RNDMZ` | none | ]1 = Seed
]2 = EOR Magic Number | Set the seed and set the magic number index | NZCV | 56 | 35 | +| `RND8` | `RAND8` | none | Generate a value between 0 and 255 | NZCV | 51 | 30 | +| `RAND` | `RANDB` | ]1 = Low bounds
]2 = High bounds | Generate a value between a given low value and high value | NZCV | 263 | 171 | +| `RND16` | `RAND16` | none | Generate a value between 0 and 65,355 | NZCV | 101 | 63 | +