# Disk 2 : STDIO Library and Aliases - [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) ## PART I: STDIO Macros and Subroutine 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. --- ## STDIO 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. - 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 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. --- ## STDIO Header File _Summary_ The STDIO header file is required for all macros and subroutines to work correctly, although there may be a select few that can get away without its inclusion. Primarily, this header file contains vectors and hooks that point to routines found in the Monitor. | Condition | Value | | --- | --- | | Name | File: HEAD.STDIO.ASM | | Type | Header File | | Author | Nathan Riggs | | Last Revision | 19-JAN-2020 | | Assembler | Merlin 8 Pro | | OS | Apple DOS 3.3 | | Purpose | Provide appropriate hooks for standard input and output | | Dependencies | none | | Bytes | 0 | | Notes | Repeatedly used subroutines by the library may be placed here in the future | --- *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 2.0: HEAD.STDIO File Heading` ```asm * *``````````````````````````````* * HEAD.REQUIRED * * * * THESE ARE HOOKS THAT ARE * * USED BY THE STDIO LIBRARY. * * * * AUTHOR: NATHAN RIGGS * * CONTACT: NATHAN.RIGGS@ * * OUTLOOK.COM * * * * DATE: 19-JAN-2020 * * ASSEMBLER: Merlin Pro 8 * * OS: DOS 3.3 * * * * BYTES: 0 * *,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* * ``` 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.10: HEAD.STDIO Source` ```asm * *``````````````````````````````* * HEAD.STDIO.ASM * * * * THESE ARE HOOKS, VECTORS AND * * POINTERS THAT ARE USED BY * * THE STDIO LIBRARY. * * * * AUTHOR: NATHAN RIGGS * * CONTACT: NATHAN.RIGGS@ * * OUTLOOK.COM * * * * DATE: 19-JAN-2020 * * ASSEMBLER: MERLIN 8 PRO * * OS: DOS 3.3 * * * * BYTES: 0 * *,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* * * OUTPUT * COUT1 EQU $FDF0 ; FASTER SCREEN OUTPUT COUT EQU $FDED ; MONITOR STD OUTPUT HOME EQU $FC58 ; CLEAR SCREEN, HOME CURSOR VTAB EQU $FC22 ; MONITOR CURSOR POS ROUTINE CURSH EQU $24 ; HPOS OF COUT CURSOR CURSV EQU $25 ; VPOS OF COUT CURSOR KEYBUFF EQU $0200 ; KEYBUFFER START GSTROBE EQU $C040 ; GAME CONNECTOR STROBE GBCALC EQU $F847 ; SCREEN CALCULATION GBPSH EQU $26 ; VALUE RETURNED BY GBCALC * * INPUT * KYBD EQU $C000 ; LDA SINGLE KEYPRESS STROBE EQU $C010 ; CLEAR KYBD BUFFER GETLN EQU $FD6F ; MONITOR GET LINE OF KB INPUT GETKEY EQU $FD0C ; MONITOR GET SINGLE KEY INPUT * * PADDLE ADDRESS LABELS * PREAD EQU $FB1E ; READ STATE OF PADDLE PB0 EQU $C061 ; PADDLE BUTTON 0 PB1 EQU $C062 ; PADDLE BUTTON 1 PB2 EQU $C063 ; PADDLE BUTTON 2 PB3 EQU $C060 ; PADDLE BUTTON 3 * ** UNUSED BY LIBRARY * WNDLEFT EQU $20 ; SCROLL WINDOW LEFT WNDWIDTH EQU $21 ; SCROLL WINDOW WIDTH WNDTOP EQU $22 ; SCROLL WINDOW TOP WNDBOT EQU $23 ; SCROLL WINDOW BOTTOM TEXTP1 EQU $0400 ; START OF TEXT PAGE 1 TEXTP2 EQU $0800 ; START OF TEXT PAGE 2 PAGE1 EQU $C054 ; SOFT SWITCH USE PAGE 1 PAGE2 EQU $C055 ; SOFT SWITCH USE PAGE 2 S80COL EQU $C01F ; READ ONLY; CHECK IF 80C TXTSET EQU $C051 ; TEXT ON SOFT SWITCH SETWND EQU $FB4B ; SET NORMAL WINDOW MODE CURADV EQU $FBF4 ; ADVANCE CURSOR RIGHT CURBS EQU $FC10 ; CURSOR LEFT CURUP EQU $FC1A ; CURSOR UP CR EQU $FC62 ; CARRIAGE RETURN TO SCREEN LF EQU $FC66 ; LINE FEED ONLY TO SCREEN CLEOL EQU $FC9C ; CLEAR TEXT TO END OF LINE OPAPP EQU $C061 ; OPEN-APPLE KEY 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)