AppleIIAsm-Collection/documentation/AppleIIAsm Library Collecti.../0.6.1/31.0 Detailed_Reference_D2_...

197 KiB

Disk 2 : STDIO Library and Aliases

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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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 TXTMOREentry for a description of how the word-wrapping implementation works.

LISTING 2.33: The TMORE Macro Source

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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 TXTPUTreflects 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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

*
*``````````````````````````````*
* 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 Next -- Disk 3: Array Macros and Subroutines