AppleIIAsm-Collection/documentation/AppleIIAsm Library Collecti.../0.6.1/32.0 Detailed_Reference_D3_...

153 KiB

Disk 3 : 8-Bit and 16-Bit 1D and 2D Arrays


Part I: The Arrays Collection

The third disk in the AppleIIASM library is dedicated to using 8-bit and 16-bit arrays with either one or two dimensions. The number of bits assigned refers to the number of possible elements: 8-bit arrays can have up to 255 elements in each dimension, with each element having a possible size of 255 bytes, whereas 16-bit arrays can have up to 65,025 elements in each dimension. When possible, 8-bit arrays should be used to save both cycles and bytes. Additionally, it should be apparent that a single 16-bit arrays could easily encompass more memory than is available in most Apple II computers; obviously, care needs to be taken not to fill up memory with a gigantic array.

There are four types of arrays supported: 8-bit, 1-dimensional arrays, 8-bit, 2-dimensional arrays, 16-bit, 1-dimensional arrays and 16-bit, 2-dimensional arrays. Each type has three main macros (and subroutines) for data manipulation: a DIM command that initializes the array, a GET command that retrieves a value from the array, and a PUT command that enters a value into an array. While more functionality might be desired, these commands for each array type take up most of the disk space available; thus, a new disk would be necessary for additional commands. These may be added, but not until the entire library is nearing completion.


Arrays Components

The Arrays collection contains the following components:

  • A header file with various hooks, vectors and definitions for the rest of the library as well as use by the end programmer.
  • Macros and subroutines for each of the four types of arrays provided. Functionality is given for declaring an array, getting data from an array, and putting data into an array.
  • A Demonstration of the macros and subroutines in use.

Arrays Header File

Currently, the Arrays header file includes an unused variable declaration only, which is present in the file only to prevent an error from being returned by the Merlin Pro 8 assembler. This is included to keep the same structure as other disks; eventually, more may be added to the header that is necessary for the working of the rest of the collection. For instance, all subroutines use a multiplication implementation; this can be moved to the header, saving space on the disk.

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

DETAILS

As with all files in the collection, this header file includes a short heading that provides contact information, date last changed, number of bytes, a short description, etc.

Listing 3.0: HEAD.ARRAYS Source

*
*``````````````````````````````*
* HEAD.ARRAYS.ASM              *
*                              *
* CURRENTLY, THIS HEADER FILE  *
* ONLY CONTAINS DUMMY CODE IN  *
* ORDER TO PREVENT AN ERROR    *
* DURING ASSEMBLY (EMPTY       *
* FILE).                       *
*                              *
* AUTHOR:    NATHAN RIGGS      *
* CONTACT:   NATHAN.RIGGS@     *
*            OUTLOOK.COM       *
*                              *
* DATE:      18-MAR-2021       *
* ASSEMBLER: MERLIN 8 PRO      *
* OS:        DOS 3.3           *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
ARRMAX   EQU   8192       ; MAXIMUM # OF BYTES
                          ; AN ARRAY CAN USE
*


Macros and Subroutines

The Arrays Collection is separated into four different macro files: 8-bit, single dimension arrays; 8-bit, double dimension arrays; 16-bit, single dimension arrays and 16-bit double dimension arrays. These are all independent of one another, but follow similar syntax and data structures. If possible, 8-bit arrays should be used in lieu of 16-bit arrays due to the extra cycles needed to calculate larger numbers for 16-bit values.


1-Dimensional, 8-Bit Arrays

These macros and subroutines are dedicated to using arrays with a maximum of 255 elements per dimension (in this case, a single dimension), with each element being up to 255 bytes in length. The macros for these arrays are located in the MAC.ARR8B1D.ASM file, named so after the types of macros it includes.

LISTING 3.10: MAC.ARR8B1D.ASM Header Source

*
*``````````````````````````````*
* MAC.ARR8B1D.ASM              *
*                              *
* LIBRARY OF MACROS FOR 8-BIT, *
* 1-DIMENSIONAL ARRAYS.        *
*                              *
* AUTHOR:    NATHAN RIGGS      *
* CONTACT:   NATHAN.RIGGS@     *
*            OUTLOOK.COM       *
*                              *
* DATE:      18-MAR-2021       *
* ASSEMBLER: MERLIN 8 PRO      *
* OS:        DOS 3.3           *
*                              *
* SUBROUTINE FILES USED        *
*                              *
*  SUB.ADIM81                  *
*  SUB.AGET81                  *
*  SUB.APUT81                  *
*                              *
* LIST OF MACROS               *
*                              *
* DIM81: DIM 1D, 8BIT ARRAY    *
* GET81: GET ELEMENT IN 8BIT,  *
*        1D ARRAY.             *
* PUT81: PUT VALUE INTO 8BIT,  *
*        1D ARRAY AT INDEX     *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*


THE DIM81 MACRO

SUMMARY

Condition Value
Name DIM81
Type Macro
File MAC.ARR8B1D.ASM
Author Nathan Riggs
Last Revision 18-MAR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Initialize an 8-bit, 1-dimensional array
Input ]1 = Address of Array
]2 = Number of Elements
]3 = Size of each Element
]4 = Default Fill Value
Output none
Dependencies SUB.ADIM81.ASM
Flags Destroyed NZCV
Cycles 234+
Bytes 146
Notes None
See Also ADIM81 GET81 AGET81 PUT81 APUT81

DETAILS

The DIM81 macro initializes an array that has one dimension and can have up to 255 elements, with each element holding up to a possible 255 bytes. This must be called before any array of its type can be used, as it sets up the data structure used by the GET81 and PUT81 macros. This data structure is fairly simple: the first byte of the array holds the number of elements in the array, and the second byte holds the size of each element. Afterwards, the accessible data of the array follows in linear order: element 1 is followed by element 2, 2 by 3, and so on until each element is accounted for.

Note that the entire block of memory used by the array is initialized once calling DIM81. Without care, this can wipe out data or a subroutine that is already held in memory. DIM81 does not check to see what it overwrites; in fact, it would be incapable of doing so. To be safe, you should always take care to calculate the size of the array yourself, either in code or calculated prior to coding. This can be done by multiplying the element length by the number of elements, then adding two for the preceding bytes that store the length of the array and element size.

LISTING 3.11: DIM81 Source

*
*``````````````````````````````*
* DIM81         (NATHAN RIGGS) *
*                              *
* CREATE A ONE DIMENSIONAL,    *
* 8-BIT ARRAY AT THE GIVEN     *
* ADDRESS.                     *
*                              *
* PARAMETERS                   *
*                              *
*  ]1 = ARRAY ADDRESS          *
*  ]2 = ARRAY BYTE LENGTH      *
*  ]3 = ELEMENT BYTE LENGTH    *
*  ]4 = FILL VALUE             *
*                              *
* CYCLES: 234+                 *
* SIZE: 146 BYTES              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
DIM81    MAC
         _MLIT ]1;WPAR1   ; {16C12B} PARSE IF LITERAL OR NOT
         LDA   ]2         ; {3C2B} ARRAY LENGTH
         STA   WPAR2      ; {3C2B}
         LDA   ]3         ; {3C2B} ELEMENT LENGTH
         STA   WPAR3      ; {3C2B}
         LDA   ]4         ; {3C2B}
         STA   BPAR1      ; {3C2B} FILL VAL
         JSR   ADIM81     ; {200C132B}
         <<<
*


THE ADIM81 SUBROUTINE

SUMMARY

Condition Value
Name ADIM81
Type Subroutine
File SUB.ADIM81.ASM
Author Nathan Riggs
Last Revision 18-MAR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Initialize an 8-bit, 1-dimensional array
Input WPAR1 = Array Address
WPAR2 = Number of Elements
WPAR3 = Element Length
BPAR1 = Default Fill Value
Output none
Dependencies SUB.ADIM81.ASM
Flags Destroyed NZCV
Cycles 194+
Bytes 129
Notes None
See Also DIM81 GET81 AGET81 PUT81 APUT81

DETAILS

The ADIM81 subroutine initializes an eight-bit, one-dimensional array stored at a given address, with its number of elements and the byte size of each element passed in the parameters, along with the default fill value for each byte of the array. The array created has a simple data structure: the first byte contains the number of elements and the second byte contains the length of each element, followed by the element data. It should be noted that ADIM81 does not care about the data it overwrites in memory; the programmer must take care to make sure that important data or routines are not overwritten.

ADIM81 first multiplies the number of elements specified by the passed size of each element, then adds two to account for the total number of bytes needed for the array. Then, the space is cleared starting at the address given for the array and filled with the specified fill character. The total size of the array is then held in RETURN in case the programmer wants to save the array size for future reference.

LISTING 3.12: SUB.ADIM81.ASM Source

*
*``````````````````````````````*
* ADIM81        (NATHAN RIGGS) *
*                              *
* DECLARE THE DIMENSIONS OF A  *
* NEW 8BIT, 1D ARRAY.          *
*                              *
*------------------------------*
* 8-BIT MULTIPLICATION CODE    *
* ADAPTED FROM WHITE FLAME'S   *
* WORK ON CODEBASE64. LICENSE  *
* MAY VARY.                    *
*------------------------------*
*                              *
* INPUT                        *
*                              *
*  WPAR1 = ARRAY ADDRESS       *
*  WPAR2 = # OF ELEMENTS       *
*  WPAR3 = LENGTH OF ELEMENTS  *
*  BPAR1 = FILL VALUE          *
*                              *
* DESTROY: NZCIDV              *
*          ^^^  ^              *
*                              *
* CYCLES: 194+                 *
* SIZE: 129 BYTES              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]ADDR    EQU   WPAR1      ; ARRAY ADDRESS
]ASIZE   EQU   WPAR2      ; NUMBER OF ELEMENTS
]ESIZE   EQU   WPAR3      ; ELEMENT SIZE, IN BYTES
]FILL    EQU   BPAR1      ; DEFAULT FILL VALUE
*
]MSIZE   EQU   VARTAB     ; TOTAL BYTES OF ARRAY
]ASZBAK  EQU   VARTAB+4   ; ARRAY SIZE BACKUP
]ESZBAK  EQU   VARTAB+6   ; ELEMENT SIZE BACKUP
*
ADIM81
         LDA   ]ESIZE     ; {2C2B} LOAD ELEMENT SIZE
         STA   ]ESZBAK    ; {3C2B} SAVE A BACKUP
         LDA   ]ASIZE     ; {2C2B} LOAD ARRAY SIZE
         STA   ]ASZBAK    ; {3C2B} SAVE A BACKUP
         LDA   #0         ; {2C2B} RESET .A
         STA   ]ASIZE+1   ; {3C2B} CLEAR ARRAY SIZE HIGH BYTE
         STA   ]ASZBAK+1  ; {3C2B} CLEAR ARRAY SIZE BACKUP HIGH BYTE
*
** MULTIPLY ARRAY SIZE BY ELEMENT SIZE
*
         LDY   #0         ; {2C2B} RESET HIBYTE FOR MULTIPLY
         TYA              ; {3C2B} RESET LOBYTE FOR MULTIPLY
         LDY   ]ASIZE+1   ; {2C2B}
         STY   SCRATCH    ; {3C2B} SAVE HIBYTE IN SCRATCH
         BEQ   :ENTLP     ; {3C2B} IF ZERO, SKIP TO LOOP
:DOADD
         CLC              ; {2C1B} ADD ASIZE TO LOBYTE
         ADC   ]ASIZE     ; {5C3B}
         TAX              ; {3C2B} TEMPORARILY STORE IN .X
         TYA              ; {3C2B} TRANSFER HIBYTE TO .A
         ADC   SCRATCH    ; {5C3B} ADD HIBYTE
         TAY              ; {3C2B} STORE BACK IN .Y
         TXA              ; {3C2B} LOAD LOBYTE IN .A AGAIN
:LP                       ; LOOP START
         ASL   ]ASIZE     ; {6C3B} MULTIPLY ASIZE BY 2
         ROL   SCRATCH    ; {6C3B} MULTIPLY HIBYTE BY 2
:ENTLP
         LSR   ]ESIZE     ; {6C3B} DIVIDE ESIZE BY 2
         BCS   :DOADD     ; {3C2B} IF >= LOBYTE IN .A, ADD AGAIN
         BNE   :LP        ; {3C2B} OTHERWISE, RELOOP
*
         STX   ]MSIZE     ; {3C2B} STORE LOBYTE
         STY   ]MSIZE+1   ; {3C2B} STORE HIBYTE
         LDA   ]MSIZE     ; {2C2B} NOW ADD TO BYTES
         CLC              ; {2C1B} TO MSIZE FOR ARRAY HEADER
         ADC   #2         ; {3C2B}
         STA   ]MSIZE     ; {3C2B} STORE LOBYTE
         LDA   ]MSIZE+1   ; {2C2B}
         ADC   #0         ; {3C2B} CARRY FOR HIBYTE
         STA   ]MSIZE+1   ; {3C2B}
*
** NOW CLEAR MEMORY BLOCKS
*
         LDA   ]FILL      ; {2C2B} GET FILL VALUE
         LDX   ]MSIZE+1   ; {2C2B} X = # O PAGES TO DO
         BEQ   :PART      ; {3C2B} BRANCH IF HIBYTE = 0
         LDY   #0         ; {2C2B} RESET INDEX
:FULL
         STA   (]ADDR),Y  ; {3C2B} FILL CURRENT BYTE
         INY              ; {2C1B} INCREMENT INDEX
         BNE   :FULL      ; {3C2B} LOOP UNTIL PAGE DONE
         INC   ]ADDR+1    ; {5C2B} GO TO NEXT PAGE
         DEX              ; {2C1B} DECREMENT COUNTER
         BNE   :FULL      ; {3C2B} LOOP IF PAGES LEFT
:PART
         LDX   ]MSIZE     ; {2C2B} PARTIAL PAGE BYTES
         BEQ   :MFEXIT    ; {3C2B} EXIT IF LOBYTE = 0
         LDY   #0         ; {2C2B} RESENT INDEX
:PARTLP
         STA   (]ADDR),Y  ; {3C2B} STORE VAL
         INY              ; {2C1B} INCREMENT INDEX
         DEX              ; {2C1B} DECREMENT COUNTER
         BNE   :PARTLP    ; {3C2B} LOOP UNTIL DONE
:MFEXIT
         LDY   #0         ; {2C2B} STORE NUMBER OF ELEMENTS
         LDA   ]ASZBAK    ; {2C2B} INTO FIRST BYTE OF ARRAY
         STA   (]ADDR),Y  ; {6C2B}
         INY              ; {2C1B}
         LDA   ]ESZBAK    ; {2C2B} STORE ELEMENT SIZE INTO
         STA   (]ADDR),Y  ; {6C2B} SECOND BYTE OF ARRAY
         LDX   ]ADDR      ; {2C2B} GET LOBYTE OF ARRAY ADDRESS
         LDY   ]ADDR+1    ; {2C2B} AND HIBYTE TO RETURN IN .X, .Y
         LDA   ]ASZBAK    ; {2C2B} RETURN NUMBER OF ELEMENTS IN .A
         LDA   ]MSIZE     ; {2C2B} STORE TOTAL ARRAY SIZE
         STA   RETURN     ; {3C2B} IN RETURN
         LDA   ]MSIZE+1   ; {2C2B}
         STA   RETURN+1   ; {3C2B}
         LDA   #2         ; {2C2B} SET RETURN LENGTH TO
         STA   RETLEN     ; {3C2B} 2 BYTES
         RTS              ; {6C1B}


THE GET81 MACRO

SUMMARY

Condition Value
Name GET81
Type Macro
File MAC.ARR8B1D.ASM
Author Nathan Riggs
Last Revision 18-MAR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Initialize an 8-bit, 1-dimensional array
Input ]1 = Address of Array
]2 = Element Index
Output none
Dependencies SUB.AGET81.ASM
Flags Destroyed NZCV
Cycles 193+
Bytes 125
Notes None
See Also DIM81 ADIM81 AGET81 PUT81 APUT81

DETAILS

The GET81 macro retrieves an element's value from an eight-bit, one-dimensional array. Parameters are passed via the registers, and the result is passed back via RETURN. The RETLEN byte holds the length of the value in RETURN. Additionally, the element address is returned in the .X (low byte) and .Y (high byte) registers, while the length of the return value is also stored in .A.

LISTING 3.13: The GET81 Macro

*
*``````````````````````````````*
* GET81         (NATHAN RIGGS) *
*                              *
* RETRIEVE A VALUE FROM THE    *
* GIVEN ARRAY AT THE SPECIFIED *
* ELEMENT INDEX AND STORE THE  *
* VALUE IN RETURN.             *
*                              *
* PARAMETERS                   *
*                              *
*  ]1 = ARRAY ADDRESS          *
*  ]2 = ELEMENT INDEX          *
*                              *
* CYCLES: 193+                 *
* SIZE: 125 BYTES              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
GET81    MAC
         _AXLIT ]1        ; {8C6B} PARSE ADDRESS
         LDY   ]2         ; {3C2B} ELEMENT INDEX
         JSR   AGET81     ; {182C117B}
         <<<
*


THE AGET81 SUBROUTINE

SUMMARY

Condition Value
Name AGET81
Type Subroutine
File SUB.AGET81.ASM
Author Nathan Riggs
Last Revision 18-MAR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Initialize an 8-bit, 1-dimensional array
Input .A = Array Address Low Byte
.X = Array Address High Byte
.Y = Array Element Index
Output none
Dependencies none
Flags Destroyed NZCV
Cycles 176+
Bytes 114
Notes None
See Also DIM81 ADIM81 GET81 PUT81 APUT81

DETAILS

The AGET81 subroutine accepts an array address via the .X (low byte) and .Y (high byte) registers and an element index in the .A register and returns the value of the element specified in the given array. The element data is held in RETURN after execution, with its length held in both RETLEN and .A. The memory address of the element is also returned back via the .X (low byte) and .Y (high byte) registers. This address is calculated by multiplying the passed index by the element length, adding two for the preceding length bytes, then added to the address of the array itself.

LISTING 3.14: The AGET81 Subroutine Source

*
*``````````````````````````````*
* AGET81        (NATHAN RIGGS) *
*                              *
* RETRIEVE A VALUE AT A GIVEN  *
* ARRAY INDEX AND STORE IN     *
* RETURN AREA WITH APPROPRIATE *
* RETURN LENGTH (RETLEN).      *
*                              *
*------------------------------*
* 8-BIT MULTIPLICATION CODE    *
* ADAPTED FROM WHITE FLAME'S   *
* WORK ON CODEBASE63. LICENSE  *
* MAY VARY.                    *
*------------------------------*
*                              *
* INPUT:                       *
*                              *
*  .A = ARRAY ADDRESS LOBYTE   *
*  .X = ARRAY ADDRESS HIBYTE   *
*  .Y = ARRAY ELEMENT INDEX    *
*                              *
* DESTROY: NZCIDV              *
*          ^^^  ^              *
*                              *
* CYCLES: 176+                 *
* SIZE: 114 BYTES              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]RES     EQU   VARTAB     ; MATH RESULTS
]IDX     EQU   VARTAB+2   ; ELEMENT INDEX
]ESIZE   EQU   VARTAB+4   ; ELEMENT SIZE
]ALEN    EQU   VARTAB+5   ; NUMBER OF ELEMENTS
*
AGET81
         STA   ADDR1      ; {3C2B} .A HOLDS ARRAY ADDRESS LOBYTE
         STX   ADDR1+1    ; {3C2B} .X HOLDS ADDRESS HIBYTE
         STY   ]IDX       ; {3C2B} .Y HOLDS THE INDEX
         LDA   #0         ; {2C2B} CLEAR INDEX HIBYTE
         STA   ]IDX+1     ; {3C2B}
         LDY   #1         ; {2C2B} GET ELEMENT SIZE FROM ARRAY
         LDA   (ADDR1),Y  ; {6C2B} HEADER
         STA   ]ESIZE     ; {3C2B}
         STA   RETLEN     ; {3C2B} STORE IN RETLEN
         DEY              ; {2C1B} MOVE TO BYTE 0 OF HEADER
         LDA   (ADDR1),Y  ; {6C2B} GET NUMBER OF ELEMENTS
         STA   ]ALEN      ; {3C2B} FROM THE ARRAY HEADER
*
** MULTIPLY INDEX BY ELEMENT SIZE, ADD 2
*
         TYA              ; {2C1B} Y ALREADY HOLDS ZERO
         STY   SCRATCH    ; {3C2B} RESET LO AND HI TO 0
         BEQ   :ENTLP     ; {3C2B} IF ZERO, SKIP TO LOOP
:DOADD
         CLC              ; {2C1B} CLEAR CARRY FLAG
         ADC   ]IDX       ; {4C3B} ADD INDEX LOBYTE
         TAX              ; {2C1B} TEMPORARILY STORE IN .X
         TYA              ; {2C1B} TRANSFER HIBYTE TO .A
         ADC   SCRATCH    ; {4C3B} ADD HIBYTE
         TAY              ; {2C1B} STORE BACK INTO .Y
         TXA              ; {2C1B} RELOAD LOBYTE IN .A
:LP
         ASL   ]IDX       ; {6C3B} MULTIPLY INDEX BY TWO
         ROL   SCRATCH    ; {6C3B} ADJUST HIBYTE CARRY
:ENTLP
         LSR   ]ESIZE     ; {6C3B} DIVIDE ELEMENT SIZE BY 2
         BCS   :DOADD     ; {3C2B} IF >= LOBYTE IN .A, ADD AGAIN
         BNE   :LP        ; {3C2B}
*
         STX   ]IDX       ; {3C2B} STORE LOBYTE
         STY   ]IDX+1     ; {3C2B} STORE HIBYTE
         CLC              ; {2C1B} CLEAR CARRY
         LDA   #2         ; {2C2B} ADD 2 BYTES TO INDEX
         ADC   ]IDX       ; {4C3B} TO ACCOUNT FOR ARRAY HEADER
         STA   ]RES       ; {3C2B} AND STORE IN RESULT
         LDA   #0         ; {2C2B} ACCOUNT FOR HIBYTE CARRY
         ADC   ]IDX+1     ; {4C3B}
         STA   ]RES+1     ; {3C2B}
*
** NOW ADD TO BASE ADDRESS TO GET ELEMENT ADDRESS
*
         CLC              ; {2C1B} CLEAR CARRY FLAG
         LDA   ]RES       ; {2C2B} LOAD RESULT FROM EARLIER
         ADC   ADDR1      ; {4C3B} ADD ARRAY ADDRESS LOBYTE
         STA   ]RES       ; {3C2B} STORE BACK IN RESULT
         LDA   ]RES+1     ; {2C2B} LOAD PRIOR RESULT HIBYTE
         ADC   ADDR1+1    ; {4C3B} ADD ARRAY ADDRESS HIBYTE
         STA   ]RES+1     ; {3C2B} STORE BACK IN RESULT HIBYTE
*
** NOW MOVE ELEMENT DATA TO RETURN LOCATION
*
         LDY   #0         ; {2C2B} RESENT INDEX
         LDA   ]RES       ; {2C2B} LOAD ADDRESS LOBYTE
         STA   ADDR1      ; {3C2B} PUT IN ZERO PAGE POINTER
         LDA   ]RES+1     ; {2C2B} GET RESULT HIBYTE
         STA   ADDR1+1    ; {3C2B} PUT IN ZERO PAGE POINTER
:LDLOOP
         LDA   (ADDR1),Y  ; {2C2B} LOAD BYTE FROM ELEMENT
         STA   RETURN,Y   ; {3C2B} STORE IN RETURN
         INY              ; {2C1B} INCREASE BYTE INDEX
         CPY   RETLEN     ; {4C2B} IF .Y <= ELEMENT SIZE
         BCC   :LDLOOP    ; {3C2B} CONTINUE LOOPING
         BEQ   :LDLOOP    ; {3C2B} KEEP LOOPING
*
         LDX   ]RES       ; {2C2B} RETURN ELEMENT ADDRESS
         LDY   ]RES+1     ; {2C2B} IN .X (LOBYTE) AND .Y (HI)
         LDA   RETLEN     ; {2C2B} RETURN ELEMENT LENGTH IN .A
         RTS              ; {6C1B}


THE PUT81 MACRO

SUMMARY

Condition Value
Name PUT81
Type Macro
File MAC.ARR8B1D.ASM
Author Nathan Riggs
Last Revision 18-MAR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Initialize an 8-bit, 1-dimensional array
Input ]1 = Address of Source Value
]2 = Array Address
]3 = Element Index
Output none
Dependencies SUB.APUT81.ASM
Flags Destroyed NZCV
Cycles 216+
Bytes 131
Notes None
See Also DIM81 ADIM81 GET81 AGET81 APUT81

DETAILS

The PUT81 macro inserts a value into an array's specified element, copied from another memory address. This last part is important, as it does depart in some ways from many other routines: PUT81 only accepts an address, and that address either 1) is passed as a literal, meaning that the value anticipated is located at the address (direct), or 2) is passed as a regular address, meaning that the address itself holds another address that points to where the value is held (indirect). Currently, there is no way to pass a literal value to PUT81; all values must be held in an address prior to calling the macro. This may change in later revisions, but it is more likely that an extra macro and subroutine pair will be created for placing literal values into an element.

LISTING 3.15: The PUT81 Macro Source

*
*``````````````````````````````*
* PUT81         (NATHAN RIGGS) *
*                              *
* PUTS THE DATA FOUND AT THE   *
* GIVEN ADDRESS INTO THE ARRAY *
* AT THE GIVEN INDEX.          *
*                              *
* PARAMETERS                   *
*                              *
*  ]1 = SOURCE ADDRESS         *
*  ]2 = ARRAY ADDRESS          *
*  ]3 = ELEMENT INDEX          *
*                              *
* CYCLES: 216+                 *
* SIZE: 131 BYTES              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
PUT81    MAC
         _MLIT ]1;WPAR1   ; {16C12B} PARSE SOURCE ADDRESS
         _MLIT ]2;WPAR2   ; {16C12B} PARSE DEST ADDRESS
         LDA   ]3         ; {3C2B} DEST INDEX
         STA   BPAR1      ; {3C2B}
         JSR   APUT81     ; {178C103B}
         <<<
*


THE APUT81 SUBROUTINE

SUMMARY

Condition Value
Name APUT81
Type Subroutine
File SUB.APUT81.ASM
Author Nathan Riggs
Last Revision 18-MAR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Initialize an 8-bit, 1-dimensional array
Input WPAR1 = Source Address
WPAR2 = Destination Array Address
BPAR1 = Element Index
Output none
Dependencies none
Flags Destroyed NZCV
Cycles 172+
Bytes 100
Notes None
See Also DIM81 ADIM81 GET81 AGET81 PUT81

DETAILS

The APUT81 subroutine accepts an address that contains a value to be transferred into the specified element of a given array, then copies that value appropriately. The length to be copied is determined by the array's element length, which should be already initialized by the ADIM81 subroutine. Afterwards, APUT81 returns the address of the element specified in .X (low byte) and .Y (high byte), with the element length held in .A.

LISTING 3.16: The APUT81 Subroutine Source

*
*``````````````````````````````*
* APUT81        (NATHAN RIGGS) *
*                              *
* PUT DATA FROM SRC LOCATION   *
* INTO 1D, 8BIT ARRAY AT THE   *
* SPECIFIED ELEMENT.           *
*                              *
*------------------------------*
* 8-BIT MULTIPLICATION CODE    *
* ADAPTED FROM WHITE FLAME'S   *
* WORK ON CODEBASE64. LICENSE  *
* MAY VARY.                    *
*------------------------------*
*                              *
* INPUT:                       *
*                              *
*  WPAR1 = SOURCE ADDRESS      *
*  WPAR2 = DESTINATION ADDRESS *
*  BPAR1 = ARRAY INDEX         *
*                              *
* DESTROY: NZCIDV              *
*          ^^^  ^              *
*                              *
* CYCLES: 172+                 *
* SIZE: 100 BYTES              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]ADDRS   EQU   WPAR1      ; SOURCE ADDRESS
]ADDRD   EQU   WPAR2      ; DESTINATION
]AIDX    EQU   BPAR1      ; ARRAY INDEX
]SCRATCH EQU   ADDR1      ; ZEROED HIBYTE
*
]ESIZE   EQU   VARTAB     ; ELEMENT SIZE
]ESIZEBK EQU   VARTAB+1   ; ^BACKUP
]ASIZE   EQU   VARTAB+2   ; # OF ELEMENTS
]IDX     EQU   VARTAB+5   ; INDEX
]RES     EQU   VARTAB+7   ; MULTIPLICATION RESULT
*
APUT81
         LDA   ]AIDX      ; {2C2B} STORE IN 2 LOCATIONS
         STA   ]IDX       ; {3C2B} FOR A BACKUP LATER
*
** MULTIPLY INDEX BY ELEM SIZE AND ADD 2
*
         LDY   #1         ; {2C2B} GET ELEMENT LENGTH FROM
         LDA   (]ADDRD),Y ; {6C2B} BYTE 1 OF ARRAY
         STA   ]ESIZE     ; {3C2B}
         STA   ]ESIZEBK   ; {3C2B}
         LDY   #0         ; {2C2B} RESET INDEX
         LDA   (]ADDRD),Y ; {6C2B} GET NUMBER OF ELEMENTS
         STA   ]ASIZE     ; {3C2B} FROM ARRAY
         TYA              ; {2C1B} .A = 0
         STY   ]SCRATCH   ; {3C2B} LOBYTE = 0
         STY   ]SCRATCH+1 ; {3C2B} HIBYTE = 0
         BEQ   :ENTLPA    ; {3C2B} IF 0, SKIP TO LOOP
:DOADD
         CLC              ; {2C1B} CLEAR CARRY FLAG
         ADC   ]AIDX      ; {4C2B} ADD INDEX LOBYTE
         TAX              ; {2C1B} TEMPORARILY STORE IN .X
         TYA              ; {2C1B} TRANSFER HIBYTE TO .A
         ADC   ]SCRATCH   ; {4C2B} ADD HIBYTE
         TAY              ; {2C1B} STORE BACK IN .Y
         TXA              ; {2C1B} RELOAD LOBYTE TO .A
:LPA
         ASL   ]AIDX      ; {6C3B} MUL INDEX BY TWO
         ROL   ]SCRATCH   ; {6C3B} ADJUST HIBYTE CARRY
:ENTLPA
         LSR   ]ESIZE     ; {6C3B} DIVIDE ELEMENT SIZE BY 2
         BCS   :DOADD     ; {3C2B} IF >= LOBYTE IN .A, ADD AGAIN
         BNE   :LPA       ; {3C2B}
         STX   ]IDX       ; {3C2B} STORE LOBYTE
         STY   ]IDX+1     ; {3C2B} STORE HIBYTE
         CLC              ; {2C1B} CLEAR CARRY FLAG
         LDA   #2         ; {2C2B} ADD 2 BYTES TO INDEX
         ADC   ]IDX       ; {3C2B} TO ACCOUNT FOR HEADER
         STA   ]RES       ; {3C2B} STORE LOBYTE
         LDA   #0         ; {2C2B} ACCOUNT FOR HIBYTE CARRY
         ADC   ]IDX+1     ; {3C2B}
         STA   ]RES+1     ; {3C2B}
*
** ADD RESULT TO ARRAY ADDRESS TO GET ELEMENT ADDR
*
         CLC              ; {2C1B} CLEAR CARRY FLAG
         LDA   ]RES       ; {3C2B} LOAD RESULT FROM EARLIER
         ADC   ]ADDRD     ; {3C2B} ADD ARRAY ADDRESS LOBYTE
         STA   ]RES       ; {3C2B} STORE BACK IN RESULT
         LDA   ]RES+1     ; {3C2B} ADD ARRAY ADDRESS HIBYTE
         ADC   ]ADDRD+1   ; {3C2B}
         STA   ]RES+1     ; {3C2B} STORE HIBYTE
*
         STA   ]ADDRD+1   ; {3C2B} STORE IN ZERO PAGE HIBYTE
         LDA   ]RES       ; {3C2B} STORE LOBYTE TO ZERO PAGE
         STA   ]ADDRD     ; {3C2B}
*
** COPY FROM SRC ADDR3 TO ELEMENT LOCATION ADDR
*
:LP
         LDA   (]ADDRS),Y ; {6C2B} LOAD BYTE FROM SOURCE
         STA   (]ADDRD),Y ; {6C2B} STORE IN ELEMENT ADDRESS
         INY              ; {2C1B} INCREASE BYTE INDEX
         CPY   ]ESIZEBK   ; {4C3B} COMPARE TO ELEMENT SIZE
         BNE   :LP        ; {3C2B} IF !=, KEEP COPYING
*
         LDY   ]ADDRD+1   ; {3C2B} .Y = ELEMENT ADDRESS HIBYTE
         LDX   ]ADDRD     ; {3C2B} .X = LOBYTE
         LDA   ]ESIZE     ; {3C2B} .A = ELEMENT SIZE
         RTS              ; {6C1B}


2-Dimensional, 8-Bit Arrays

These macros and subroutines are dedicated to using arrays with a maximum of 255 elements per dimension (in this case, two dimensions), with each element being up to 255 bytes in length. The macros for these arrays are located in the MAC.ARR8B2D.ASM file, named so after the types of macros it includes (8 Bytes, 2 Dimensions).

LISTING 3.20: MAC.ARR8B2D.ASM Header Source

*
*``````````````````````````````*
* MAC.ARR8B2D.ASM              *
*                              *
* A MACRO LIBRARY FOR 8BIT,    *
* 2-DIMENSIONAL ARRAYS.        *
*                              *
* AUTHOR:    NATHAN RIGGS      *
* CONTACT:   NATHAN.RIGGS@     *
*            OUTLOOK.COM       *
*                              *
* DATE:      18-MAR-2021       *
* ASSEMBLER: MERLIN 8 PRO      *
* OS:        DOS 3.3           *
*                              *
* SUBROUTINE FILES USED        *
*                              *
*  SUB.ADIM82                  *
*  SUB.AGET82                  *
*  SUB.APUT82                  *
*                              *
* LIST OF MACROS               *
*                              *
* DIM82: DIM A 2D, 8BIT ARRAY  *
* GET82: GET ELEMENT IN 8BIT,  *
*        2D ARRAY              *
* PUT82: PUT VALUE INTO 8BIT,  *
*        2D ARRAY AT INDEX     *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*


THE DIM82 MACRO

SUMMARY

Condition Value
Name DIM82
Type Macro
File MAC.ARR8B2D.ASM
Author Nathan Riggs
Last Revision 18-MAR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Initialize an 8-bit, 2-dimensional array
Input ]1 = Array Address
]2 = First Dimension Length
]3 = Second Dimension Length
]4 = Default Fill Value
Output none
Dependencies SUB.ADIM82.ASM
Flags Destroyed NZCV
Cycles 355+
Bytes 228
Notes None
See Also ADIM82 GET82 AGET82 PUT82 APUT82

DETAILS

The DIM82 macro initializes a two-dimensional array that can hold up to 255 elements in each dimension, with a possible element length up to 255 bytes. This macro must be used before GET82 or PUT82 can be used, as those macros (and their respective subroutines) expect the data structure that DIM82 puts into place. This data structure is fairly simple: first, the first dimension (often referred to as the X dimension) length is stored at the array's base address, followed by the length of the second dimension (also known as the Y dimension). After these two values, which encompass a byte each, the size of the elements is recorded in the third byte. The actual data stored in the array is then placed after these preceding bytes in linear order. After being called, all elements should contain the fill value specified.

Note that DIM82 can be dangerous due to the fact that there is no value-checking of the address locations it overwrites; if the programmer is not careful, critical data or even commands could be overwritten by the macro. A four-byte value is placed in RETURN that indicates the total amount of memory that the array fills, but it is more sensible to know this value before declaring the array. To do so, simply multiply the number of elements in the first dimension by the number of elements in the second dimension, then multiply that product by the element length. Add three to this value to account for the preceding length bytes of the array, and you will then have the number of bytes the array will take as a whole.

LISTING 3.21: The DIM82 Macro Source

*
*``````````````````````````````*
* DIM82         (NATHAN RIGGS) *
*                              *
* INITIALIZES AN 8-BIT ARRAY   *
* WITH TWO DIMENSIONS.         *
*                              *
* PARAMETERS                   *
*                              *
*  ]1 = ARRAY ADDRESS          *
*  ]2 = X DIMENSION            *
*  ]3 = Y DIMENSION            *
*  ]4 = ELEMENT SIZE           *
*  ]5 = FILL VALUE             *
*                              *
* CYCLES: 355+                 *
* SIZE: 228 BYTES              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
DIM82    MAC
         _MLIT ]1;WPAR1   ; {16C12B} PARSE ARRAY ADDRESS
         LDA   ]2         ; {3C2B} X DIM
         STA   WPAR2      ; {3C2B}
         LDA   ]3         ; {3C2B} Y DIM
         STA   WPAR3      ; {3C2B}
         LDA   ]4         ; {3C2B} ELEMENT LENGTH
         STA   BPAR2      ; {3C2B}
         LDA   ]5         ; {3C2B} FILL VAL
         STA   BPAR1      ; {3C2B}
         JSR   ADIM82     ; {315C200B}
         <<<
*


THE ADIM82 SUBROUTINE

SUMMARY

Condition Value
Name ADIM82
Type Subroutine
File SUB.ADIM82.ASM
Author Nathan Riggs
Last Revision 18-MAR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Initialize an 8-bit, 2-dimensional array
Input WPAR1 = Array Address
WPAR2 = First Dimension Length
WPAR3 = Second Dimension Length
BPAR1 = Default Fill Value
BPAR2 = Element Length in Bytes
Output none
Dependencies none
Flags Destroyed NZCV
Cycles 309+
Bytes 197
Notes None
See Also DIM82 GET82 AGET82 PUT82 APUT82

DETAILS

The ADIM82 Subroutine creates an array data structure with two dimensions starting at the given address, with a maximum number of elements per dimension of 255. Elements can be specified to be between 1 and 255 bytes long, and a default value is passed that will fill every byte of the array's data. It should be noted that no protections are given against ADIM82 writing over pre-existing data or commands in memory, and thus should be used with care. The method used for finding the length of an eight-bit, two-dimensional array can be found in the DIM82 macro listing.

LISTING 3.22: SUB.ADIM82.ASM Subroutine Source

*
*``````````````````````````````*
* ADIM82        (NATHAN RIGGS) *
*                              *
* DECLARE THE DIMENSIONS OF A  *
* NEW 8-BIT, 2D ARRAY.         *
*                              *
*------------------------------*
* 8-BIT MULTIPLICATION CODE    *
* ADAPTED FROM WHITE FLAME'S   *
* WORK ON CODEBASE64. LICENSE  *
* MAY VARY.                    *
*------------------------------*
*                              *
* INPUT:                       *
*                              *
*  WPAR1 = ARRAY ADDRESS       *
*  WPAR2 = 1ST DIM LENGTH      *
*  WPAR3 = 2ND DIM LENGTH      *
*  BPAR1 = FILL VALUE          *
*  BPAR2 = ELEMENT LENGTH      *
*                              *
* DESTROY: NZCIDV              *
*          ^^^  ^              *
*                              *
* CYCLES: 309+                 *
* SIZE: 197 BYTES              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]ADDR    EQU   WPAR1      ; ARRAY ADDRESS
]AXSIZE  EQU   WPAR2      ; FIRST DIM # OF ELEMENTS
]AYSIZE  EQU   WPAR3      ; SECOND DIM # OF ELEMENTS
]FILL    EQU   BPAR1      ; FILL VALUE
]ESIZE   EQU   BPAR2      ; ELEMENT SIZE
*
]PROD    EQU   VARTAB     ; PRODUCT
]AXBAK   EQU   VARTAB+4   ; ARRAY X SIZE BACKUP
]AYBAK   EQU   VARTAB+5   ; ARRAY Y SIZE BACKUP
]MLIER   EQU   VARTAB+6   ; MULTIPLIER
]MCAND   EQU   VARTAB+8   ; MULTIPLICAND, ELEMENT SIZE
*
ADIM82
         LDA   ]ESIZE     ; {2C2B} ELEMENT LENGTH
         STA   ]MCAND     ; {3C2B} AND STORE AS MULTIPLICAND
         LDA   ]AYSIZE    ; {2C2B} GET ARRAY Y SIZE
         STA   ]AYBAK     ; {3C2B} BACK IT UP
         LDA   ]AXSIZE    ; {2C2B}
         STA   ]AXBAK     ; {3C2B} AND BACK THAT UP TOO
         LDA   #0         ; {2C2B} CLEAR MCAND HIBYTE
         STA   ]MCAND+1   ; {3C2B}
*
** MULTIPLY X AND Y
*
         TAY              ; {2C1B} AND LOBYTE
         STY   SCRATCH    ; {3C2B}
         BEQ   :ENTLP     ; {3C2B} IF ZERO, SKIP TO LOOP
:DOADD
         CLC              ; {2C1B} CLEAR CARRY FLAG
         ADC   ]AXSIZE    ; {5C3B} ADD X LENGTH
         TAX              ; {2C1B} TEMPORARILY STORE IN .X
         TYA              ; {2C1B} TRANSFER HIBYTE TO .A
         ADC   SCRATCH    ; {5C3B} ADD HIBYTE
         TAY              ; {2C1B} STORE BACK IN .Y
         TXA              ; {2C1B} RELOAD LOBYTE INTO .A
:LP
         ASL   ]AXSIZE    ; {6C3B} MULTIPLY X LENGTH BY 2
         ROL   SCRATCH    ; {6C3B} ADJUST HIBYTE
:ENTLP
         LSR   ]AYSIZE    ; {6C3B} DIVIDE Y LENGTH BY 2
         BCS   :DOADD     ; {3C2B} IF >= LOBYTE IN .A,
         BNE   :LP        ; {3C2B} ADD AGAIN; OTHERWISE, LOOP
         STX   ]MLIER     ; {3C2B} STORE LOBYTE IN MULTIPLIER
         STY   ]MLIER+1   ; {3C2B} STORE HIBYTE IN MULTIPLIER
*
** NOW MULTIPLY BY LENGTH OF ELEMENTS
*
         LDA   #0         ; {2C2B} CLEAR PRODUCT LOBYTE
         STA   ]PROD      ; {3C2B}
         STA   ]PROD+1    ; {3C2B} CLEAR NEXT BYTE
         STA   ]PROD+2    ; {3C2B} CLEAR NEXT BYTE
         STA   ]PROD+3    ; {3C2B} CLEAR HIBYTE
         LDX   #$10       ; {2C2B} LOAD $10 IN .X (#16)
:SHIFTR  LSR   ]MLIER+1   ; {6C3B} DIVIDE MLIER BY TWO
         ROR   ]MLIER     ; {6C3B} ADJUST LOBYTE
         BCC   :ROTR      ; {3C2B} IF LESS THAN PRODUCT, ROTATE
         LDA   ]PROD+2    ; {2C2B} LOAD PRODUCT 3RD BYTE
         CLC              ; {2C1B} CLEAR CARRY
         ADC   ]MCAND     ; {5C3B} ADD MULTIPLICAND
         STA   ]PROD+2    ; {3C2B} STORE BACK INTO PRODUCT 3RD BYTE
         LDA   ]PROD+3    ; {2C2B} LOAD PRODUCT HIBYTE
         ADC   ]MCAND+1   ; {5C3B} ADD MULTIPLICAND HIBYTE
:ROTR
         ROR              ; {6C3B} ROTATE .A RIGHT
         STA   ]PROD+3    ; {3C2B} STORE IN PRODUCT HIBYTE
         ROR   ]PROD+2    ; {6C3B} ROTATE PRODUCT 3RD BYTE
         ROR   ]PROD+1    ; {6C3B} ROTATE PRODUCT 2ND BYTE
         ROR   ]PROD      ; {6C3B} ROTATE PRODUCT LOBYTE
         DEX              ; {2C1B} DECREMENT COUNTER
         BNE   :SHIFTR    ; {3C2B} IF NOT 0, BACK TO SHIFTER
*
         LDA   ]PROD      ; {2C2B} LOAD PRODUCT LOBYTE TO .A
         CLC              ; {2C1B} CLEAR CARRY FLAG
         ADC   #3         ; {5C3B} ADD 3
         STA   ]PROD      ; {3C2B} STORE BACK INTO PRODUCT LOBYTE
         LDA   ]PROD+1    ; {2C2B}
         ADC   #0         ; {5C3B} INITIATE CARRY FOR 2ND BYTE
         STA   ]PROD+1    ; {3C2B}
         LDA   ]PROD+2    ; {2C2B}
         ADC   #0         ; {5C3B} AND THIRD BYTE
         STA   ]PROD+2    ; {3C2B}
*
** NOW CLEAR MEMORY BLOCKS, WHOLE PAGES FIRST
*
         LDA   ]FILL      ; {2C2B} GET FILL VALUE
         LDX   ]PROD+1    ; {2C2B} LOAD SECOND BYTE OF PRODUCT
         BEQ   :PART      ; {3C2B} IF 0, THEN ONLY PARTIAL PAGE
         LDY   #0         ; {2C2B} CLEAR INDEX
:FULL
         STA   (]ADDR),Y  ; {3C2B} COPY FILL BYTE TO ADDRESS
         INY              ; {2C1B} INCREASE INDEX
         BNE   :FULL      ; {3C2B} IF NO OVERFLOW, KEEP FILL
         INC   ]ADDR+1    ; {6C3B} INCREASE ADDRESS HIBYTE
         DEX              ; {2C1B} DECREMENT COUNTER
         BNE   :FULL      ; {3C2B} LOOP UNTIL PAGES DONE
:PART
         LDX   ]PROD      ; {2C2B} LOAD PRODUCT LOBYTE TO X
         BEQ   :MFEXIT    ; {3C2B} IF ZERO, THEN WE'RE DONE
         LDY   #0         ; {2C2B} RESET INDEX
:PARTLP
         STA   (]ADDR),Y  ; {6C2B} STORE FILL BYTE
         INY              ; {2C1B} INCREASE INDEX
         DEX              ; {2C1B} DECREASE COUNTER
         BNE   :PARTLP    ; {3C2B} LOOP UNTIL DONE
:MFEXIT
         LDY   #0         ; {2C2B} RESET INDEX
         LDA   ]AXBAK     ; {2C2B} PUT X LENGTH INTO
         STA   (]ADDR),Y  ; {6C2B} FIRST BYTE OF ARRAY
         INY              ; {2C1B} INCREMENT INDEX
         LDA   ]AYBAK     ; {2C2B} PUT Y LENGTH INTO
         STA   (]ADDR),Y  ; {6C2B} SECOND BYTE OF ARRAY
         INY              ; {2C1B} INCREMENT INDEX
         LDA   ]MCAND     ; {2C2B} PUT ELEMENT SIZE
         STA   (]ADDR),Y  ; {6C2B} INTO 3RD BYTE OF ARRAY
         LDX   ]ADDR      ; {2C2B} RETURN ARRAY ADDR LOBYTE IN .X
         LDY   ]ADDR+1    ; {2C2B} RETURN ARRAY ADDR HIBYTE IN .Y
         LDA   ]PROD      ; {2C2B} STORE PRODUCT LOBYTE IN RETURN
         STA   RETURN     ; {3C2B}
         LDA   ]PROD+1    ; {2C2B} STORE NEXT BYTE
         STA   RETURN+1   ; {3C2B}
         LDA   ]PROD+2    ; {2C2B} NEXT BYTE
         STA   RETURN+2   ; {3C2B}
         LDA   ]PROD+3    ; {2C2B} STORE HIBYTE
         STA   RETURN+3   ; {3C2B}
         LDA   #4         ; {2C2B} SIZE OF RETURN
         STA   RETLEN     ; {3C2B} SPECIFY RETURN LENGTH
         LDA   ]MCAND     ; {2C2B} RETURN ELEMENT SIZE IN .A
         RTS              ; {6C3B}


THE GET82 MACRO

SUMMARY

Condition Value
Name GET82
Type Macro
File MAC.ARR8B2D.ASM
Author Nathan Riggs
Last Revision 18-MAR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Retrieve an element from an 8-bit, 2-dimensional array
Input ]1 = Array Address
]2 = First Dimension Index
3] = Second Dimension Index
Output none
Dependencies SUB.AGET82.ASM
Flags Destroyed NZCV
Cycles 340+
Bytes 255
Notes None
See Also DIM82 ADIM82 AGET82 PUT82 APUT82

DETAILS

The GET82 macro retrieves a value from a given element at the X,Y position (first dimension, second dimension) in an eight-bit, two-dimensional array. The value retrieved is stored in RETURN, with the corresponding byte-length of the value stored in RETLEN. The length of the value is also passed back via the .A register, and the physical address where the element is stored is returned via the .X and .Y registers (low byte and high byte, respectively).

LISTING 3.23: The GET82 Macro Source

*
*``````````````````````````````*
* GET82         (NATHAN RIGGS) *
*                              *
* RETRIEVE VALUE FROM ELEMENT  *
* OF 8-BIT, TWO DIMENSIONAL    *
* ARRAY.                       *
*                              *
* PARAMETERS                   *
*                              *
*  ]1 = ARRAY ADDRESS          *
*  ]2 = X INDEX                *
*  ]3 = Y INDEX                *
*                              *
* CYCLES: 340+                 *
* SIZE: 222 BYTES              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
GET82    MAC
         _MLIT ]1;WPAR1   ; {16C12B}
         LDA   ]2         ; {3C2B} X INDEX
         STA   BPAR1      ; {3C2B}
         LDA   ]3         ; {3C2B} Y INDEX
         STA   BPAR2      ; {3C2B}
         JSR   AGET82     ; {312C192B}
         <<<
*


THE AGET82 SUBROUTINE

SUMMARY

Condition Value
Name AGET82
Type Subroutine
File SUB.AGET82.ASM
Author Nathan Riggs
Last Revision 18-MAR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Retrieve element from an 8-bit, 2-dimensional array
Input WPAR1 = Array Address
BPAR1 = First Dimension Index
BPAR2 = Second Dimension Index
Output none
Dependencies none
Flags Destroyed NZCV
Cycles 306+
Bytes 189
Notes None
See Also DIM82 ADIM82 GET82 PUT82 APUT82

DETAILS

The AGET82 subroutine retrieves an element value from a two-dimension, eight-bit array, returning the value in RETURN with its length byte in RETLEN. Note that the array in question must first have been initialized with the DIM82 macro or the ADIM82 subroutine in order to work properly, unless the exact same data structure is created by the programmer without the aid of the DIM functionality. Note also that GET82, like all array subroutines, does not do any error-checking: requesting an element that is out-of-bounds will result in trash being returned, if the system does not crash in the first place. A programmer should always be careful to manage array boundaries on her own, like memory management in general.

LISTING 3.24: The AGET82 Subroutine Source

*
*``````````````````````````````*
* AGET82        (NATHAN RIGGS) *
*                              *
* RETRIEVE AN ELEMENT VALUE    *
* FROM AN 8-BIT, 2D ARRAY AND  *
* HOLD IT IN THE RETURN        *
* ADDRESS, WITH ITS LENGTH IN  *
* RETLEN.                      *
*                              *
*------------------------------*
* 8-BIT MULTIPLICATION CODE    *
* ADAPTED FROM WHITE FLAME'S   *
* WORK ON CODEBASE64. LICENSE  *
* MAY VARY.                    *
*------------------------------*
*                              *
* INPUT:                       *
*                              *
*  WPAR1 = ARRAY ADDRESS       *
*  BPAR1 = 1ST DIM INDEX       *
*  BPAR2 = 2ND DIM INDEX       *
*                              *
* DESTROY: NZCIDV              *
*          ^^^  ^              *
*                              *
* CYCLES: 306+                 *
* SIZE: 189 BYTES              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]ADDR    EQU   WPAR1      ; ARRAY ADDRESS
]XIDX    EQU   BPAR1      ; 1ST DIMENSION INDEX
]YIDX    EQU   BPAR2      ; 2ND DIMENSION INDEX
*
]XLEN    EQU   VARTAB+0   ; X DIMENSION LENGTH
]YLEN    EQU   VARTAB+2   ; Y DIMENSION LENGTH
]PROD    EQU   VARTAB+4   ; PRODUCT
]MLIER   EQU   VARTAB+8   ; MULTIPLIER
]MCAND   EQU   VARTAB+10  ; MULTIPLICAND
]ELEN    EQU   VARTAB+12  ; ELEMENT LENGTH
]PBAK    EQU   VARTAB+14  ; PRODUCT BACKUP
*
AGET82
         LDY   #0         ; {2C2B} RESET INDEX
         LDA   (]ADDR),Y  ; {6C2B} GET X-LENGTH FROM ARRAY
         STA   ]XLEN      ; {3C2B}
         LDY   #1         ; {2C2B} INCREMENT INDEX
         LDA   (]ADDR),Y  ; {6C2B} GET Y-LENGTH FROM ARRAY
         STA   ]YLEN      ; {3C2B}
         LDY   #2         ; {2C2B} INCREMENT INDEX
         LDA   (]ADDR),Y  ; {6C2B} GET ELEMENT LENGTH FROM ARRAY
         STA   ]ELEN      ; {3C2B}
*
** MULTIPLY Y-INDEX BY Y-LENGTH
*
         LDA   #0         ; {2C2B} RESET LOBYTE
         TAY              ; {2C1B} RESET HIBYTE
         STY   SCRATCH    ; {3C2B} SAVE HIBYTE IN SCRATCH
         BEQ   :ENTLP     ; {3C2B} IF ZERO, SKIP TO LOOP
:DOADD
         CLC              ; {2C1B} CLEAR CARRY FLAG
         ADC   ]YIDX      ; {4C3B} ADD Y-INDEX
         TAX              ; {2C1B} TEMPORARILY STORE IN .X
         TYA              ; {2C1B} LOAD HIBYTE TO .A
         ADC   SCRATCH    ; {4C3B} ADD HIBYTE
         TAY              ; {2C1B} TRANSFER BACK INTO .Y
         TXA              ; {2C1B} RELOAD LOBYTE
:LP
         ASL   ]YIDX      ; {6C3B} MULTIPLY Y-INDEX BY 2
         ROL   SCRATCH    ; {6C3B} DEAL WITH HIBYTE
:ENTLP
         LSR   ]YLEN      ; {6C3B} DIVIDE Y-LENGTH BY 2
         BCS   :DOADD     ; {3C2B} IF >= LOBYTE IN .A, ADD AGAIN
         BNE   :LP        ; {3C2B} ELSE, LOOP
         STX   ]PBAK      ; {3C2B} STORE LOBYTE IN PRODUCT BACKUP
         STY   ]PBAK+1    ; {3C2B} STORE HIBYTE
*
** NOW MULTIPLY LENGTH OF ELEMENTS BY XIDX
*
         LDA   ]XIDX      ; {3C2B} PUT X-INDEX INTO
         STA   ]MLIER     ; {3C2B} MULTIPLIER
         LDA   ]ELEN      ; {3C2B} ELEMENT LENGTH INTO
         STA   ]MCAND     ; {3C2B} MULTIPLICAND
         LDA   #0         ; {2C2B} RESET PRODUCT LOBYTE
         STA   ]MLIER+1   ; {3C2B} RESET MULTIPLIER HIBYTE
         STA   ]MCAND+1   ; {3C2B} RESET MULTIPLICAND HIBYTE
         STA   ]PROD      ; {3C2B}
         STA   ]PROD+1    ; {3C2B} RESET PRODUCT 2ND BYTE
         STA   ]PROD+2    ; {3C2B} RESET PRODUCT 3RD BYTE
         STA   ]PROD+3    ; {3C2B} RESET PRODUCT HIBYTE
         LDX   #$10       ; {2C2B} LOAD $10 INTO .X (#16)
:SHIFTR  LSR   ]MLIER+1   ; {6C3B} DIVIDE MULTIPLIER BY 2
         ROR   ]MLIER     ; {6C3B} ADJUST LOBYTE
         BCC   :ROTR      ; {3C2B} IF < PRODUCT, ROTATE
         LDA   ]PROD+2    ; {3C2B} LOAD PRODUCT 3RD BYTE
         CLC              ; {2C1B} CLEAR CARRY FLAG
         ADC   ]MCAND     ; {4C3B} ADD MULTIPLICAND
         STA   ]PROD+2    ; {3C2B} STORE BACK INTO 3RD
         LDA   ]PROD+3    ; {3C2B} LOAD HIBYTE
         ADC   ]MCAND+1   ; {4C3B} ADD MULTIPLICAND HIBYTE
:ROTR
         ROR              ; {6C3B} ROTATE .A RIGHT
         STA   ]PROD+3    ; {3C2B} STORE IN PRODUCT HIBYTE
         ROR   ]PROD+2    ; {6C3B} ROTATE PRODUCT 3RD BYTE
         ROR   ]PROD+1    ; {6C3B} ROTATE PRODUCT 2ND BYTE
         ROR   ]PROD      ; {6C3B} ROTATE PRODUCT LOBYTE
         DEX              ; {2C1B} DECREMENT COUNTER
         BNE   :SHIFTR    ; {3C2B} IF NOT 0, BACK TO SHIFTER
         LDA   ]PROD      ; {3C2B} LOAD PRODUCT LOBYTE
         CLC              ; {2C1B} CLEAR CARRY FLAG
         ADC   #3         ; {2C2B} INCREASE BY 3
         STA   ]PROD      ; {3C2B} STORE BACK INTO LOBYTE
         LDA   ]PROD+1    ; {3C2B} ACCOUNT FOR CARRIES
         ADC   #0         ; {2C2B}
         STA   ]PROD+1    ; {3C2B}
*
** NOW ADD THAT TO EARLIER CALC
*
         CLC              ; {2C1B} CLEAR CARRY FLAG
         LDA   ]PROD      ; {3C2B} LOAD PRODUCT LOBYTE
         ADC   ]PBAK      ; {4C3B} ADD PREVIOUS PRODUCT
         STA   ]PROD      ; {3C2B} STORE NEW PRODUCT LOBYTE
         LDA   ]PROD+1    ; {3C2B} LOAD PRODUCT HIBYTE
         ADC   ]PBAK+1    ; {4C3B} ADD PREV PRODUCT HIBYTE
         STA   ]PROD+1    ; {3C2B} STORE PRODUCT HIBYTE
*
** NOW ADD ARRAY ADDRESS TO GET INDEX ADDR
*
         CLC              ; {2C1B} CLEAR CARRY FLAG
         LDA   ]PROD      ; {3C2B} LOAD PRODUCT LOBYTE
         ADC   ]ADDR      ; {4C3B} ADD ARRAY ADDRESS LOBYTE
         STA   ]PROD      ; {3C2B} STORE BACK IN PRODUCT LOBYTE
         LDA   ]PROD+1    ; {3C2B} LOAD HIBYTE
         ADC   ]ADDR+1    ; {4C3B} ADD ADDRESS HIBYTE
         STA   ]PROD+1    ; {3C2B} STORE IN PRODUCT HIBYTE
*
         LDY   ]PROD      ; {3C2B} LOAD PRODUCT LOBYTE IN .Y
         LDX   ]PROD+1    ; {3C2B} LOAD HIBYTE IN .X FOR SOME REASON
         STY   ]ADDR      ; {3C2B} TRANSFER TO ZERO PAGE
         STX   ]ADDR+1    ; {3C2B}
         LDY   #0         ; {2C2B} RESET INDEX
:RLP
         LDA   (]ADDR),Y  ; {6C2B} LOAD BYTE
         STA   RETURN,Y   ; {3C2B} STORE IN RETURN
         INY              ; {2C1B} INCREASE INDEX
         CPY   ]ELEN      ; {4C3B} IF INDEX != ELEMENT LENGTH
         BNE   :RLP       ; {3C2B} THEN KEEP COPYING
         LDA   ]ELEN      ; {3C2B} OTHERWISE, STORE ELEMENT LENGTH
         STA   RETLEN     ; {3C2B} INTO RETURN LENGTH
         LDA   RETLEN     ; {3C2B} AND IN .A
         LDX   ]ADDR      ; {3C2B} RETURN ARRAY ADDRESS LOBYTE IN .X
         LDY   ]ADDR+1    ; {3C2B} RETURN HIBYTE IN .Y
         RTS              ; {6C1B}


THE PUT82 MACRO

SUMMARY

Condition Value
Name PUT82
Type Macro
File MAC.ARR8B2D.ASM
Author Nathan Riggs
Last Revision 18-MAR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Write value to element in an 8-bit, 2-dimensional array
Input ]1 = Source Address
]2 = Destination Array Address
]3 = First Dimension Index
]4 = Second Dimension Index
Output none
Dependencies SUB.APUT82.ASM
Flags Destroyed NZCV
Cycles 352+
Bytes 223
Notes None
See Also DIM82 ADIM82 GET82 AGET82 APUT82

DETAILS

The PUT82 macro inserts the value given in a source memory address range into the specified X,Y element of the destination array's address. The number of bytes copied is determined by the array's element length attribute. As with other array routines, there is no error-checking for out-of-bounds requests; it is up to the programmer to keep track of an array's boundaries once they are set. Using PUT82 with out-of-bounds dimensional indices will either overwrite important data stored already or will cause a fatal crash. Additionally note that this macro only accepts an address for the value to be copied, not a literal value; this too is in common with other array PUT macros and subroutines.

After being called, the address of the element in question is returned via the .X and .Y registers (the low and high byte of the address, respectively), and the element length is returned in the .A register.

LISTING 3.25: The PUT82 Macro Source

*
*``````````````````````````````*
* PUT82         (NATHAN RIGGS) *
*                              *
* SET VALUE OF AN ELEMENT IN   *
* AN 8-BIT, TWO-DIMENSIONAL    *
* ARRAY.                       *
*                              *
* PARAMETERS                   *
*                              *
*  ]1 = SOURCE ADDRESS         *
*  ]2 = DEST ARRAY ADDRESS     *
*  ]3 = ELEMENT X INDEX        *
*  ]4 = Y INDEX                *
*                              *
* CYCLES: 352+                 *
* SIZE: 223 BYTES              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
PUT82    MAC
         _MLIT ]1;WPAR1   ; {16C12B} PARSE SOURCE ADDRESS
         _MLIT ]2;WPAR2   ; {16C12B} PARSE DEST ADDRESS
         LDA   ]3         ; {3C2B} X INDEX
         STA   BPAR1      ; {3C2B}
         LDA   ]4         ; {3C2B} Y INDEX
         STA   BPAR2      ; {3C2B}
         JSR   APUT82     ; {308C191B}
         <<<
*


THE APUT82 SUBROUTINE

SUMMARY

Condition Value
Name APUT82
Type Subroutine
File SUB.APUT82.ASM
Author Nathan Riggs
Last Revision 18-MAR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Write a value into an 8-bit, 1-dimensional array's element
Input WPAR1 = Source Address
WPAR2 = Destination Array Address
BPAR1 = First Dimension Index
BPAR2 = Second Dimension Index
Output none
Dependencies none
Flags Destroyed NZCV
Cycles 302+
Bytes 188
Notes None
See Also DIM82 ADIM82 GET82 AGET82 PUT82

DETAILS

The APUT82 subroutine takes a series of bytes found in a source address and places them into the given X,Y element of the specified eight-bit, two-dimensional array. As stated in the associated macro's entry (PUT82), note that 1) no error-checking is involved, thus the boundaries are not protected, and 2) a value can only be transferred from another memory location to the desired array element; that is, direct "putting" into the element cannot be accomplished with this subroutine.

Once finished, the subroutine returns the beginning address of the element in the .X register (low byte) and .Y register (high byte), with the element length returned in the .A register.

LISTING 3.26: The APUT82 Subroutine Source

*
*``````````````````````````````*
* APUT82        (NATHAN RIGGS) *
*                              *
* PUT DATA FROM SOURCE INTO    *
* A 2D, 8BIT ARRAY ELEMENT.    *
*                              *
* INPUT:                       *
*                              *
*  WPAR1 = SOURCE ADDRESS      *
*  WPAR2 = ARRAY ADDRESS       *
*  BPAR1 = 1ST DIM INDEX       *
*  BPAR2 = 2ND DIM INDEX       *
*                              *
* DESTROY: NZCIDV              *
*          ^^^  ^              *
*                              *
* CYCLES: 302+                 *
* SIZE: 188 BYTES              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]ADDRS   EQU   WPAR1      ; SOURCE ADDRESS
]ADDRD   EQU   WPAR2      ; ARRAY ADDRESS
]XIDX    EQU   BPAR1      ; X INDEX
]YIDX    EQU   BPAR2      ; Y INDEX
*
]ESIZE   EQU   VARTAB     ; ELEMENT LENGTH
]MCAND   EQU   VARTAB+1   ; MULTIPLICAND
]MLIER   EQU   VARTAB+3   ; MULTIPLIER
]PROD    EQU   VARTAB+5   ; PRODUCT
]XLEN    EQU   VARTAB+9   ; ARRAY X-LENGTH
]YLEN    EQU   VARTAB+13  ; ARRAY Y-LENGTH
]PBAK    EQU   VARTAB+15  ; PRODUCT BACKUP
*
APUT82
         LDY   #0         ; {2C2B} RESET INDEX
         LDA   (]ADDRD),Y ; {6C2B} GET ARRAY X-LENGTH
         STA   ]XLEN      ; {3C2B}
         LDY   #1         ; {2C2B} INCREMENT INDEX
         LDA   (]ADDRD),Y ; {6C2B} GET ARRAY Y-LENGTH
         STA   ]YLEN      ; {3C2B}
         LDY   #2         ; {2C2B} INCREMENT INDEX
         LDA   (]ADDRD),Y ; {6C2B} GET ARRAY ELEMENT LENGTH
         STA   ]ESIZE     ; {3C2B}
*
** MULTIPLY Y-INDEX BY Y-LENGTH
*
         LDA   #0         ; {2C2B} RESET LOBYTE
         TAY              ; {2C1B} RESET HIBYTE
         STY   SCRATCH    ; {3C2B} SAVE HIBYTE IN SCRATCH
         BEQ   :ENTLP     ; {3C2B} IF ZERO, SKIP TO LOOP
:DOADD
         CLC              ; {2C1B} CLEAR CARRY FLAG
         ADC   ]YIDX      ; {4C3B} ADD Y-INDEX
         TAX              ; {2C1B} STORE IN .X
         TYA              ; {2C1B} LOAD HIBYTE
         ADC   SCRATCH    ; {4C3B} ADD HIBYTE
         TAY              ; {2C1B} STORE IN .Y
         TXA              ; {2C1B} RELOAD LOBYTE
:LP
         ASL   ]YIDX      ; {6C3B} MULTIPLY Y-INDEX BY 2
         ROL   SCRATCH    ; {6C3B} DEAL WITH HIBYTE
:ENTLP
         LSR   ]YLEN      ; {6C3B} DIVIDE Y-LENGTH BY 2
         BCS   :DOADD     ; {3C2B} IF >= LOBYTE, ADD AGAIN
         BNE   :LP        ; {3C2B} ELSE, LOOP
         STX   ]PBAK      ; {3C2B} STORE LOBYTE IN PRODUCT BACKUP
         STY   ]PBAK+1    ; {3C2B} STORE HIBYTE
         LDA   ]XIDX      ; {3C2B} PUT X-INDEX INTO MULTIPLIER
         STA   ]MLIER     ; {3C2B}
         LDA   #0         ; {2C2B} RESET HIBYTE
         STA   ]MLIER+1   ; {3C2B} TRANSFER HIBYTE
         LDA   ]ESIZE     ; {3C2B} PUT ELEMENT LENGTH
         STA   ]MCAND     ; {3C2B} INTO MULTIPLICAND
         LDA   #0         ; {2C2B} RESET HIBYTE
         STA   ]MCAND+1   ; {3C2B}
*
** NOW MULTIPLY XIDX BY ELEMENT LENGTH
*
         STA   ]PROD      ; {3C2B} RESET PRODUCT LOBYTE
         STA   ]PROD+1    ; {3C2B} RESET 2ND BYTE
         STA   ]PROD+2    ; {3C2B} RESET 3RD BYTE
         STA   ]PROD+3    ; {3C2B} RESET HIBYTE
         LDX   #$10       ; {2C2B} LOAD $10 INTO .X (#16)
:SHIFTR  LSR   ]MLIER+1   ; {6C3B} DIVIDE MULTIPLIER BY 2
         ROR   ]MLIER     ; {6C3B} DEAL WITH HIBYTE
         BCC   :ROTR      ; {3C2B} IF < RODUCT, ROTATE
         LDA   ]PROD+2    ; {3C2B} LOAD PRODUCT 3RD BYTE
         CLC              ; {2C1B} CLEAR CARRY FLAG
         ADC   ]MCAND     ; {4C3B} ADD MULTIPLICAND
         STA   ]PROD+2    ; {3C2B} STORE 3RD BYTE
         LDA   ]PROD+3    ; {3C2B} LOAD HIBYTE
         ADC   ]MCAND+1   ; {4C3B} ADD MULTIPLICAND HIBYTE
:ROTR
         ROR              ; {6C3B} ROTATE .A RIGHT
         STA   ]PROD+3    ; {3C2B} STORE IN PRODUCT HIBYTE
         ROR   ]PROD+2    ; {6C3B} ROTATE PRODUCT 3RD BYTE
         ROR   ]PROD+1    ; {6C3B} ROTATE RODUCT 2ND
         ROR   ]PROD      ; {6C3B} ROTATE LOBYTE
         DEX              ; {2C1B} DECREMENT COUNTER
         BNE   :SHIFTR    ; {3C2B} IF NOT 0, BACK TO SHIFTER
*
** NOW ADD PRODUCT TO REST
*
         LDA   ]PBAK      ; {3C2B} LOAD FIRST PRODUCT LOBYTE
         CLC              ; {2C1B} CLEAR CARRY FLAG
         ADC   ]PROD      ; {4C3B} ADD 2ND PRODUCT LOBYTE
         STA   ]PROD      ; {3C2B} STORE NEW PRODUCT LOBYTE
         LDA   ]PBAK+1    ; {3C2B} LOAD FIRST PRODUCT HIBYTE
         ADC   ]PROD+1    ; {4C3B} ADD 2ND HIBYTE
         STA   ]PROD+1    ; {3C2B} STORE HIBYTE
         LDA   ]PROD      ; {3C2B} LOAD NEW PRODUCT LOBYTE
         CLC              ; {2C1B} CLEAR CARRY FLAG
         ADC   #3         ; {4C3B} INCREASE BY 3
         STA   ]PROD      ; {3C2B} STORE IN LOBYTE
         LDA   ]PROD+1    ; {3C2B} APPLY CARRY TO HIBYTE
         ADC   #0         ; {2C2B}
         STA   ]PROD+1    ; {3C2B}
*
** ADD ARRAY ADDRESS TO GET INDEX
*
         CLC              ; {2C1B} CLEAR CARRY FLAG
         LDA   ]PROD      ; {3C2B} LOAD PRODUCT LOBYTE
         ADC   ]ADDRD     ; {4C3B} ADD ARRAY ADDRESS LOBYTE
         STA   ]PROD      ; {3C2B} STORE IN PRODUCT
         LDA   ]PROD+1    ; {3C2B} LOAD PRODUCT HIBYTE
         ADC   ]ADDRD+1   ; {4C3B} ADD ARRAYH ADDRESS HIBYTE
         STA   ]PROD+1    ; {3C2B} STORE HIBYTE
         LDX   ]PROD      ; {3C2B} PUT ELEMENT ADDRESS LOBYTE IN .X
         LDY   ]PROD+1    ; {3C2B} PUT HIBYTE IN Y
         STX   ADDR2      ; {3C2B} STORE IN ZERO PAGE
         STY   ADDR2+1    ; {3C2B}
         LDY   #0         ; {2C2B} RESET INDEX
*
** COPY FROM SRC ADDR TO DEST ADDR
*
:CLP
         LDA   (]ADDRS),Y ; {6C2B} GET BYTE FROM SOURCE
         STA   (ADDR2),Y  ; {3C2B} STORE IN ELEMENT
         INY              ; {2C1B} INCREASE INDEX
         CPY   ]ESIZE     ; {4C3B} IF < ELEMENT SIZE,
         BNE   :CLP       ; {3C2B} CONTINUE COPYING
         LDX   ADDR2      ; {3C2B} PUT ELEMENT LOBYTE IN .X
         LDY   ADDR2+1    ; {3C2B} PUT HIBYTE IN .Y
         LDA   ]ESIZE     ; {3C2B} PUT ELEMENT SIZE IN .A
         RTS              ; {6C1B}


1-Dimensional, 16-Bit Arrays

These macros and subroutines are dedicated to using arrays with a maximum of 65,025 elements per dimension (in this case, one dimension), with each element being up to 255 bytes in length. The macros for these arrays are located in the MAC.ARR16B1D.ASM file, named so after the types of macros it includes (16 Bytes, 1 Dimensions).

If it is not obvious: a single array with an element size of one byte each can easily take up more memory than most Apple II computers can contain. The macros and subroutines listed here are inadequate for implementations that require such large arrays, as they would also (usually) need to access extra memory provided by third-party hardware. Even in normal 128k systems like the Apple //c, these macros and subroutines cannot utilize memory to its fullest extent; while bank switching can be enacted by the programmer, an array created with this collection cannot fill part of one bank and continue to another.

LISTING 3.30: MAC.ARR16B1D.ASM Header Source

*
*``````````````````````````````*
* MAC.ARR16B1D.ASM             *
*                              *
* A MACRO LIBRARY FOR 16-BIT   *
* 1-DIMENSIONAL ARRAYS.        *
*                              *
* AUTHOR:    NATHAN RIGGS      *
* CONTACT:   NATHAN.RIGGS@     *
*            OUTLOOK.COM       *
*                              *
* DATE:      18-MAR-2021       *
* ASSEMBLER: MERLIN 8 PRO      *
* OS:        DOS 3.3           *
*                              *
* SUBROUTINE FILES USED        *
*                              *
*  SUB.ADIM161                 *
*  SUB.AGET161                 *
*  SUB.APUT161                 *
*                              *
* LIST OF MACROS               *
*                              *
* DIM161: DIM 1D, 16BIT ARRAY  *
* GET161: GET ELEMENT FROM 1D, *
*         16BIT ARRAY.         *
* PUT161: PUT VALUE INTO A 1D, *
*         16BIT ARRAY INDEX.   *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*


THE DIM161 MACRO

SUMMARY

Condition Value
Name DIM161
Type Macro
File MAC.ARR16B1D.ASM
Author Nathan Riggs
Last Revision 18-MAR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Initialize a 16-bit, 1-dimensional array
Input ]1 = Array Address
]2 = Number of Elements
]3 = Element Byte Length
]4 = Default Fill Value
Output none
Dependencies SUB.ADIM161.ASM
Flags Destroyed NZCV
Cycles 271+
Bytes 165
Notes none
See Also ADIM161 GET161 AGET161 PUT161 APUT161

DETAILS

The DIM161 macro initializes a one-dimensional array that can hold up to 65,025 elements, with each element being less than 255 bytes long. Note that not only can this macro easily fill the entire available memory on many Apple II machines, but it can also accidentally write over data or program statements that are important to the overall stability and functioning of the program and the computer. As such, the programmer should calculate the required amount of memory necessary for the DIM161 array and make sure the space is clear prior to actually calling the macro. This can be done by multiplying the number of elements being declared by the given element length, then adding to account for the 3-byte header of the array. The header consists of three bytes that hold the number of elements and the size of the elements. The first two bytes indicate the number of elements as a 16-bit number (big-endian) and the third byte indicates the length of each element, which can vary from 1 to 255.

After finishing initialization, the macro returns the total length of the array, in bytes, in RETURN (16-bit value). While this might be useful information after the fact, it does nothing to solve the problem of knowing the array's complete size before execution.

LISTING 3.31: The DIM161 Macro Source

*
*``````````````````````````````*
* DIM161        (NATHAN RIGGS) *
*                              *
* INITIALIZE A 16-BIT ARRAY    *
* WITH A SINGLE DIMENSION.     *
*                              *
* PARAMETERS                   *
*                              *
*  ]1 = ARRAY ADDRESS          *
*  ]2 = ARRAY BYTE LENGTH      *
*  ]3 = ELEMENT BYTE LENGTH    *
*  ]4 = ARRAY FILL VALUE       *
*                              *
* CYCLES: 271+                 *
* SIZE: 165 BYTES              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
DIM161   MAC
         _MLIT ]1;WPAR1   ; {16C12B} PARSE ARRAY ADDRESS
         _MLIT ]2;WPAR2   ; {16C12B} PARSE BYTE LENGTH
         LDA   ]3         ; {3C2B} ELEMENT LENGTH
         STA   WPAR3      ; {3C2B}
         LDA   ]4         ; {3C2B} FILL VALUE
         STA   BPAR1      ; {3C2B}
         JSR   ADIM161    ; {227C133B}
         <<<
*


THE ADIM161 SUBROUTINE

SUMMARY

Condition Value
Name ADIM161
Type Subroutine
File SUB.ADIM161.ASM
Author Nathan Riggs
Last Revision 18-MAR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Initialize a single-dimension, 16-bit array
Input WPAR1 = Array Address
WPAR2 = Number of Elements
WPAR3 = Element Byte-length
BPAR1 = Default Fill Value
Output none
Dependencies none
Flags Destroyed NZCV
Cycles 221+
Bytes 130
Notes None
See Also DIM161 GET161 AGET161 PUT161 APUT161

DETAILS

The ADIM161 subroutine creates the data structure for a 16-bit, one-dimensional array, which can hold up to 65,025 elements of a length between 1 and 255 bytes. Please see the listing for the DIM161 macro for a short description of how the routine works as well as any necessary precautions to be considered.

LISTING 3.32: The ADIM161 Subroutine Source

*
*``````````````````````````````*
* ADIM161       (NATHAN RIGGS) *
*                              *
* INITIALIZE A 16BIT, 1D ARRAY *
*                              *
*------------------------------*
* MULTIPLICATION CODE ADAPTED  *
* FROM WHITE FLAME'S WORK ON   *
* CODEBASE64. LICENSE MAY VARY *
*------------------------------*
*                              *
* INPUT:                       *
*                              *
*  WPAR1 = ARRAY ADDRESS       *
*  WPAR2 = # OF ELEMENTS       *
*  WPAR3 = ELEMENT LENGTH      *
*  BPAR1 = FILL VALUE          *
*                              *
* DESTROY: NZCIDV              *
*          ^^^  ^              *
*                              *
* CYCLES: 221+                 *
* SIZE: 130 BYTES              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]ADDRD   EQU   WPAR1      ; ARRAY ADDRESS
]ASIZE   EQU   WPAR2      ; # OF ELEMENTS
]ESIZE   EQU   WPAR3      ; ELEMENT BYTE LENGTH
]FILL    EQU   BPAR1      ; FILL VALUE
*
]MSIZE   EQU   VARTAB     ; TOTAL ARRAY BYTES
]ASZBAK  EQU   VARTAB+4   ; BACKUP OF ELEMENT #
]ESZBAK  EQU   VARTAB+7   ; BACKUP
*
ADIM161
         LDA   ]ESIZE     ; {3C2B} ELEMENT SIZE
         STA   ]ESZBAK    ; {3C2B} ELEMENT LENGTH BACKUP
         LDA   ]ASIZE     ; {3C2B}
         STA   ]ASZBAK    ; {3C2B} ARRAY SIZE BACKUP
         LDA   ]ASIZE+1   ; {3C2B}
         STA   ]ASZBAK+1  ; {3C2B} BACKUP
         STA   SCRATCH    ; {3C2B} HIBYTE FOR MULTIPLICATION
         LDA   ]ADDRD     ; {3C2B}
         STA   ADDR2      ; {3C2B}
         LDA   ]ADDRD+1   ; {3C2B}
         STA   ADDR2+1    ; {3C2B}
         LDY   #0         ; {2C2B} CLEAR INDEX
         LDA   #0         ; {2C2B} CLEAR ACCUMULATOR
         BEQ   :ENTLP     ; {3C2B} IF 0, SKIP TO LOOP
*
** MULTIPLY ARRAY SIZE BY ELEMENT SIZE
*
:DOADD
         CLC              ; {2C1B} CLEAR CARRY FLAG
         ADC   ]ASIZE     ; {4C3B} ADD ARRAY SIZE
         TAX              ; {2C1B} HOLD IN .X
         TYA              ; {2C1B} LOAD HIBYTE
         ADC   SCRATCH    ; {4C3B} ADD HIBYTE
         TAY              ; {2C1B} HOLD IN .Y
         TXA              ; {2C1B} RELOAD LOBYTE
:LP
         ASL   ]ASIZE     ; {6C3B} MULTIPLY ARRAY SIZE BY 2
         ROL   SCRATCH    ; {6C3B} ADJUST HIBYTE
:ENTLP
         LSR   ]ESIZE     ; {6C3B} DIVIDE ELEMENT SIZE BY 2
         BCS   :DOADD     ; {3C2B} IF >= LOBYTE IN .A,
         BNE   :LP        ; {3C2B} ADD AGAIN--ELSE, LOOP
         CLC              ; {2C1B} CLEAR CARRY
         TXA              ; {2C1B} LOBYTE TO .A
         ADC   #3         ; {4C3B} ADD 2 FOR HEADER
         STA   ]MSIZE     ; {3C2B} STORE IN TOTAL LOBYTE
         TYA              ; {2C1B} HIBYTE TO .A
         ADC   #0         ; {4C3B} DO CARRY
         STA   ]MSIZE+1   ; {3C2B} STORE IN TOTAL HIBYTE
*
** CLEAR MEMORY BLOCKS
*
         LDA   ]FILL      ; {3C2B} GET FILL VALUE
         LDX   ]MSIZE+1   ; {3C2B} LOAD TOTAL SIZE LOBYTE
         BEQ   :PART      ; {3C2B} IF NO WHOLE PAGES, JUST PART
         LDY   #0         ; {2C2B} RESET INDEX
:FULL
         STA   (]ADDRD),Y ; {6C2B} COPY BYTE TO ADDRESS
         INY              ; {2C1B} NEXT BYTE
         BNE   :FULL      ; {3C2B} LOOP UNTIL PAGE DONE
         INC   ]ADDRD+1   ; {6C2B} GO TO NEXT PAGE
         DEX              ; {2C1B} DECREMENT COUNTER
         BNE   :FULL      ; {3C2B} LOOP IF PAGES LEFT
:PART
         LDX   ]MSIZE     ; {3C2B} PARTIAL PAGE BYTES
         BEQ   :MFEXIT    ; {3C2B} EXIT IF = 0
         LDY   #0         ; {2C2B} RESET INDEX
:PARTLP
         STA   (]ADDRD),Y ; {6C2B} STORE BYTE
         INY              ; {2C1B} INCREMENT INDEX
         DEX              ; {2C1B} DECREMENT COUNTER
         BNE   :PARTLP    ; {3C2B} LOOP UNTIL DONE
:MFEXIT
         LDY   #0         ; {2C2B} RESET INDEX
         LDA   ]ASZBAK    ; {3C2B} STORE ARRAY SIZE IN HEADER
         STA   (ADDR2),Y  ; {6C2B}
         INY              ; {2C1B} INCREASE INDEX
         LDA   ]ASZBAK+1  ; {3C2B} STORE ARRAY SIZE HIBYTE
         STA   (ADDR2),Y  ; {6C2B}
         INY              ; {2C1B} INCREMENT INDEX
         LDA   ]ESZBAK    ; {3C2B} STORE ELEMENT SIZE
         STA   (ADDR2),Y  ; {6C2B} IN HEADER
         LDX   ]ADDRD     ; {3C2B} .X HOLDS ARRAY ADDRESS LOBYTE
         LDY   ]ADDRD+1   ; {3C2B} .Y HOLDS HIBYTE
         LDA   ]MSIZE     ; {3C2B} STORE TOTAL ARRAY SIZE
         STA   RETURN     ; {3C2B} IN RETURN
         LDA   ]MSIZE+1   ; {3C2B}
         STA   RETURN+1   ; {3C2B}
         LDA   #2         ; {2C2B}
         STA   RETLEN     ; {3C2B} 2 BYTE LENGTH
         LDA   ]ASZBAK    ; {3C2B} .A HOLDS # OF ELEMENTS
         RTS              ; {6C1B}


THE GET161 MACRO

SUMMARY

Condition Value
Name GET161
Type Macro
File MAC.ARR16B1D.ASM
Author Nathan Riggs
Last Revision 18-MAR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Get an element value from a 16-bit, 1-dimensional array
Input ]1 = Array Address
]2 = Element Index
Output none
Dependencies SUB.AGET161.ASM
Flags Destroyed NZCV
Cycles 223+
Bytes 130
Notes none
See Also DIM161 ADIM161 AGET161 PUT161 APUT161

DETAILS

The GET161 macro retrieves a value from a desired element in a one-dimensional, 16-bit array. After retrieval, the data is stored in RETURN, with the byte length of the data held in RETLEN (the length is determined by the array's element length attribute). Additionally, the element length is held in .A after execution, and the address of the given element is held in the .X and .Y registers (low byte and high byte of address, respectively).

While requesting an out-of-bounds element is unlikely to freeze the system on its own (unless a soft switch happens to be in the area accessed), using the trash data collected from such an attempt may lead to crashes. As such, the programmer should take care to always know the bounds of the given array.

LISTING 3.33: The GET161 Macro

*
*``````````````````````````````*
* GET161        (NATHAN RIGGS) *
*                              *
* GET THE VALUE STORED IN THE  *
* ELEMENT OF A 16-BIT, ONE-    *
* DIMENSIONAL ARRAY.           *
*                              *
* PARAMETERS                   *
*                              *
*  ]1 = SOURCE ADDRESS         *
*  ]2 = ARRAY ADDRESS          *
*                              *
* CYCLES: 223+                 *
* SIZE: 130 BYTES              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
GET161   MAC
         _MLIT ]1;WPAR1   ; {16C12B} PARSE SOURCE ADDRESS
         _MLIT ]2;WPAR2   ; {16C12B} PARSE INDEX
         JSR   AGET161    ; {191C116B}
         <<<
*


THE AGET161 SUBROUTINE

SUMMARY

Condition Value
Name AGET161
Type Subroutine
File SUB.AGET161.ASM
Author Nathan Riggs
Last Revision 18-MAR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose read an element value in a 16-bit, one-dimensional array
Input WPAR1 = Array Address
WPAR2 = Element Index
Output none
Dependencies none
Flags Destroyed NZCV
Cycles 185+
Bytes 113
Notes None
See Also DIM161 ADIM161 GET161 PUT161 APUT161

DETAILS

The AGET161 subroutine receives its parameters from WPAR1 and WPAR2, where the Array Address and the Element Index is held (respectively), then retrieves the value in the given array's indexed element. This data is held in RETURN, with the length of the value (in bytes) held in both RETLEN and the .A register. Note that like all array GET macros and subroutines, this follows the TITO rule: trash in, trash out. If an element is requested that is out of bounds for the given array, then a trash value will be returned--an error will not interrupt the flow of execution. Thus, the programmer is responsible for keeping track of all array boundaries.

LISTING 3.34: The AGET161 Subroutine Source

*
*``````````````````````````````*
* AGET161       (NATHAN RIGGS) *
*                              *
* GET DATA IN 16-BIT, 2D ARRAY *
*                              *
*------------------------------*
* MULTIPLICATION CODE ADAPTED  *
* FROM WHITE FLAME'S WORK ON   *
* CODEBASE64. LICENSE MAY VARY *
*------------------------------*
*                              *
* INPUT:                       *
*                              *
*  WPAR1 = ARRAY ADDRESS       *
*  WPAR2 = ELEMENT INDEX       *
*                              *
* DESTROY: NZCIDV              *
*          ^^^  ^              *
*                              *
* CYCLES: 185+                 *
* SIZE: 113 BYTES              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]AIDX    EQU   WPAR2      ; ELEMENT INDEX
]ADDR    EQU   WPAR1      ; ARRAY ADDRESS
*
]ESIZE   EQU   VARTAB     ; ELEMENT LENGTH
]ESIZEB  EQU   VARTAB+1   ; ^BACKUP
]ASIZE   EQU   VARTAB+2   ; NUMBER OF ELEMENTS
]IDX     EQU   VARTAB+6   ; INDEX BACKUP
*
AGET161
         LDA   ]AIDX      ; {3C2B}
         STA   ]IDX       ; {3C2B}
         LDA   ]AIDX+1    ; {3C2B} GET INDEX HIBYTE
         STA   ]AIDX+1    ; {3C2B}
         STA   SCRATCH    ; {3C2B}
         LDY   #0         ; {2C2B} RESET INDEX
         LDA   (]ADDR),Y  ; {6C2B} GET NUMBER OF
         STA   ]ASIZE     ; {3C2B} ARRAY ELEMENTS
         LDY   #1         ; {2C2B} GET HIBYTE OF
         LDA   (]ADDR),Y  ; {6C2B} # OF ARRAY ELEMENTS
         STA   ]ASIZE+1   ; {3C2B}
         INY              ; {2C1B} INCREASE BYTE INDEX
         LDA   (]ADDR),Y  ; {6C2B} GET ELEMENT LENGTH
         STA   ]ESIZE     ; {3C2B}
         STA   ]ESIZEB    ; {3C2B}
*
** MULTIPLY INDEX BY ELEMENT SIZE, ADD 3
*
         LDY   #0         ; {2C2B} RESET .Y AND .A
         LDA   #0         ; {2C2B}
         BEQ   :ENTLPA    ; {3C2B} IF ZERO, SKIP TO LOOP
:DOADD
         CLC              ; {2C1B} CLEAR CARRY
         ADC   ]AIDX      ; {4C3B} ADD INDEX TO .A
         TAX              ; {2C1B} HOLD IN .X
         TYA              ; {2C1B} LOAD HIBYTE
         ADC   SCRATCH    ; {4C3B} ADD HIBYTE
         TAY              ; {2C1B} HOLD IN .Y
         TXA              ; {2C1B} RELOAD LOBYTE
:LPA
         ASL   ]AIDX      ; {6C3B} MULTIPLY INDEX BY 2
         ROL   SCRATCH    ; {6C3B} ADJUST HIBYTE
:ENTLPA
         LSR   ]ESIZE     ; {6C3B} DIVIDE ELEMENT LENGTH BY 2
         BCS   :DOADD     ; {3C2B} IF BIT1 SHIFTED IN CARRY +MORE
         BNE   :LPA       ; {3C2B} CONTINUE LOOPING IF Z FLAG UNSET
         STX   ]IDX       ; {3C2B} STORE LOBYTE
         STY   ]IDX+1     ; {3C2B} STORE HIBYTE
         LDA   #3         ; {2C2B} ADD 3 TO INDEX LOBYTE
         CLC              ; {2C1B} CLEAR CARRY
         ADC   ]IDX       ; {4C3B}
         STA   ADDR2      ; {3C2B} STORE ON ZERO PAGE
         LDA   ]IDX+1     ; {3C2B} ADJUST HIBYTE
         ADC   #0         ; {2C2B}
         STA   ADDR2+1    ; {3C2B}
*
         LDA   ADDR2      ; {3C2B} ADD ARRAY ADDRESS
         CLC              ; {2C1B}
         ADC   ]ADDR      ; {4C3B} LOBYTE
         STA   ADDR2      ; {3C2B}
         LDA   ADDR2+1    ; {3C2B} HIBYTE
         ADC   ]ADDR+1    ; {4C3B}
         STA   ADDR2+1    ; {3C2B}
         LDY   #0         ; {2C2B} RESET BYTE INDEX
:LP
         LDA   (ADDR2),Y  ; {6C2B} GET BYTE FROM ELEMENT
         STA   RETURN,Y   ; {3C2B} PUT INTO RETURN
         INY              ; {2C1B} INCREASE BYTE INDEX
         CPY   ]ESIZEB    ; {4C3B} IF INDEX != ELEMENT LENGTH
         BNE   :LP        ; {3C2B} CONTINUE LOOP
         LDA   ]ESIZEB    ; {3C2B} .A = ELEMENT SIZE
         STA   RETLEN     ; {3C2B} STORE IN RETLEN
         LDY   ADDR2+1    ; {3C2B} .Y = ELEMENT ADDRESS HIBYTE
         LDX   ADDR2      ; {3C2B} .X = ELEMENT ADDRESS LOBYTE
         RTS              ; {6C1B}


THE PUT161 MACRO

SUMMARY

Condition Value
Name PUT161
Type Macro
File MAC.ARR16B1D.ASM
Author Nathan Riggs
Last Revision 18-MAR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Copy a value to an element in a 16-bit, 1-dimensional array
Input ]1 = Source Address
]2 = Array Address
]3 = Element Index
Output none
Dependencies SUB.APUT161.ASM
Flags Destroyed NZCV
Cycles 238+
Bytes 148
Notes none
See Also DIM161 ADIM161 GET161 AGET161 APUT161

DETAILS

The PUT161 macro copies a value from a memory address and places it in the specified element of the given 16-bit, one-dimensional array. It should be noted that this macro does not accept a literal value to be placed in the array--only the address of a value is accepted. Additionally, there is no error-checking; thus, if the programmer enters an out-of-bounds element index, it is possible that the subroutine will write over data or commands that should not be written over, possibly causing the computer to crash.

After executing, the .A register holds the array's element byte length, as well as the physical memory location of the index in question in .X and .Y (low byte and high byte of address, respectively).

LISTING 3.35: The PUT161 Macro Source

*
*``````````````````````````````*
* PUT161        (NATHAN RIGGS) *
*                              *
* SET THE VALUE OF AN INDEX    *
* ELEMENT IN A 16-BIT, ONE-    *
* DIMENSIONAL ARRAY.           *
*                              *
* PARAMETERS                   *
*                              *
*  ]1 = SOURCE ADDRESS         *
*  ]2 = ARRAY ADDRESS          *
*  ]3 = ELEMENT INDEX          *
*                              *
* CYCLES: 238+                 *
* SIZE: 148 BYTES              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
PUT161   MAC
         _MLIT ]1;WPAR1   ; {16C12B} PARSE SOURCE ADDRESS
         _MLIT ]2;WPAR2   ; {16C12B} PARSE ARRAY ADDRESS
         _MLIT ]3;WPAR3   ; {16C12B} PARSE INDEX
         JSR   APUT161    ; {190C112B}
         <<<
*


THE APUT161 SUBROUTINE

SUMMARY

Condition Value
Name APUT161
Type Subroutine
File SUB.APUT161.ASM
Author Nathan Riggs
Last Revision 18-MAR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Put a value in the specified element of a 16-bit, one-dimensional array
Input WPAR1 = Source Address
WPAR2 = Destination Array Address
WPAR3 = Destination Element Index
Output none
Dependencies none
Flags Destroyed NZCV
Cycles 184+
Bytes 109
Notes None
See Also DIM161 ADIM161 GET161 AGET161``APUT161

DETAILS

The APUT161 subroutine takes the value from a given source address range of bytes and puts that data into a specified element of a 16-bit, one-dimensional array. Afterward, the array's element byte-length is held in the .A register, with the element's physical address passed back via the .X and .Y registers (low byte and high byte, respectively).

LISTING 3.36: The APUT161 Subroutine Source

*
*``````````````````````````````*
* APUT161       (NATHAN RIGGS) *
*                              *
*------------------------------*
* MULTIPLICATION CODE ADAPTED  *
* FROM WHITE FLAME'S WORK ON   *
* CODEBASE64. MICENSE MAY VARY *
*------------------------------*
*                              *
* INPUT:                       *
*                              *
*  WPAR1 = SOURCE ADDRESS      *
*  WPAR2 = ARRAY ADDRESS       *
*  WPAR3 = ELEMENT INDEX       *
*                              *
* DESTROY: NZCIDV              *
*          ^^^  ^              *
*                              *
* CYCLES: 184+                 *
* SIZE: 109 BYTES              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]ADDRS   EQU   WPAR1      ; SOURCE ADDRESS
]ADDRD   EQU   WPAR2      ; ARRAY DESTINATION ADDRESS
]AIDX    EQU   WPAR3      ; ELEMENT INDEX
*
]ESIZE   EQU   VARTAB     ; ELEMENT SIZE
]ESIZEB  EQU   VARTAB+1   ; ^BACKUP
]ASIZE   EQU   VARTAB+2   ; NUMBER OF ELEMENTS
]IDX     EQU   VARTAB+6   ; ANOTHER INDEX
*
APUT161
         LDA   ]AIDX      ; {3C2B}
         STA   ]IDX       ; {3C2B}
         LDA   ]AIDX+1    ; {3C2B}
         STA   ]IDX+1     ; {3C2B}
         STA   SCRATCH    ; {3C2B}
         LDY   #0         ; {2C2B} RESET BYTE COUNTER
         LDA   (]ADDRD),Y ; {6C2B} GET NUMBER OF ELEMENTS
         STA   ]ASIZE     ; {3C2B} LOBYTE
         LDY   #1         ; {2C2B} INCREMENT INDEX
         LDA   (]ADDRD),Y ; {6C2B} GET NUMBER OF ELEMENTS
         STA   ]ASIZE+1   ; {3C2B} HIBYTE
         INY              ; {2C1B} INCREMENT INDEX
         LDA   (]ADDRD),Y ; {6C2B} GET ELEMENT LENGTH
         STA   ]ESIZE     ; {3C2B}
         STA   ]ESIZEB    ; {3C2B} BACKUP
*
** MULTIPLY INDEX BY ELEMENT SIZE, THEN ADD 3
*
         LDY   #0         ; {2C2B} RESET LOBYTE
         LDA   #0         ; {2C2B} AND HIBYTE
         BEQ   :ENTLPA    ; {3C2B} SKIP TO LOOP
:DOADD
         CLC              ; {2C1B} CLEAR CARRY
         ADC   ]AIDX      ; {4C3B} ADD INDEX LOBYTE
         TAX              ; {2C1B} HOLD IN .X
         TYA              ; {2C1B} LOAD HIBYTE
         ADC   SCRATCH    ; {4C3B} ADD HIBYTE
         TAY              ; {2C1B} HOLD BACK IN .Y
         TXA              ; {2C1B} RETURN LOBYTE TO .A
:LPA
         ASL   ]AIDX      ; {6C2B} MULTIPLY INDEX BY 2
         ROL   SCRATCH    ; {6C2B} ADJUST HIBYTE
:ENTLPA
         LSR   ]ESIZE     ; {6C2B} DIVIDE ELEM LENGTH BY 2
         BCS   :DOADD     ; {3C2B} IF 1 SHIFTED TO CARRY, ADD AGAIN
         BNE   :LPA       ; {3C2B} CONTINUE LOOP IF ZERO UNSET
         STX   ]IDX       ; {3C2B} LOBYTE IN .X
         STY   ]IDX+1     ; {3C2B} HIBYTE IN .Y
         CLC              ; {2C1B}
         LDA   #3         ; {2C2B} ADD 3 TO LOBYTE
         ADC   ]IDX       ; {4C3B}
         STA   ADDR2      ; {3C2B} STORE ON ZERO PAGE
         LDA   ]IDX+1     ; {3C2B} ADJUST HIBYTE
         ADC   #0         ; {4C3B}
         STA   ADDR2+1    ; {3C2B}
*
         CLC              ; {2C1B} CLEAR CARRY
         LDA   ADDR2      ; {3C2B} ADD ARRAY ADDRESS
         ADC   ]ADDRD     ; {4C3B} LOBYTE
         STA   ADDR2      ; {3C2B} ADD ARRAY ADDRESS
         LDA   ADDR2+1    ; {3C2B} HIBYTE
         ADC   ]ADDRD+1   ; {4C3B}
         STA   ADDR2+1    ; {3C2B}
         LDY   #0         ; {2C2B}
:LP
*
** OOPS; NEED TO CONVERT THIS TO 16 BITS
*
         LDA   (]ADDRS),Y ; {6C2B} GET BYTE FROM SOURCE
         STA   (ADDR2),Y  ; {3C2B} STORE IN ELEMENT
         INY              ; {2C1B} INCREMENT BYTE INDEX
         CPY   ]ESIZEB    ; {4C3B} IF INDEX != ELEMENT LENGTH
         BNE   :LP        ; {3C2B} KEEP LOOPING
         LDY   ADDR2+1    ; {3C2B} HIBYTE OF ELEMENT ADDRESS
         LDX   ADDR2      ; {3C2B} LOBYTE
         LDA   ]ESIZEB    ; {3C2B} .A = ELEMENT SIZE
         RTS              ; {6C1B}


2-Dimensional, 16-Bit Arrays

These macros and subroutines are dedicated to using arrays with a maximum of 65,025 elements per dimension (in this case, two dimensions), with each element being up to 255 bytes in length. The macros for these arrays are located in the MAC.ARR16B2D.ASM file, named so after the types of macros it includes (16 Bytes, 2 Dimensions).

WARNING: using 16-bit two-dimensional arrays can fill up memory fast. As an example, imagine have an array with 255 elements in the first dimension and 255 elements in the second dimension, with only two bytes per element. To calculate the amount of memory required for this, we multiply the elements on the first dimension by the elements in the second dimension, then multiply that by the number of bytes in each element--two. We then add four more bytes for the array header, and we have the total number that the array will take: 130,054 bytes! This is more memory than any Apple II computer has without special additional hardware (except the IIGS), and the limits of the subroutine are not even close to being met. Special care must be taken to control the size of an array before the array is even declared.

LISTING 3.40: MAC.ARR16B2D.ASM Header Source

*
*``````````````````````````````*
* MAC.ARR16B2D.ASM             *
*                              *
* A MACRO LIBRARY FOR 16-BIT,  *
* 2-DIMENSIONAL ARRAYS.        *
*                              *
* AUTHOR:    NATHAN RIGGS      *
* CONTACT:   NATHAN.RIGGS@     *
*            OUTLOOK.COM       *
*                              *
* DATE:      18-MAR-2021       *
* ASSEMBLER: MERLIN 8 PRO      *
* OS:        DOS 3.3           *
*                              *
* SUBROUTINE FILES USED        *
*                              *
*  SUB.ADIM162                 *
*  SUB.AGET162                 *
*  SUB.APUT162                 *
*                              *
* LIST OF MACROS               *
*                              *
* DIM162: DIM 2D, 16BIT ARRAY  *
* GET162: GET ELEMENT FROM 2D, *
*         16BIT ARRAY.         *
* PUT162: PUT VALUE INTO A 2D, *
*         16BIT ARRAY INDEX.   *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*


THE ADIM162 MACRO

SUMMARY

Condition Value
Name DIM162
Type Macro
File MAC.ARR16B2D.ASM
Author Nathan Riggs
Last Revision 18-MAR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Initialize a 16-bit, two-dimensional array
Input ]1 = Array Address
]2 = First Dimension Element Count
]3 = Second Dimension Element Count
]4 = Byte size of Elements
]5 = Default Fill Value
Output none
Dependencies SUB.APUT162.ASM
Flags Destroyed NZCV
Cycles 464+
Bytes 373
Notes none
See Also ADIM162 GET162 AGET162 PUT162 APUT162

DETAILS

The DIM162 macro initializes a 16-bit, two-dimensional array at the given memory address. As indicated by its 16-bit description, this type of array can hold up to 65,025 elements per dimension, with each element having a length of between one and 255. Such arrays are typically used for things like mapping coordinates on a two-dimensional plane.

Whenever possible, the 8-bit version of two-dimensional arrays should be used instead of the 16-bit version due to the extra number of cycles needed to accommodate 16-bits. When arrays are used, they tend to be used often in a program, since they are such a staple "low level" data type; this means that even a few cycles here and there can add up quickly, slowing program execution. Additionally, as always, care should be taken with memory management when using arrays in general: they quickly gobble of space faster than a user may realize, and planning needs to be done before declaring and initializing the array.

LISTING 3.41: The DIM162 Macro Source

*
*``````````````````````````````*
* DIM162        (NATHAN RIGGS) *
*                              *
* INITIALIZE A 16-BIT, TWO-    *
* DIMENSIONAL ARRAY.           *
*                              *
* PARAMETERS                   *
*                              *
*  ]1 = ARRAY ADDRESS          *
*  ]2 = X DIMENSION            *
*  ]3 = Y DIMENSION            *
*  ]4 = ELEMENT SIZE           *
*  ]5 = FILL VALUE             *
*                              *
* CYCLES: 464+                 *
* SIZE: 373 BYTES              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
DIM162   MAC
         _MLIT ]1;WPAR3   ; {16C12B} PARSE ARRAY ADDRESS
         _MLIT ]2;WPAR1   ; {16C12B} PARSE X DIMENSION
         _MLIT ]3;WPAR2   ; {16C12B} PARSE Y DIMENSION
         LDA   ]4         ; {3C2B} ELEMENT LENGTH
         STA   BPAR1      ; {3C2B}
         LDA   ]5         ; {3C2B} FILL VAL
         STA   BPAR2      ; {3C2B}
         JSR   ADIM162    ; {404C229B}
         <<<
*


THE ADIM162 SUBROUTINE

SUMMARY

Condition Value
Name ADIM162
Type Subroutine
File SUB.ADIM162.ASM
Author Nathan Riggs
Last Revision 18-MAR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Initialize a two-dimensional, 16-bit array
Input WPAR1 = First Dimension Length
WPAR2 = Second Dimension Length
WPAR3 = Array Address
BPAR1 = Element Byte Length
BPAR2 = Default Fill Value
Output none
Dependencies none
Flags Destroyed NZCV
Cycles 398+
Bytes 226
Notes None
See Also DIM162 GET162 AGET162 PUT162 APUT162

DETAILS

The ADIM162 subroutine declares and initializes a 16-bit, two-dimensional array. This is a fairly simple data type, though it is also usually a fundamental data type as well. At the starting two bytes of the array, the length of the first dimension is stored, with the lowest byte of the address coming first (big-endian); then the next two bytes are dedicated to the length of the second dimension. Finally, the fifth byte holds the length of each element in the array, and every byte afterwards holds the actual data placed in each element of the array.

After executing, ADIM162 holds the total array size in bytes in RETURN, with the length of the value in RETLEN.

LISTING 3.42: The ADIM162 Subroutine Source

*
*``````````````````````````````*
* ADIM162       (NATHAN RIGGS) *
*                              *
* INITIALIZE A 1-DIMENSIONAL,  *
* 16-BIT ARRAY.                *
*                              *
*------------------------------*
* MULTIPLICATION ADAPTED FROM  *
* WHITE FLAME'S WORK ON        *
* CODEBASE64. LICENSE MAY VARY *
*------------------------------*
*                              *
* INPUT:                       *
*                              *
*  WPAR1 = 1ST DIM LENGTH      *
*  WPAR2 = 2ND DIM LENGTH      *
*  WPAR3 = ARRAY ADDRESS       *
*  BPAR1 = ELEMENT LENGTH      *
*  BPAR2 = FILL VALUE          *
*                              *
* DESTROY: NZCIDV              *
*          ^^^  ^              *
*                              *
* CYCLES: 398+                 *
* SIZE: 226 BYTES              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]AXSIZE  EQU   WPAR1      ; FIRST DIMENSION LENGTH
]AYSIZE  EQU   WPAR2      ; SECOND DIMENSION LENGTH
]ELEN    EQU   BPAR1      ; ELEMENT BYTE LENGTH
]FILL    EQU   BPAR2      ; FILL VALUE
]ADDR    EQU   WPAR3      ; ARRAY ADDRESS
]ADDR2   EQU   ADDR1      ; ZERO-PAGE WORKING SPACE
*
]PROD    EQU   VARTAB     ; PRODUCT
]AXBAK   EQU   VARTAB+4   ; X SIZE BACKUP
]AYBAK   EQU   VARTAB+6   ; Y SIZE BACKUP
]MLIER   EQU   VARTAB+8   ; MULTIPLIER
]MCAND   EQU   VARTAB+10  ; MULTIPLICAND
*
ADIM162
         LDA   ]AYSIZE    ; {3C2B}
         STA   ]AYBAK     ; {3C2B}
         STA   ]MCAND     ; {3C2B}
         LDA   ]AYSIZE+1  ; {3C2B}
         STA   ]AYBAK+1   ; {3C2B}
         STA   ]MCAND+1   ; {3C2B}
         LDA   ]AXSIZE    ; {3C2B}
         STA   ]AXBAK     ; {3C2B}
         STA   ]MLIER     ; {3C2B}
         LDA   ]AXSIZE+1  ; {3C2B}
         STA   ]AXBAK+1   ; {3C2B}
         STA   ]MLIER+1   ; {3C2B}
         LDA   ]ADDR      ; {3C2B} GET ARRAY ADDRESS
         STA   ]ADDR2     ; {3C2B} LOBYTE; PUT IN ZERO PAGE
         LDA   ]ADDR+1    ; {3C2B} GET ARRAY ADDRESS HIBYTE
         STA   ]ADDR2+1   ; {3C2B}
*
** MULTIPLY X AND Y
*
         LDA   #0         ; {2C2B} RESET HIBYTE,LOBYTE
         STA   ]PROD+2    ; {3C2B} CLEAR PRODUCT BYTE 3
         STA   ]PROD+3    ; {3C2B} CLEAR PRODUCT BYTE 4
         LDX   #$10       ; {2C2B} (#16)
:SHIFT_R
         LSR   ]MLIER+1   ; {6C2B} DIVIDE MLIER BY TWO
         ROR   ]MLIER     ; {6C2B} ADJUST LOBYTE
         BCC   :ROT_R     ; {3C2B} IF 0 IN CARRY, ROTATE MORE
         LDA   ]PROD+2    ; {3C2B} GET 3RD BYTE OF PRODUCT
         CLC              ; {2C1B}
         ADC   ]MCAND     ; {4C3B} ADD MULTIPLICAND
         STA   ]PROD+2    ; {3C2B} STORE 3RD BYTE
         LDA   ]PROD+3    ; {3C2B} LOAD 4TH BYTE
         ADC   ]MCAND+1   ; {4C3B} ADD MULTIPLICAND HIBYTE
:ROT_R
         ROR              ; {6C2B} ROTATE PARTIAL PRODUCT
         STA   ]PROD+3    ; {3C2B} STORE IN HIBYTE
         ROR   ]PROD+2    ; {6C2B} ROTATE THIRD BYTE
         ROR   ]PROD+1    ; {6C2B} ROTATE 2ND BYTE
         ROR   ]PROD      ; {6C2B} ROTATE LOBYTE
         DEX              ; {2C1B} DECREASE COUNTER
         BNE   :SHIFT_R   ; {3C2B} IF NOT ZERO, BACK TO SHIFTER
*
         LDA   ]ELEN      ; {3C2B} PUT ELEMENT LENGTH
         STA   ]MCAND     ; {3C2B} INTO MULTIPLICAND
         LDA   #0         ; {2C2B} CLEAR HIBYTE
         STA   ]MCAND+1   ; {3C2B}
         LDA   ]PROD      ; {3C2B} LOAD EARLIER PRODUCT
         STA   ]MLIER     ; {3C2B} STORE LOBYTE IN MULTIPLIER
         LDA   ]PROD+1    ; {3C2B} DO SAME FOR HIBYTE
         STA   ]MLIER+1   ; {3C2B}
*
** NOW MULTIPLY BY LENGTH OF ELEMENTS
*
         LDA   #0         ; {2C2B} CLEAR PRODUCT
         STA   ]PROD      ; {3C2B}
         STA   ]PROD+1    ; {3C2B}
         STA   ]PROD+2    ; {3C2B}
         STA   ]PROD+3    ; {3C2B}
         LDX   #$10       ; {2C2B}
:SHIFTR  LSR   ]MLIER+1   ; {6C2B} SHIFT BYTES LEFT (/2)
         ROR   ]MLIER     ; {6C2B} ADJUST LOBYTE
         BCC   :ROTR      ; {3C2B} IF CARRY = 0, ROTATE
         LDA   ]PROD+2    ; {3C2B} LOAD 3RD BYTE OF PRODUCT
         CLC              ; {2C1B}
         ADC   ]MCAND     ; {4C3B} ADD MULTIPLICAND
         STA   ]PROD+2    ; {3C2B} STORE IN 3RD BYTE
         LDA   ]PROD+3    ; {3C2B} LOAD HIBYTE
         ADC   ]MCAND+1   ; {4C3B} ADD MULTIPLICAND HIBYTE
:ROTR
         ROR              ; {6C2B} ROTATE .A RIGHT
         STA   ]PROD+3    ; {3C2B} ROTATE 4TH
         ROR   ]PROD+2    ; {6C2B} ROTATE 3RD
         ROR   ]PROD+1    ; {6C2B} ROTATE 2ND
         ROR   ]PROD      ; {6C2B} ROTATE LOBYTE
         DEX              ; {2C1B} DECREMENT COUNTER
         BNE   :SHIFTR    ; {3C2B}IF NOT 0, BACK TO SHIFTER
*
         CLC              ; {2C1B} CLEAR CARRY
         LDA   ]PROD      ; {3C2B} INCREASE BY 5
         ADC   #5         ; {2C2B}
         STA   ]PROD      ; {3C2B} SAVE LOBYTE
         LDA   ]PROD+1    ; {3C2B}
         ADC   #0         ; {2C2B}
         STA   ]PROD+1    ; {3C2B} SAVE HIBYTE
*
** NOW CLEAR MEMORY BLOCKS, WHOLE PAGES FIRST
*
         LDA   ]FILL      ; {3C2B} GET FILL VALUE
         LDX   ]PROD+1    ; {3C2B} LOAD PRODUCT 2ND BYTE
         BEQ   :PART      ; {3C2B} IF 0, THEN PARTIAL PAGE
         LDY   #0         ; {2C2B} CLEAR INDEX
:FULL
         STA   (]ADDR),Y  ; {6C2B} COPY FILL BYTE TO ADDRESS
         INY              ; {2C1B} INCREASE BYTE COUNTER
         BNE   :FULL      ; {3C2B} LOOP UNTIL PAGES DONE
         INC   ]ADDR+1    ; {6C2B} INCREASE HIBYTE
         DEX              ; {2C1B} DECREASE COUNTER
         BNE   :FULL      ; {3C2B} LOOP UNTIL PAGES DONE
*
** NOW DO REMAINING BYTES
*
:PART
         LDX   ]PROD      ; {3C2B} LOAD PRODUCT LOBYTE IN X
         BEQ   :MFEXIT    ; {3C2B} IF 0, THEN WE'RE DONE
         LDY   #0         ; {2C2B} CLEAR BYTE INDEX
:PARTLP
         STA   (]ADDR),Y  ; {6C2B} STORE FILL BYTE
         INY              ; {2C1B} INCREASE BYTE INDEX
         DEX              ; {2C1B} DECREASE COUNTER
         BNE   :PARTLP    ; {3C2B} LOOP UNTIL DONE
:MFEXIT
         LDY   #0         ; {2C2B} CLEAR BYTE INDEX
         LDA   ]AXBAK     ; {3C2B} LOAD ORIGINAL X LENGTH
         STA   (]ADDR2),Y ; {6C2B} STORE IN ARRAY HEADER
         INY              ; {2C1B} INCREASE BYTE COUNTER
         LDA   ]AXBAK+1   ; {3C2B} STORE HIBYTE
         STA   (]ADDR2),Y ; {6C2B}
         INY              ; {2C1B} INCREASE BYTE INDEX
         LDA   ]AYBAK     ; {3C2B} LOAD Y LENGTH LOBYTE
         STA   (]ADDR2),Y ; {6C2B} STORE IN ARRAY HEADER
         INY              ; {2C1B} INCREMENT BYTE INDEX
         LDA   ]AYBAK+1   ; {3C2B} STORE Y HIBYTE
         STA   (]ADDR2),Y ; {6C2B}
         INY              ; {2C1B} INCREMENT BYTE INDEX
         LDA   ]ELEN      ; {3C2B} STORE ELEMENT LENGTH
         STA   (]ADDR2),Y ; {6C2B}
*
         LDY   ]ADDR2     ; {3C2B} LOBYTE OF ARRAY ADDRESS
         LDX   ]ADDR2+1   ; {3C2B} ARRAY ADDRESS HIBYTE
         LDA   ]PROD      ; {3C2B} STORE TOTAL ARRAY SIZE
         STA   RETURN     ; {3C2B} IN BYTES IN RETURN
         LDA   ]PROD+1    ; {3C2B}
         STA   RETURN+1   ; {3C2B}
         LDA   ]PROD+2    ; {3C2B}
         STA   RETURN+2   ; {3C2B}
         LDA   ]PROD+3    ; {3C2B}
         STA   RETURN+3   ; {3C2B}
         LDA   #4         ; {2C2B} SIZE OF RETURN
         STA   RETLEN     ; {3C2B}
         RTS              ; {6C1B}


THE AGET162 MACRO

SUMMARY

Condition Value
Name GET162
Type Macro
File MAC.ARR16B2D.ASM
Author Nathan Riggs
Last Revision 18-MAR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Retrieve a value held in an element of a 16-bit, 2-dimensional array
Input ]1 = Array Address
]2 = First Dimension Element Address
]3 = Second Dimension Element Address
Output none
Dependencies SUB.AGET162.ASM
Flags Destroyed NZCV
Cycles 436+
Bytes 263
Notes none
See Also DIM162 ADIM162 AGET162 PUT162 APUT162

DETAILS

The GET162 macro retrieves a value from a specified element in a 16-bit, two-dimensional array and puts it in RETURN, with the element value length held in RETLEN. Note that if an out-of-bounds element index is given for either the first or second dimension, the value returned will be trash; there is no error-handling built into arrays, and thus it must be handled by the programmer.

LISTING 3.43: The GET162 Macro Source

*
*``````````````````````````````*
* GET162        (NATHAN RIGGS) *
*                              *
* GET THE VALUE STORED AT AN   *
* ELEMENT OF A 16-BIT, TWO-    *
* DIMENSIONAL ARRAY.           *
*                              *
* PARAMETERS                   *
*                              *
*  ]1 = ARRAY ADDRESS          *
*  ]2 = ELEMENT X INDEX        *
*  ]3 = Y INDEX                *
*                              *
* CYCLES: 436+                 *
* SIZE: 263 BYTES              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
GET162   MAC
         _MLIT ]1;WPAR1   ; {16C12B} PARSE ARAY ADDRESS
         _MLIT ]2;WPAR2   ; {16C12B} PARSE X INDEX
         _MLIT ]3;WPAR3   ; {16C12B} PARSE Y INDEX
         JSR   AGET162    ; {388C227B}
         <<<
*


THE AGET162 SUBROUTINE

SUMMARY

Condition Value
Name AGET162
Type Subroutine
File SUB.AGET162.ASM
Author Nathan Riggs
Last Revision 18-MAR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Retrieve an indexed value in a two-dimensional, 16-bit array
Input WPAR1 = Array Address
WPAR2 = First Dimension Index
WPAR3 = Second Dimension Index
Output none
Dependencies none
Flags Destroyed NZCV
Cycles 382+
Bytes 224
Notes None
See Also

DETAILS

The AGET162 subroutine retrieves the value stored in an element of a two-dimensional, 16-bit array, holding the value in RETURN with its length in RETLEN. Additionally, the value length is held in the .A register, and the starting address of the element being read is held in the .X register (low byte of address) and the .Y register (high byte of address).

Parameters are passed to AGET162 via the zero page to save cycles.

LISTING 3.44: The AGET162 Subroutine Source

*
*``````````````````````````````*
* AGET162       (NATHAN RIGGS) *
*                              *
* GET A VALUE FROM AN ELEMENT  *
* IN A 2-DIMENSIONAL, 16-BIT   *
* ARRAY.                       *
*                              *
*------------------------------*
* MULTIPLICATION CODE ADAPTED  *
* FROM WHITE FLAME'S WORK ON   *
* CODEBASE64. LICENSE MAY VARY *
*------------------------------*
*                              *
* INPUT:                       *
*                              *
*  WPAR1 = ARRAY ADDRESS       *
*  WPAR2 = 1ST DIM INDEX       *
*  WPAR3 = 2ND DIM INDEX       *
*                              *
* DESTROY: NZCIDV              *
*          ^^^  ^              *
*                              *
* CYCLES: 382+                 *
* SIZE: 224 BYTES              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]ADDR    EQU   WPAR1      ; ARRAY ADDRESS
]XIDX    EQU   WPAR2      ; FIRST DIMENSION LENGTH
]YIDX    EQU   WPAR3      ; SECOND DIMENSION LENGTH
*
]ESIZE   EQU   VARTAB     ; ELEMENT LENGTH
]MCAND   EQU   VARTAB+2   ; MULTIPLICAND
]MLIER   EQU   VARTAB+4   ; MULTIPLIER
]PROD    EQU   VARTAB+6   ; PRODUCT
]PBAK    EQU   VARTAB+10  ; ^BACKUP
]XLEN    EQU   VARTAB+12  ; X-DIM LENGTH
]YLEN    EQU   VARTAB+14  ; Y-DIM LENGTH
*
AGET162
         LDY   #4         ; {2C2B} READ BYTE 4 FROM HEADER
         LDA   (]ADDR),Y  ; {6C2B} TO GET ELEMENT SIZE
         STA   ]ESIZE     ; {3C2B}
         LDY   #0         ; {2C2B} READ BYTE 0 FROM HEADER
         LDA   (]ADDR),Y  ; {6C2B} TO GET X-DIM LENGTH LOBYTE
         STA   ]XLEN      ; {3C2B}
         LDY   #1         ; {2C2B} READ BYTE 1 FROM HEADER
         LDA   (]ADDR),Y  ; {6C2B} TO GET X-DIM LENGTH HIBYTE
         STA   ]XLEN+1    ; {3C2B}
         LDY   #2         ; {2C2B} READ BYTE 2 FROM HEADER
         LDA   (]ADDR),Y  ; {6C2B} TO GET Y-DIM LENGTH LOBYTE
         STA   ]YLEN      ; {3C2B}
         LDY   #3         ; {2C2B} READ BYTE 3 OF HEADER
         LDA   (]ADDR),Y  ; {6C2B} TO GET Y-DIM LENGTH HIBYTE
         STA   ]YLEN+1    ; {3C2B}
         LDY   #0         ; {2C2B} RESET BYTE INDEX
*
** MULTIPLY Y-INDEX BY Y-LENGTH
*
         LDA   ]YIDX      ; {3C2B} PUT Y-INDEX INTO
         STA   ]MLIER     ; {3C2B} MULTIPLIER
         LDA   ]YIDX+1    ; {3C2B} ALSO HIBYTE
         STA   ]MLIER+1   ; {3C2B}
         LDA   ]YLEN      ; {3C2B} PUT Y-DIM LENGTH LOBYTE
         STA   ]MCAND     ; {3C2B} INTO MULTIPLICAND
         LDA   ]YLEN+1    ; {3C2B} ALSO HIBYTE
         STA   ]MCAND+1   ; {3C2B}
         LDA   #00        ; {2C2B} RESET
         STA   ]PROD      ; {3C2B} PRODUCT BYTES
         STA   ]PROD+1    ; {3C2B}
         STA   ]PROD+2    ; {3C2B}
         STA   ]PROD+3    ; {3C2B}
         LDX   #$10       ; {2C2B} LOAD #16 INTO X REGISTER
:SHIFT_R
         LSR   ]MLIER+1   ; {6C2B} DIVIDE MULTIPLIER BY 2
         ROR   ]MLIER     ; {6C2B} ADJUST HIBYTE
         BCC   :ROT_R     ; {3C2B} IF 0 PUT IN CARRY, ROTATE MORE
         LDA   ]PROD+2    ; {3C2B} LOAD PRODUCT 3RD BYTE
         CLC              ; {2C1B} CLEAR CARRY
         ADC   ]MCAND     ; {4C3B} ADD MULTIPLICAND
         STA   ]PROD+2    ; {3C2B} STORE IN PRODUCT 3RD
         LDA   ]PROD+3    ; {3C2B} LOAD PRODUCT HIBYTE
         ADC   ]MCAND+1   ; {4C3B} ADD MULTIPLICAN HIBYTE
:ROT_R
         ROR              ; {6C2B} ROTATE .A RIGHT
         STA   ]PROD+3    ; {3C2B} STORE IN PRODUCT HIBYTE
         ROR   ]PROD+2    ; {6C2B} ROTATE 3RD BYTE
         ROR   ]PROD+1    ; {6C2B} ROTATE 2ND BYTE
         ROR   ]PROD      ; {6C2B} ROTATE LOBYTE
         DEX              ; {2C1B} DECREASE X COUNTER
         BNE   :SHIFT_R   ; {3C2B} IF NOT ZERO, SHIFT AGAIN
*
** NOW MULTIPLY XIDX BY ELEMENT SIZE
*
         LDA   ]PROD      ; {3C2B} BACKUP PREVIOUS PRODUCT
         STA   ]PBAK      ; {3C2B} 1ST AND 2ND BYTES; THE
         LDA   ]PROD+1    ; {3C2B} 3RD AND 4TH ARE NOT USED
         STA   ]PBAK+1    ; {3C2B}
         LDA   ]XIDX      ; {3C2B} LOAD X-INDEX LOBYTE
         STA   ]MLIER     ; {3C2B} AND STORE IN MULTIPLIER
         LDA   ]XIDX+1    ; {3C2B} LOAD HIBYTE AND STORE
         STA   ]MLIER+1   ; {3C2B}
         LDA   ]ESIZE     ; {3C2B} LOAD ELEMENT SIZE AND
         STA   ]MCAND     ; {3C2B} STORE LOBYTE IN MULTIPLICAND
         LDA   #0         ; {2C2B} CLEAR MULTIPLICAND HIBYTE
         STA   ]MCAND+1   ; {3C2B}
*
         STA   ]PROD      ; {3C2B} CLEAR ALL PRODUCT BYTES
         STA   ]PROD+1    ; {3C2B}
         STA   ]PROD+2    ; {3C2B}
         STA   ]PROD+3;   ; {3C2B}
         LDX   #$10       ; {2C2B} LOAD #16 IN COUNTER
:SHIFTR  LSR   ]MLIER+1   ; {6C2B} DIVIDE MULTIPLIER HIBYTE BY 2
         ROR   ]MLIER     ; {6C2B} ADJUST LOBYTE
         BCC   :ROTR      ; {3C2B} IF 0 PUT IN CARRY, ROTATE
         LDA   ]PROD+2    ; {3C2B} LOAD PRODUCT 3RD BYTE
         CLC              ; {2C1B} CLEAR CARRY
         ADC   ]MCAND     ; {4C3B} ADD MULTIPLICAND LOBYTE
         STA   ]PROD+2    ; {3C2B} STORE PRODUCT 3RD BYTE
         LDA   ]PROD+3    ; {3C2B} LOAD PRODUCT HIBYTE
         ADC   ]MCAND+1   ; {4C3B} ADD MULTIPLICAND HIBYTE
:ROTR
         ROR              ; {6C2B} ROTATE .A RIGHT
         STA   ]PROD+3    ; {3C2B} STORE IN PRODUCT HIBYTE
         ROR   ]PROD+2    ; {6C2B} ROTATE PRODUCT 3RD BYTE
         ROR   ]PROD+1    ; {6C2B} ROTATE 2ND BYTE
         ROR   ]PROD      ; {6C2B} ROTATE LOBYTE
         DEX              ; {2C1B} DECREMENT X COUNTER
         BNE   :SHIFTR    ; {3C2B} IF != 0, SHIFT AGAIN
*
** NOW ADD X * ESIZE TO RUNNING PRODUCT
*
         CLC              ; {2C1B} CLEAR CARRY
         LDA   ]PROD      ; {3C2B} ADD PREVIOUS PRODUCT
         ADC   ]PBAK      ; {4C3B} LOBYTE TO CURRENT
         STA   ]PROD      ; {3C2B} AND STORE IN PRODUCT
         LDA   ]PROD+1    ; {3C2B} DO THE SAME WITH HIBYTES
         ADC   ]PBAK+1    ; {4C3B}
         STA   ]PROD+1    ; {3C2B}
         CLC              ; {2C1B} CLEAR CARRY
         LDA   ]PROD      ; {3C2B} ADD 5 BYTES TO PRODUCT
         ADC   #5         ; {4C3B} TO ACCOUNT FOR ARRAY HEADER
         STA   ]PROD      ; {3C2B}
         LDA   ]PROD+1    ; {3C2B}
         ADC   #0         ; {2C2B} ADJUST HIBYTE
         STA   ]PROD+1    ; {3C2B}
*
** NOW ADD BASE ADDRESS OF ARRAY TO GET
** THE ADDRESS OF THE INDEX VALUE
*
         CLC              ; {2C1B} CLEAR CARRY
         LDA   ]PROD      ; {3C2B} ADD PRODUCT TO ARRAY
         ADC   ]ADDR      ; {4C3B} ADDRESS, LOBYTES
         STA   ADDR2      ; {3C2B} STORE IN ZERO PAGE
         LDA   ]PROD+1    ; {3C2B} DO THE SAME WITH HIBYTES
         ADC   ]ADDR+1    ; {4C3B}
         STA   ADDR2+1    ; {3C2B}
         LDY   #0         ; {2C2B} RESET BYTE INDEX
*
** COPY FROM SRC ADDR TO DEST ADDR
*
:CLP
         LDA   (ADDR2),Y  ; {6C2B} LOAD BYTE FROM ELEMENT
         STA   RETURN,Y   ; {5C3B} AND STORE IN RETURN
         INY              ; {2C1B} INCREMENT BYTE COUNTER
         CPY   ]ESIZE     ; {4C3B} IF != ELEMENT LENGTH,
         BNE   :CLP       ; {3C2B} CONTINUE LOOPING
         LDA   ]ESIZE     ; {3C2B} .A = ELEMENT SIZE
         STA   RETLEN     ; {3C2B} ALSO IN RETLEN
         LDY   ADDR2+1    ; {3C2B} .Y = ELEMENT ADDRESS HIBYTE
         LDX   ADDR2      ; {3C2B} .X = ELEMENT ADDRESS LOBYTE
         RTS              ; {6C1B}


THE PUT162 MACRO

SUMMARY

Condition Value
Name PUT162
Type Macro
File MAC.ARR16B2D.ASM
Author Nathan Riggs
Last Revision 18-MAR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Place a value in an element of a 16-bit, 2-dimensional array
Input ]1 = Source Address
]2 = Destination Array Address
]3 = First Dimension Element Address
]4 = Second Dimension Element Address
Output none
Dependencies SUB.APUT162.ASM
Flags Destroyed NZCV
Cycles 352+
Bytes 223
Notes none
See Also DIM162 ADIM162 GET162 AGET162 APUT162

DETAILS

The PUT162 macro uses the APUT162 subroutine to place a value in an element of a 16-bit, two-dimensional array. Like other array PUT macros and subroutines, APUT162 only accepts an address that either 1) points to the address of the value to be written, or 2) is the address of the value to be written. This is determined by either sending a literal value as the address (preceded by a # sign), which indicates that the value is held at the given address, or simply by passing the address as it is, which indicates it is an indirect reference. This is how addresses are treated throughout the library as a whole; the difference is that one might expect to send a value in itself as a parameter, when this is simply not the case. In the future, extra functionality may be added to allow for direct and literal placement of a value as a parameter, but this is not currently a pressing issue.

LISTING 3.45: The PUT162 Macro Source

*
*``````````````````````````````*
* PUT82         (NATHAN RIGGS) *
*                              *
* SET VALUE OF AN ELEMENT IN   *
* AN 8-BIT, TWO-DIMENSIONAL    *
* ARRAY.                       *
*                              *
* PARAMETERS                   *
*                              *
*  ]1 = SOURCE ADDRESS         *
*  ]2 = DEST ARRAY ADDRESS     *
*  ]3 = ELEMENT X INDEX        *
*  ]4 = Y INDEX                *
*                              *
* CYCLES: 352+                 *
* SIZE: 223 BYTES              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
PUT82    MAC
         _MLIT ]1;WPAR1   ; {16C12B} PARSE SOURCE ADDRESS
         _MLIT ]2;WPAR2   ; {16C12B} PARSE DEST ADDRESS
         LDA   ]3         ; {3C2B} X INDEX
         STA   BPAR1      ; {3C2B}
         LDA   ]4         ; {3C2B} Y INDEX
         STA   BPAR2      ; {3C2B}
         JSR   APUT82     ; {308C191B}
         <<<
*

THE APUT162 SUBROUTINE

SUMMARY

Condition Value
Name APUT162
Type Subroutine
File SUB.APUT162.ASM
Author Nathan Riggs
Last Revision 18-MAR-2021
Assembler Merlin Pro 8
OS Apple DOS 3.3
Purpose Initialize a two-dimensional, 16-bit array
Input WPAR1 = Source Address
WPAR2 = Array Address
WPA32 = First Dimension Index
ADDR1 = Second Dimension Index
Output none
Dependencies none
Flags Destroyed NZCV
Cycles 378+
Bytes 220
Notes None
See Also DIM162 ADIM162 GET162 AGET162 PUT162

DETAILS

The APUT162 subroutine places a value found at one address into the location of an element in a 16-bit, two-dimensional array. The length to be copied is determined by the array's element length. After executing, the .A register holds the length of the value, and the .X and .Y registers hold the starting address of the element in question (.X holds the low byte of the address, while .Y holds the high byte). Note that providing an out-of-bounds value for either dimensional index will result in overwriting either data or code that should not be written to, and this will likely crash the system. As such, care should be taken by the programmer to keep all array requests within their given boundaries.

LISTING 3.46: The APUT182 Subroutine Source

*
*``````````````````````````````*
* APUT162       (NATHAN RIGGS) *
*                              *
* PLACE A VALUE HELD IN ONE    *
* ADDRESS INTO THE SPECIFIED   *
* ELEMENT IN A 16-BIT, TWO-    *
* DIMENSIONAL ARRAY.           *
*                              *
*------------------------------*
* MULTIPLICATION ADAPTED FROM  *
* WHITE FLAME'S WORK ON        *
* CODEBASE64. LICENSE MAY VARY *
*------------------------------*
*                              *
* INPUT:                       *
*                              *
*  WPAR1 = SOURCE ADDRESS      *
*  WPAR2 = ARRAY ADDRESS       *
*  WPAR3 = 1ST DIM INDEX       *
*  ADDR1 = 2ND DIM INDEX       *
*                              *
* DESTROY: NZCIDV              *
*          ^^^  ^              *
*                              *
* CYCLES: 378+                 *
* SIZE: 220 BYTES              *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]ADDRS   EQU   WPAR1      ; VALUE SOURCE ADDRESS
]ADDRD   EQU   WPAR2      ; ARRAY ADDRESS
]XIDX    EQU   WPAR3      ; FIRST DIMENSION INDEX
]YIDX    EQU   ADDR1      ; SECOND DIMENSION INDEX
*
]ESIZE   EQU   VARTAB     ; ELEMENT LENGTH
]MCAND   EQU   VARTAB+6   ; MULTIPLICAND
]MLIER   EQU   VARTAB+8   ; MULTIPLIER
]PBAK    EQU   VARTAB+10  ; PRODUCT BACKUP
]XLEN    EQU   VARTAB+12  ; X-DIMENSION LENGTH
]YLEN    EQU   VARTAB+14  ; Y-DIMENSION LENGTH
]PROD    EQU   VARTAB+16  ; PRODUCT OF MULTIPLICATION
*
APUT162
         LDY   #4         ; {2C2B} LOAD BYTE 4 OF ARRAY
         LDA   (]ADDRD),Y ; {6C2B} HEADER TO GET ELEM LENGTH
         STA   ]ESIZE     ; {3C2B}
         LDY   #0         ; {2C2B} LOAD BYTE 0 TO GET
         LDA   (]ADDRD),Y ; {6C2B} X-DIMENSION LENGTH LOBYTE
         STA   ]XLEN      ; {3C2B}
         LDY   #1         ; {2C2B} LOAD BYTE 1 TO GET
         LDA   (]ADDRD),Y ; {6C2B} X-DIMENSION LENGTH HIBYTE
         STA   ]XLEN+1    ; {3C2B}
         LDY   #2         ; {2C2B} LOAD BYTE 2 TO GET THE
         LDA   (]ADDRD),Y ; {6C2B} Y-DIMENSION LENGTH LOBYTE
         STA   ]YLEN      ; {3C2B}
         LDY   #3         ; {2C2B} LOAD BYTE 3 TO GET THE
         LDA   (]ADDRD),Y ; {6C2B} Y-DIMENSION LENGTH HIBYTE
         STA   ]YLEN+1    ; {3C2B}
         LDY   #0         ; {2C2B} RESET BYTE INDEX
*
** MULTIPLY Y-INDEX BY Y-LENGTH
*
         LDA   ]YIDX      ; {3C2B} LOAD Y-INDEX LOBYTE
         STA   ]MLIER     ; {3C2B} PUT IN MULTIPLIER LOBYTE
         LDA   ]YIDX+1    ; {3C2B} DO SAME FOR HIBYTES
         STA   ]MLIER+1   ; {3C2B}
         LDA   ]YLEN      ; {3C2B} PUT Y-DIM LENGTH LOBYTE
         STA   ]MCAND     ; {3C2B} INTO MULTIPLICAND
         LDA   ]YLEN+1    ; {3C2B} DO SAME FOR HIBYTE
         STA   ]MCAND+1   ; {3C2B}
         LDA   #00        ; {2C2B} CLEAR PRODUCT BYTES
         STA   ]PROD      ; {3C2B}
         STA   ]PROD+1    ; {3C2B}
         STA   ]PROD+2    ; {3C2B}
         STA   ]PROD+3    ; {3C2B}
         LDX   #$10       ; {2C2B} INIT COUNTER TO #16
:SHIFT_R
         LSR   ]MLIER+1   ; {6C2B} DIVIDE MULTIPLIER HIBYTE BY 2
         ROR   ]MLIER     ; {6C2B} ADJUST LOBYTE
         BCC   :ROT_R     ; {3C2B} IF 0 PUT IN CARRY, ROTATE PRODUCT
         LDA   ]PROD+2    ; {3C2B} LOAD PRODUCT 3RD BYTE
         CLC              ; {2C1B} CLEAR CARRY
         ADC   ]MCAND     ; {4C3B} ADD MULTIPLICAND
         STA   ]PROD+2    ; {3C2B} STORE 3RD BYTE
         LDA   ]PROD+3    ; {3C2B} LOAD PRODUCT HIBYTE
         ADC   ]MCAND+1   ; {4C3B} ADD MULTIPLICAND HIBYTE
:ROT_R
         ROR              ; {6C2B} ROTATE .A RIGHT
         STA   ]PROD+3    ; {3C2B} STORE IN PRODUCT HIBYTE
         ROR   ]PROD+2    ; {6C2B} ROTATE 3RD BYTE
         ROR   ]PROD+1    ; {6C2B} ROTATE 2ND
         ROR   ]PROD      ; {6C2B} ROTATE LOBYTE
         DEX              ; {2C1B} DECREASE X COUNTER
         BNE   :SHIFT_R   ; {3C2B} IF NOT ZERO, LOOP AGAIN
*
** NOW MULTIPLY XIDX BY ELEMENT SIZE
*
         LDA   ]PROD      ; {3C2B} BACKUP PREVIOUS
         STA   ]PBAK      ; {3C2B} PRODUCT FOR USE LATER
         LDA   ]PROD+1    ; {3C2B} DO SAME FOR HIBYTE
         STA   ]PBAK+1    ; {3C2B}
         LDA   ]XIDX      ; {3C2B} PUT X-INDEX LOBYTE
         STA   ]MLIER     ; {3C2B} INTO MULTIPLIER
         LDA   ]XIDX+1    ; {3C2B} DO SAME FOR HIBYTE
         STA   ]MLIER+1   ; {3C2B}
         LDA   ]ESIZE     ; {3C2B} PUT ELEMENT SIZE
         STA   ]MCAND     ; {3C2B} INTO MULTIPLICAND
         LDA   #0         ; {2C2B} CLEAR MULTIPLICAND HIBYTE
         STA   ]MCAND+1   ; {3C2B}
*
         STA   ]PROD      ; {3C2B} CLEAR PRODUCT
         STA   ]PROD+1    ; {3C2B}
         STA   ]PROD+2    ; {3C2B}
         STA   ]PROD+3    ; {3C2B}
         LDX   #$10       ; {2C2B} INIT X COUNTER TO #16
:SHIFTR  LSR   ]MLIER+1   ; {6C2B} DIVIDE MULTIPLIER BY 2
         ROR   ]MLIER     ; {6C2B} ADJUST LOBYTE
         BCC   :ROTR      ; {3C2B} IF 0 PUT INTO CARRY, ROTATE PROD
         LDA   ]PROD+2    ; {3C2B} LOAD PRODUCT 3RD BYTE
         CLC              ; {2C1B} CLEAR CARRY
         ADC   ]MCAND     ; {4C3B} ADD MULTIPLICAND LOBYTE
         STA   ]PROD+2    ; {3C2B}
         LDA   ]PROD+3    ; {3C2B} LOAD PRODUCT HIBYTE
         ADC   ]MCAND+1   ; {4C3B} HAD MULTIPLICAND HIBYTE
:ROTR
         ROR              ; {6C2B} ROTATE .A RIGHT
         STA   ]PROD+3    ; {3C2B} STORE PRODUCT HIBYTE
         ROR   ]PROD+2    ; {6C2B} ROTATE 3RD BYTE
         ROR   ]PROD+1    ; {6C2B} ROTATE 2ND BYTE
         ROR   ]PROD      ; {6C2B} ROTATE LOBYTE
         DEX              ; {2C1B} DECREASE X COUNTER
         BNE   :SHIFTR    ; {3C2B} IF NOT 0, KEEP LOOPING
*
** NOW ADD X * ESIZE TO RUNNING PRODUCT
*
         CLC              ; {2C1B} CLEAR CARRY
         LDA   ]PROD      ; {3C2B} ADD CURRENT PRODUCT
         ADC   ]PBAK      ; {4C3B} TO PREVIOUS PRODUCT
         STA   ]PROD      ; {3C2B} AND STORE BACK IN PRODUCT
         LDA   ]PROD+1    ; {3C2B}
         ADC   ]PBAK+1    ; {4C3B}
         STA   ]PROD+1    ; {3C2B}
         CLC              ; {2C1B} CLEAR CARRY
         LDA   ]PROD      ; {3C2B} INCREASE LOBYTE BY 5
         ADC   #5         ; {2C2B} TO ACCOUNT FOR ARRAY
         STA   ]PROD      ; {3C2B} HEADER
         LDA   ]PROD+1    ; {3C2B}
         ADC   #0         ; {2C2B} ADJUST HIBYTE
         STA   ]PROD+1    ; {3C2B}
*
** ADD ARRAY ADDRESS TO GET INDEX
*
         CLC              ; {2C1B} CLEAR CARRY
         LDA   ]PROD      ; {3C2B} ADD ARRAY ADDRESS
         ADC   ]ADDRD     ; {4C3B} TO PRODUCT TO GET
         STA   ADDR2      ; {3C2B} ELEMENT ADDRESS; STORE
         LDA   ]PROD+1    ; {3C2B} ADDRESS ON ZERO PAGE
         ADC   ]ADDRD+1   ; {4C3B}
         STA   ADDR2+1    ; {3C2B}
         LDY   #0         ; {2C2B} RESET BYTE INDEX
:CLP
         LDA   (]ADDRS),Y ; {6C2B} LOAD BYTE FROM SOURCE
         STA   (ADDR2),Y  ; {6C2B} STORE AT ELEMENT ADDRESS
         INY              ; {2C1B} INCREASE BYTE INDEX
         CPY   ]ESIZE     ; {4C3B} IF != ELEMENT LENGTH, LOOP
         BNE   :CLP       ; {3C2B}
         LDY   ADDR2+1    ; {3C2B} .Y = ELEMENT ADDRESS HIBYTE
         LDX   ADDR2      ; {3C2B} .X = ELEMENT ADDRESS LOBYTE
         LDA   ]ESIZE     ; {3C2B} .A = ELEMENT LENGTH
         RTS              ; {6C1B}


PART II: THE ARRAYS COLLECTION DEMO FILE

The Arrays Collection Demo File includes basic methods for calling each macro (and thus subroutine) in the collection, along with substantial commentary that explains how the macros are being used. This demo is not meant to be exhaustive; rather, it simply shows the most common ways to use the collection, and serves as a proof-of-concept rather than a rigorous test of each macro and subroutine. Ultimately, this demo--as well as others in the library--is provided for pedagogical reasons rather than practical reasons, and should serve a beginner well in trying to learn how to use the library, or how to use 6502 Assembly with macros in general.

LISTING 3.5: The DEMO.ARRAYS.ASM File Source

*
*``````````````````````````````*
* DEMO.ARRAYS                  *
*                              *
* A DECIDEDLY NON-EXHAUSTIVE   *
* DEMO OF ARRAY FUNCTIONALITY  *
* IN THE APPLEIIASM LIBRARY.   *
*                              *
* AUTHOR:    NATHAN RIGGS      *
* CONTACT:   NATHAN.RIGGS@     *
*            OUTLOOK.COM       *
*                              *
* DATE:      18-MAR-2021       *
* ASSEMBLER: MERLIN 8 PRO      *
* OS:        DOS 3.3           *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
** ASSEMBLER DIRECTIVES
*
         CYC   AVE
         EXP   OFF
         TR    ON
         DSK   DEMO.ARRAYS
         OBJ   $BFE0
         ORG   $6000
*
*``````````````````````````````*
*  TOP INCLUDES (HOOKS,MACROS) *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
         PUT   MIN.HEAD.REQUIRED.ASM
         USE   MIN.MAC.REQUIRED.ASM
         USE   MIN.MAC.ARR8B1D.ASM
         USE   MIN.MAC.ARR8B2D.ASM
         USE   MIN.MAC.ARR16B1D.ASM
         USE   MIN.MAC.ARR16B2D.ASM
         PUT   MIN.HEAD.ARRAYS.ASM
*
]HOME2   EQU   $FC58
*
*``````````````````````````````*
*      PROGRAM MAIN BODY       *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
** THE ARRAY COLLECTION OF THE APPLEIIASM LIBRARY
** CONTAINS THE CORE FUNCTIONALITY NECESSARY FOR
** USING ARRAYS. CURRENTLY, THERE ARE 4 TYPES OF
** ARRAYS SUPPORTED: 8-BIT, 1 DIMENSIONAL ARRAYS,
** WHICH CAN HOLD UP TO 255 KEYS, 8-BIT, 2-DIMENSIONAL
** ARRAYS THAT CAN HOLD UP TO 255 X 255 ELEMENTS
** ACCESSED VIA AN X,Y COORDINATE; 16-BIT,
** 1-DIMENSION ARRAYS THAT CAN HOLD JUST OVER 65K
** ELEMENTS (A MAXIMUM THAT WOULD TAKE UP MOST OF THE
** MEMORY ON A TYPICAL APPLE II SYSTEM), AND 16-BIT,
** 2-DIMENSIONAL ARRAYS THAT CAN HOLD A MAXIMUM OF
** 65K X 65K ELEMENTS ACCESSED VIA X,Y COORDINATES.
*
** AS IT IS, EACH TYPE OF ARRAY HAS THREE MACROS DEDICATED
** TO IT: AN ARRAY INITIALIZATION MACRO (DIM), A MACRO
** THAT GETS AN ELEMENT VALUE, AND A MACRO THAT ENTERS
** A VALUE AT THE SPECIFIED INDEX ELEMENT. SUPPORT FOR
** SPECIFIC MACROS AND SUBROUTINES THAT ACT ON ARRAYS, SUCH
** AS SORTING ALGORITHMS, MERGING, SEARCHING AND SO ON,
** ARE LIKELY TO BE ADDED IN THE FUTURE, BUT THIS WILL
** REQUIRE A SECOND DISK DEDICATED TO ARRAYS, GIVEN SPACE
** CONSTRAINTS. SUPPORT FOR 8-BIT 3-DIMENSIONAL ARRAYS
** MAY ALSO BE SUPPORTED, BUT THAT DEPENDS ON NECESSITY OR
** DEMAND.
*
** THIS DEMO FILE SHOWS THE USAGE AND PERFORMANCE OF THE
** FOLLOWING MACROS (AND THE SUBROUTINES BEHIND THEM):
*
**  DIM81   : INITIALIZE AN 8-BIT, 1 DIMENSIONAL ARRAY
**  GET81   : RETRIEVE ELEMENT VALUE FROM 8-BIT, 1-D ARRAY
**  PUT81   : PLACE VALUE IN ELEMENT OF 8-BIT, 1-D ARRAY
**  DIM82   : INITIALIZE AN 8-BIT, 2 DIMENSIONAL ARRAY
**  GET82   : RETRIEVE ELEMENT VALUE FROM 8-BIT, 2-D ARRAY
**  PUT82   : PLACE VALUE IN ELEMENT OF 8-BIT, 2-D ARRAY
**  DIM161  : INITIALIZE A 16-BIT, 1 DIMENSIONAL ARRAY
**  GET161  : RETRIEVE ELEMENT VALUE FROM 16-BIT, 1-D ARRAY
**  PUT161  : PLACE VALUE IN ELEMENT OF 16-BIT, 1-D ARRAY
**  DIM162  : INITIALIZE A 16-BIT, 2 DIMENSIONAL ARRAY
**  GET162  : RETRIEVE ELEMENT VALUE FROM 16-BIT, 2-D ARRAY
**  PUT162  : PLACE VALUE IN ELEMENT OF 16-BIT, 2-D ARRAY
*
*``````````````````````````````*
* 8-BIT ARRAY MACRO EXAMPLES   *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
** SINCE THESE ROUTINES DO NOT NORMALLY OUTPUT ANYTHING
** ON THEIR OWN, WE WILL BE USING THE DUMP MACRO FROM THE
** REQUIRED LIBRARY COLLECTION IN ORDER TO PEEK AT THE
** ARRAY CONTENTS.
*
** FIRST, LET'S TACKLE ONE-DIMENSIONAL 8-BIT ARRAYS. WE START
** BY INITIALIZING THE ARRAY, WHICH ALWAYS MUST BE DONE BEFORE
** ANY OTHER USE OF AN ARRAY. WE DO THIS WITH THE DIM81 MACRO,
** AS SUCH:
*
         DIM81 #$300;#10;#2;#$EE
*
** HERE WE ARE INITIALIZING AN ARRAY AT THE ADDRESS #$300,
** WITH 10 ELEMENTS THAT ARE EACH 2 BYTES LONG. EACH ELEMENT
** IS FILLED WITH THE VALUE #$EE TO BEGIN WITH. NOW WE CAN
** USE DUMP TO VIEW THE ARRAY:
*
         JSR   ]HOME2
         _PRN  "8-BIT, 1-DIMENSIONAL ARRAYS",8D
         _PRN  "===========================",8D8D8D
         DUMP  #$300;#2
         DUMP  #$302;#10
         DUMP  #$30C;#10
         _PRN  " ",8D8D
         _WAIT
*
** IT IS IMPORTANT TO NOTE HOW WE ARE VIEWING THE ARRAY HERE.
** FIRST, WE ARE DUMPING THE FIRST TWO BYTES OF THE ARRAY;
** THESE HOLD THE NUMBER OF ELEMENTS IN THE ARRAY AND THE
** LENGTH OF EACH ELEMENT, RESPECTIVELY. AFTERWARDS, WE
** DUMP THE FOLLOWING 20 BYTES, DETERMINED BY THE ELEMENT
** SIZE TIMES THE NUMBER OF ELEMENTS, WHICH HOLDS THE DATA
** OF THE ARRAY. ALL FOUR TYPES OF ARRAYS ARE BUILT IN A
** SIMILAR FASHION, BUT VARY BASED ON THE NEEDS OF EACH TYPE.
** THE DIFFERENCES WILL BE EASY TO DISCERN ONCE WE DUMP
** A 2-DIMENSIONAL ARRAY.
*
** NOW WE CAN PUT A VALUE IN AN ELEMENT. THIS IS EASILY DONE VIA
** THE PUT81 MACRO:
*
         PUT81 #]WORD1;#$300;#5
         DUMP  #$300;#2
         DUMP  #$302;#10
         DUMP  #$30C;#10
         _PRN  " ",8D8D
         _WAIT
*
** TWO THINGS SHOULD BECOME APPARENT AFTER VIEWING THE
** CONTENTS OF THIS ARRAY, AND THESE EXTEND TO ALL TYPES
** OF ARRAYS: FIRST, AN ITEM IS COPIED TO THE ARRAY FROM
** ANOTHER MEMORY ADDRESS, AND THE NUMBER OF BYTES COPIED
** IS DETERMINED BY THE ELEMENT LENGTH OF THE ARRAY;
** SECOND, IT SEEMS AT FIRST AS THOUGH THE ELEMENT ALTERED
** IS AT INDEX 6 RATHER THAN 5, BUT THIS IS BECAUSE ARRAYS
** START AT ELEMENT 0. NOT ELEMENT 1. WHILE THERE WOULD NOT
** BE MUCH OF A PROBLEM TREATING ARRAYS AS STARTING AT
** ELEMENT 1, PROVIDED YOU ARE CAREFUL NOT TO OVERFLOW THE
** ARRAY, INDEXING MIGHT GET CONFUSING.
*
** NOW WE CAN GET BACK THE VALUE WE PLACED IN THE ARRAY
** WITH THE GET81 MACRO, AS SUCH:
*
         GET81 #$300;#5
         DUMP  #RETURN;RETLEN
         _WAIT
*
** NOTE HERE THAT THE VALUE IS PLACED IN #RETURN, AND ITS
** LENGTH IS PLACED IN RETLEN. THIS IS HOW ALL ARRAY GET
** MACROS WORK.
*
** NOW THAT WE HAVE ONE TYPE OF ARRAY DOWN, THE REST OF THEM
** ARE SIMPLE TO UNDERSTAND BECAUSE THEY ALL WORK THE SAME
** WAY. LET'S LOOK AT HOW TWO-DIMENSION ARRAYS ARE HANDLED AT
** THE 8-BIT LEVEL TO HAVE A MORE COMPLETE UNDERSTANDING BEFORE
** MOVING TO 16 BITS, HOWEVER. FIRST, WE INITIALIZE THE ARRAY:
*
         JSR   ]HOME2
         _PRN  "8-BIT, 2-DIMENSIONAL ARRAYS",8D
         _PRN  "---------------------------",8D8D8D
         DIM82 #$300;#5;#5;#1;#$AA
         DUMP  #$300;#3
         DUMP  #$303;#5
         DUMP  #$308;#5
         DUMP  #$30D;#5
         DUMP  #$312;#5
         DUMP  #$317;#5
         _WAIT
         _PRN  " ",8D8D
*
** AND NOW THAT WE HAVE DECLARED THE 2D, 8BIT ARRAY,
** ADDING A VALUE AND RETRIVING IT IS EASY:
*
         PUT82 #]WORD1;#$300;#3;#3
         GET82 #$300;#3;#3
         DUMP  #RETURN;RETLEN
         _WAIT
*
*
** IN THE ABOVE CODE, YOU CAN SEE THAT WE INITIALIZE
** THE 2-DIMENSIONAL ARRAY AT #$300, WHICH WRITES OVER
** THE PREVIOUS 1-DIMENSIONAL ARRAY. THERE ARE NO
** PROTECTIONS AGAINST THIS; YOU ARE EXPECTED TO KEEP
** TRACK OF THE LOCATIONS YOURSELF. AFTER THE ADDRESS,
** THE LENGTHS OF EACH DIMENSION ARE DECLARED (IN THIS
** CASE, 5 EACH), THEN THE LENGTH OF EACH ELEMENT IN
** THE ARRAY. FINALLY, THE DEFAULT VALUE, #$AA, IS
** PROVIDED. THE ARRAY IS THEN LISTED WITH THE DUMP
** COMMAND, 5 ELEMENTS AT A TIME.
*
** NOW ARMED WITH EXAMPLES OF 1 DIMENSIONAL AND TWO
** DIMENSIONAL ARRAYS, IT CAN BE EASILY INFERRED HOW
** THE REST OF THE MACROS WORK. THE ONLY REAL DIFFERENCE
** IS THAT 16-BIT ARRAYS USE TWO BYTES FOR THE NUMBER OF
** ELEMENTS IN EACH DIMENSION, RATHER THAN A SINGLE BYTE
** (THUS THE 16-BIT DESCRIPTION). THUS, WE CAN DECLARE
** A 16-BIT, 1-DIMENSIONAL ARRAY AS WELL AS PUT AND GET
** TO AND FROM IT AS SUCH:
*
         JSR   ]HOME2
         DIM161 #$300;#10;#1;#$55
         PUT161 #]WORD1;#$300;#3
         GET161 #$300;#3
         _PRN  "16-BIT, 1-DIMENSIONAL ARRAYS",8D
         _PRN  "============================",8D8D8D
         DUMP  #$300;#3
         DUMP  #$303;#10
         _PRN  " ",8D8D
         DUMP  #RETURN;RETLEN
         _WAIT
*
** AND AS YOU WOULD EXPECT, 16-BIT 2 DIMENSIONAL ARRAYS
** WORK IN MUCH THE SAME WAY:
*
         JSR   ]HOME2
         DIM162 #$300;#3;#3;#1;#$66
         PUT162 #]WORD1;#$300;#0;#0
         GET162 #$300;#0;#0
         _PRN  "16-BIT, 2-DIMENSIONAL ARRAYS",8D
         _PRN  "============================",8D8D8D
         DUMP  #$300;#5
         DUMP  #$305;#3
         DUMP  #$308;#3
         DUMP  #$30B;#3
         _PRN  " ",8D8D
         DUMP  #RETURN;RETLEN
         _WAIT
         JSR   ]HOME2
         _PRN  " ",8D8D
         _PRN  "FIN.",8D8D
*
         JMP   REENTRY
*
*``````````````````````````````*
* BOTTOM INCLUDES (ROUTINES)   *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
         PUT   MIN.LIB.REQUIRED.ASM
*
** INDIVIDUAL SUBROUTINE INCLUDES
*
** 8-BIT 1-DIMENSIONAL ARRAY SUBROUTINES
*
         PUT   MIN.SUB.ADIM81.ASM
         PUT   MIN.SUB.AGET81.ASM
         PUT   MIN.SUB.APUT81.ASM
*
** 8-BIT 2-DIMENSIONAL ARRAY SUBROUTINES
*
         PUT   MIN.SUB.ADIM82.ASM
         PUT   MIN.SUB.AGET82.ASM
         PUT   MIN.SUB.APUT82.ASM
*
** 16-BIT 1-DIMENSIONAL ARRAYS
*
         PUT   MIN.SUB.ADIM161.ASM
         PUT   MIN.SUB.APUT161.ASM
         PUT   MIN.SUB.AGET161.ASM
*
** 16-BIT 2-DIMENSIONAL ARRAYS
*
         PUT   MIN.SUB.ADIM162.ASM
         PUT   MIN.SUB.APUT162.ASM
         PUT   MIN.SUB.AGET162.ASM
*
** VARIABLES
*
]WORD1   HEX   FFFF