AppleIIAsm-Collection/documentation/AppleIIAsm Library Collection Technical Manual/0.6.1/32.0 Detailed_Reference_D3_ARRAYS.md
Nathan D Riggs c28c9d87da Disk 1: REQUIRED disk and documentation
Cleaned up disk one documentation, both inline and in the manual
2021-06-06 21:50:54 -04:00

3640 lines
153 KiB
Markdown

# Disk 3 : 8-Bit and 16-Bit 1D and 2D Arrays
- [Part I: The Arrays Collection](#part-i-the-arrays-collection)
- [Arrays Components](#arrays-components)
- [Arrays Header File](#arrays-header-file)
- [Arrays Macros and Subroutines](#macros-and-subroutines)
- [8-Bit Arrays](#1-dimensional-8-bit-arrays)
- [1-Dimensional, 8-Bit Arrays](#1-dimensional-8-bit-arrays)
- [The DIM81 Macro](#the-dim81-macro)
- [The ADIM81 Subroutine](#the-adim81-subroutine)
- [The GET81 Macro](#the-get81-macro)
- [The AGET81 Subroutine](#the-aget81-subroutine)
- [The PUT81 Macro](#the-put81-macro)
- [The APUT81 Subroutine](#the-aput81-subroutine)
- [2-Dimensional, 8-Bit Arrays](#the-dim82-macro)
- [The DIM82 Macro](#the-dim82-macro)
- [The ADIM82 Subroutine](#the-adim82-subroutine)
- [The GET82 Macro](#the-get82-macro)
- [The AGET82 Subroutine](#the-aget82-subroutine)
- [The PUT82 Macro](#the-put82-macro)
- [The APUT82 Subroutine](#the-aput82-subroutine)
- [16-Bit Arrays](#1-dimensional-16-bit-arrays)
- [1-Dimensional, 16-Bit Arrays](#1-dimensional-16-bit-arrays)
- [The DIM161 Macro](#the-dim161-macro)
- [The ADIM161 Subroutine](#the-adim161-subroutine)
- [The GET161 Macro](#the-get161-macro)
- [The AGET161 Subroutine](#the-aget161-subroutine)
- [The PUT161 Macro](#the-put161-macro)
- [The APUT161 Subroutine](#the-aput161-subroutine)
- [2-Dimensional, 16-Bit Arrays](#2-dimensional-16-bit-arrays)
- [The DIM162 Macro](#the-dim162-macro)
- [The ADIM162 Subroutine](#the-adim162-subroutine)
- [The GET162 Macro](#the-get162-macro)
- [The AGET162 Subroutine](#the-aget162-subroutine)
- [The PUT162 Macro](#the-put162-macro)
- [The APUT162 Subroutine](#the-aput162-subroutine)
- [Part II: The Arrays Collection Demo File](#part-ii-the-arrays-collection-demo-file)
---
## Part I: The Arrays Collection
The third disk in the AppleIIASM library is dedicated to using 8-bit and 16-bit arrays with either one or two dimensions. The number of bits assigned refers to the number of possible elements: 8-bit arrays can have up to 255 elements in each dimension, with each element having a possible size of 255 bytes, whereas 16-bit arrays can have up to 65,025 elements in each dimension. When possible, 8-bit arrays should be used to save both cycles and bytes. Additionally, it should be apparent that a single 16-bit arrays could easily encompass more memory than is available in most Apple II computers; obviously, care needs to be taken not to fill up memory with a gigantic array.
There are four types of arrays supported: 8-bit, 1-dimensional arrays, 8-bit, 2-dimensional arrays, 16-bit, 1-dimensional arrays and 16-bit, 2-dimensional arrays. Each type has three main macros (and subroutines) for data manipulation: a DIM command that initializes the array, a GET command that retrieves a value from the array, and a PUT command that enters a value into an array. While more functionality might be desired, these commands for each array type take up most of the disk space available; thus, a new disk would be necessary for additional commands. These may be added, but not until the entire library is nearing completion.
---
## Arrays Components
The Arrays collection contains the following components:
- A header file with various hooks, vectors and definitions for the rest of the library as well as use by the end programmer.
- Macros and subroutines for each of the four types of arrays provided. Functionality is given for declaring an array, getting data from an array, and putting data into an array.
- A Demonstration of the macros and subroutines in use.
---
## Arrays Header File
Currently, the Arrays header file includes an unused variable declaration only, which is present in the file only to prevent an error from being returned by the Merlin Pro 8 assembler. This is included to keep the same structure as other disks; eventually, more may be added to the header that is necessary for the working of the rest of the collection. For instance, all subroutines use a multiplication implementation; this can be moved to the header, saving space on the disk.
| Condition | Value |
| ------------- | ------------------------------------------------------------ |
| Name | File: HEAD.ARRAYS.ASM |
| Type | Header File |
| Author | Nathan Riggs |
| Last Revision | 19-MAR-2021 |
| Assembler | Merlin 8 Pro |
| OS | Apple DOS 3.3 |
| Purpose | Provide appropriate hooks and routines for the Arrays collection |
| Dependencies | none |
| Bytes | 0 |
| Notes | Repeatedly used subroutines by the library may be placed here in the future |
| See Also | |
---
*DETAILS*
As with all files in the collection, this header file includes a short heading that provides contact information, date last changed, number of bytes, a short description, etc.
`Listing 3.0: HEAD.ARRAYS Source`
```assembly
*
*``````````````````````````````*
* HEAD.ARRAYS.ASM *
* *
* CURRENTLY, THIS HEADER FILE *
* ONLY CONTAINS DUMMY CODE IN *
* ORDER TO PREVENT AN ERROR *
* DURING ASSEMBLY (EMPTY *
* FILE). *
* *
* AUTHOR: NATHAN RIGGS *
* CONTACT: NATHAN.RIGGS@ *
* OUTLOOK.COM *
* *
* DATE: 18-MAR-2021 *
* ASSEMBLER: MERLIN 8 PRO *
* OS: DOS 3.3 *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
ARRMAX EQU 8192 ; MAXIMUM # OF BYTES
; AN ARRAY CAN USE
*
```
---
## Macros and Subroutines
The Arrays Collection is separated into four different macro files: 8-bit, single dimension arrays; 8-bit, double dimension arrays; 16-bit, single dimension arrays and 16-bit double dimension arrays. These are all independent of one another, but follow similar syntax and data structures. If possible, 8-bit arrays should be used in lieu of 16-bit arrays due to the extra cycles needed to calculate larger numbers for 16-bit values.
---
### 1-Dimensional, 8-Bit Arrays
These macros and subroutines are dedicated to using arrays with a maximum of 255 elements per dimension (in this case, a single dimension), with each element being up to 255 bytes in length. The macros for these arrays are located in the MAC.ARR8B1D.ASM file, named so after the types of macros it includes.
`LISTING 3.10: MAC.ARR8B1D.ASM Header Source`
```assembly
*
*``````````````````````````````*
* MAC.ARR8B1D.ASM *
* *
* LIBRARY OF MACROS FOR 8-BIT, *
* 1-DIMENSIONAL ARRAYS. *
* *
* AUTHOR: NATHAN RIGGS *
* CONTACT: NATHAN.RIGGS@ *
* OUTLOOK.COM *
* *
* DATE: 18-MAR-2021 *
* ASSEMBLER: MERLIN 8 PRO *
* OS: DOS 3.3 *
* *
* SUBROUTINE FILES USED *
* *
* SUB.ADIM81 *
* SUB.AGET81 *
* SUB.APUT81 *
* *
* LIST OF MACROS *
* *
* DIM81: DIM 1D, 8BIT ARRAY *
* GET81: GET ELEMENT IN 8BIT, *
* 1D ARRAY. *
* PUT81: PUT VALUE INTO 8BIT, *
* 1D ARRAY AT INDEX *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
```
---
### THE DIM81 MACRO
_SUMMARY_
| Condition | Value |
| --------------- | ------------------------------------------------------------ |
| Name | `DIM81` |
| Type | Macro |
| File | `MAC.ARR8B1D.ASM` |
| Author | Nathan Riggs |
| Last Revision | 18-MAR-2021 |
| Assembler | Merlin Pro 8 |
| OS | Apple DOS 3.3 |
| Purpose | Initialize an 8-bit, 1-dimensional array |
| Input | ]1 = Address of Array<br />]2 = Number of Elements<br />]3 = Size of each Element<br />]4 = Default Fill Value |
| Output | none |
| Dependencies | `SUB.ADIM81.ASM` |
| Flags Destroyed | NZCV |
| Cycles | 234+ |
| Bytes | 146 |
| Notes | None |
| See Also | `ADIM81` `GET81` `AGET81` `PUT81` `APUT81` |
---
*DETAILS*
The `DIM81` macro initializes an array that has one dimension and can have up to 255 elements, with each element holding up to a possible 255 bytes. This must be called before any array of its type can be used, as it sets up the data structure used by the `GET81` and `PUT81` macros. This data structure is fairly simple: the first byte of the array holds the number of elements in the array, and the second byte holds the size of each element. Afterwards, the accessible data of the array follows in linear order: element 1 is followed by element 2, 2 by 3, and so on until each element is accounted for.
Note that the entire block of memory used by the array is initialized once calling DIM81. Without care, this can wipe out data or a subroutine that is already held in memory. **`DIM81` does not check to see what it overwrites**; in fact, it would be incapable of doing so. To be safe, you should always take care to calculate the size of the array yourself, either in code or calculated prior to coding. This can be done by multiplying the element length by the number of elements, then adding two for the preceding bytes that store the length of the array and element size.
`LISTING 3.11: DIM81 Source`
```assembly
*
*``````````````````````````````*
* DIM81 (NATHAN RIGGS) *
* *
* CREATE A ONE DIMENSIONAL, *
* 8-BIT ARRAY AT THE GIVEN *
* ADDRESS. *
* *
* PARAMETERS *
* *
* ]1 = ARRAY ADDRESS *
* ]2 = ARRAY BYTE LENGTH *
* ]3 = ELEMENT BYTE LENGTH *
* ]4 = FILL VALUE *
* *
* CYCLES: 234+ *
* SIZE: 146 BYTES *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
DIM81 MAC
_MLIT ]1;WPAR1 ; {16C12B} PARSE IF LITERAL OR NOT
LDA ]2 ; {3C2B} ARRAY LENGTH
STA WPAR2 ; {3C2B}
LDA ]3 ; {3C2B} ELEMENT LENGTH
STA WPAR3 ; {3C2B}
LDA ]4 ; {3C2B}
STA BPAR1 ; {3C2B} FILL VAL
JSR ADIM81 ; {200C132B}
<<<
*
```
---
### THE ADIM81 SUBROUTINE
_SUMMARY_
| Condition | Value |
| --------------- | ------------------------------------------------------------ |
| Name | `ADIM81` |
| Type | Subroutine |
| File | `SUB.ADIM81.ASM` |
| Author | Nathan Riggs |
| Last Revision | 18-MAR-2021 |
| Assembler | Merlin Pro 8 |
| OS | Apple DOS 3.3 |
| Purpose | Initialize an 8-bit, 1-dimensional array |
| Input | WPAR1 = Array Address<br />WPAR2 = Number of Elements<br />WPAR3 = Element Length<br />BPAR1 = Default Fill Value |
| Output | none |
| Dependencies | `SUB.ADIM81.ASM` |
| Flags Destroyed | NZCV |
| Cycles | 194+ |
| Bytes | 129 |
| Notes | None |
| See Also | `DIM81` `GET81` `AGET81` `PUT81` `APUT81` |
---
*DETAILS*
The `ADIM81` subroutine initializes an eight-bit, one-dimensional array stored at a given address, with its number of elements and the byte size of each element passed in the parameters, along with the default fill value for each byte of the array. The array created has a simple data structure: the first byte contains the number of elements and the second byte contains the length of each element, followed by the element data. It should be noted that `ADIM81` does not care about the data it overwrites in memory; the programmer must take care to make sure that important data or routines are not overwritten.
`ADIM81` first multiplies the number of elements specified by the passed size of each element, then adds two to account for the total number of bytes needed for the array. Then, the space is cleared starting at the address given for the array and filled with the specified fill character. The total size of the array is then held in `RETURN` in case the programmer wants to save the array size for future reference.
`LISTING 3.12: SUB.ADIM81.ASM Source`
```assembly
*
*``````````````````````````````*
* ADIM81 (NATHAN RIGGS) *
* *
* DECLARE THE DIMENSIONS OF A *
* NEW 8BIT, 1D ARRAY. *
* *
*------------------------------*
* 8-BIT MULTIPLICATION CODE *
* ADAPTED FROM WHITE FLAME'S *
* WORK ON CODEBASE64. LICENSE *
* MAY VARY. *
*------------------------------*
* *
* INPUT *
* *
* WPAR1 = ARRAY ADDRESS *
* WPAR2 = # OF ELEMENTS *
* WPAR3 = LENGTH OF ELEMENTS *
* BPAR1 = FILL VALUE *
* *
* DESTROY: NZCIDV *
* ^^^ ^ *
* *
* CYCLES: 194+ *
* SIZE: 129 BYTES *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]ADDR EQU WPAR1 ; ARRAY ADDRESS
]ASIZE EQU WPAR2 ; NUMBER OF ELEMENTS
]ESIZE EQU WPAR3 ; ELEMENT SIZE, IN BYTES
]FILL EQU BPAR1 ; DEFAULT FILL VALUE
*
]MSIZE EQU VARTAB ; TOTAL BYTES OF ARRAY
]ASZBAK EQU VARTAB+4 ; ARRAY SIZE BACKUP
]ESZBAK EQU VARTAB+6 ; ELEMENT SIZE BACKUP
*
ADIM81
LDA ]ESIZE ; {2C2B} LOAD ELEMENT SIZE
STA ]ESZBAK ; {3C2B} SAVE A BACKUP
LDA ]ASIZE ; {2C2B} LOAD ARRAY SIZE
STA ]ASZBAK ; {3C2B} SAVE A BACKUP
LDA #0 ; {2C2B} RESET .A
STA ]ASIZE+1 ; {3C2B} CLEAR ARRAY SIZE HIGH BYTE
STA ]ASZBAK+1 ; {3C2B} CLEAR ARRAY SIZE BACKUP HIGH BYTE
*
** MULTIPLY ARRAY SIZE BY ELEMENT SIZE
*
LDY #0 ; {2C2B} RESET HIBYTE FOR MULTIPLY
TYA ; {3C2B} RESET LOBYTE FOR MULTIPLY
LDY ]ASIZE+1 ; {2C2B}
STY SCRATCH ; {3C2B} SAVE HIBYTE IN SCRATCH
BEQ :ENTLP ; {3C2B} IF ZERO, SKIP TO LOOP
:DOADD
CLC ; {2C1B} ADD ASIZE TO LOBYTE
ADC ]ASIZE ; {5C3B}
TAX ; {3C2B} TEMPORARILY STORE IN .X
TYA ; {3C2B} TRANSFER HIBYTE TO .A
ADC SCRATCH ; {5C3B} ADD HIBYTE
TAY ; {3C2B} STORE BACK IN .Y
TXA ; {3C2B} LOAD LOBYTE IN .A AGAIN
:LP ; LOOP START
ASL ]ASIZE ; {6C3B} MULTIPLY ASIZE BY 2
ROL SCRATCH ; {6C3B} MULTIPLY HIBYTE BY 2
:ENTLP
LSR ]ESIZE ; {6C3B} DIVIDE ESIZE BY 2
BCS :DOADD ; {3C2B} IF >= LOBYTE IN .A, ADD AGAIN
BNE :LP ; {3C2B} OTHERWISE, RELOOP
*
STX ]MSIZE ; {3C2B} STORE LOBYTE
STY ]MSIZE+1 ; {3C2B} STORE HIBYTE
LDA ]MSIZE ; {2C2B} NOW ADD TO BYTES
CLC ; {2C1B} TO MSIZE FOR ARRAY HEADER
ADC #2 ; {3C2B}
STA ]MSIZE ; {3C2B} STORE LOBYTE
LDA ]MSIZE+1 ; {2C2B}
ADC #0 ; {3C2B} CARRY FOR HIBYTE
STA ]MSIZE+1 ; {3C2B}
*
** NOW CLEAR MEMORY BLOCKS
*
LDA ]FILL ; {2C2B} GET FILL VALUE
LDX ]MSIZE+1 ; {2C2B} X = # O PAGES TO DO
BEQ :PART ; {3C2B} BRANCH IF HIBYTE = 0
LDY #0 ; {2C2B} RESET INDEX
:FULL
STA (]ADDR),Y ; {3C2B} FILL CURRENT BYTE
INY ; {2C1B} INCREMENT INDEX
BNE :FULL ; {3C2B} LOOP UNTIL PAGE DONE
INC ]ADDR+1 ; {5C2B} GO TO NEXT PAGE
DEX ; {2C1B} DECREMENT COUNTER
BNE :FULL ; {3C2B} LOOP IF PAGES LEFT
:PART
LDX ]MSIZE ; {2C2B} PARTIAL PAGE BYTES
BEQ :MFEXIT ; {3C2B} EXIT IF LOBYTE = 0
LDY #0 ; {2C2B} RESENT INDEX
:PARTLP
STA (]ADDR),Y ; {3C2B} STORE VAL
INY ; {2C1B} INCREMENT INDEX
DEX ; {2C1B} DECREMENT COUNTER
BNE :PARTLP ; {3C2B} LOOP UNTIL DONE
:MFEXIT
LDY #0 ; {2C2B} STORE NUMBER OF ELEMENTS
LDA ]ASZBAK ; {2C2B} INTO FIRST BYTE OF ARRAY
STA (]ADDR),Y ; {6C2B}
INY ; {2C1B}
LDA ]ESZBAK ; {2C2B} STORE ELEMENT SIZE INTO
STA (]ADDR),Y ; {6C2B} SECOND BYTE OF ARRAY
LDX ]ADDR ; {2C2B} GET LOBYTE OF ARRAY ADDRESS
LDY ]ADDR+1 ; {2C2B} AND HIBYTE TO RETURN IN .X, .Y
LDA ]ASZBAK ; {2C2B} RETURN NUMBER OF ELEMENTS IN .A
LDA ]MSIZE ; {2C2B} STORE TOTAL ARRAY SIZE
STA RETURN ; {3C2B} IN RETURN
LDA ]MSIZE+1 ; {2C2B}
STA RETURN+1 ; {3C2B}
LDA #2 ; {2C2B} SET RETURN LENGTH TO
STA RETLEN ; {3C2B} 2 BYTES
RTS ; {6C1B}
```
---
### THE GET81 MACRO
_SUMMARY_
| Condition | Value |
| --------------- | --------------------------------------------- |
| Name | `GET81` |
| Type | Macro |
| File | `MAC.ARR8B1D.ASM` |
| Author | Nathan Riggs |
| Last Revision | 18-MAR-2021 |
| Assembler | Merlin Pro 8 |
| OS | Apple DOS 3.3 |
| Purpose | Initialize an 8-bit, 1-dimensional array |
| Input | ]1 = Address of Array<br />]2 = Element Index |
| Output | none |
| Dependencies | `SUB.AGET81.ASM` |
| Flags Destroyed | NZCV |
| Cycles | 193+ |
| Bytes | 125 |
| Notes | None |
| See Also | `DIM81` `ADIM81` `AGET81` `PUT81` `APUT81` |
---
*DETAILS*
The `GET81` macro retrieves an element's value from an eight-bit, one-dimensional array. Parameters are passed via the registers, and the result is passed back via `RETURN`. The `RETLEN` byte holds the length of the value in `RETURN`. Additionally, the element address is returned in the **.X** (low byte) and **.Y** (high byte) registers, while the length of the return value is also stored in **.A**.
`LISTING 3.13: The GET81 Macro`
```assembly
*
*``````````````````````````````*
* GET81 (NATHAN RIGGS) *
* *
* RETRIEVE A VALUE FROM THE *
* GIVEN ARRAY AT THE SPECIFIED *
* ELEMENT INDEX AND STORE THE *
* VALUE IN RETURN. *
* *
* PARAMETERS *
* *
* ]1 = ARRAY ADDRESS *
* ]2 = ELEMENT INDEX *
* *
* CYCLES: 193+ *
* SIZE: 125 BYTES *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
GET81 MAC
_AXLIT ]1 ; {8C6B} PARSE ADDRESS
LDY ]2 ; {3C2B} ELEMENT INDEX
JSR AGET81 ; {182C117B}
<<<
*
```
---
### THE AGET81 SUBROUTINE
_SUMMARY_
| Condition | Value |
| --------------- | ------------------------------------------------------------ |
| Name | `AGET81` |
| Type | Subroutine |
| File | `SUB.AGET81.ASM` |
| Author | Nathan Riggs |
| Last Revision | 18-MAR-2021 |
| Assembler | Merlin Pro 8 |
| OS | Apple DOS 3.3 |
| Purpose | Initialize an 8-bit, 1-dimensional array |
| Input | .A = Array Address Low Byte<br />.X = Array Address High Byte<br />.Y = Array Element Index |
| Output | none |
| Dependencies | none |
| Flags Destroyed | NZCV |
| Cycles | 176+ |
| Bytes | 114 |
| Notes | None |
| See Also | `DIM81` `ADIM81` `GET81` `PUT81` `APUT81` |
---
*DETAILS*
The `AGET81` subroutine accepts an array address via the **.X** (low byte) and **.Y** (high byte) registers and an element index in the **.A** register and returns the value of the element specified in the given array. The element data is held in `RETURN` after execution, with its length held in both `RETLEN` and **.A**. The memory address of the element is also returned back via the **.X** (low byte) and **.Y** (high byte) registers. This address is calculated by multiplying the passed index by the element length, adding two for the preceding length bytes, then added to the address of the array itself.
`LISTING 3.14: The AGET81 Subroutine Source`
```assembly
*
*``````````````````````````````*
* AGET81 (NATHAN RIGGS) *
* *
* RETRIEVE A VALUE AT A GIVEN *
* ARRAY INDEX AND STORE IN *
* RETURN AREA WITH APPROPRIATE *
* RETURN LENGTH (RETLEN). *
* *
*------------------------------*
* 8-BIT MULTIPLICATION CODE *
* ADAPTED FROM WHITE FLAME'S *
* WORK ON CODEBASE63. LICENSE *
* MAY VARY. *
*------------------------------*
* *
* INPUT: *
* *
* .A = ARRAY ADDRESS LOBYTE *
* .X = ARRAY ADDRESS HIBYTE *
* .Y = ARRAY ELEMENT INDEX *
* *
* DESTROY: NZCIDV *
* ^^^ ^ *
* *
* CYCLES: 176+ *
* SIZE: 114 BYTES *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]RES EQU VARTAB ; MATH RESULTS
]IDX EQU VARTAB+2 ; ELEMENT INDEX
]ESIZE EQU VARTAB+4 ; ELEMENT SIZE
]ALEN EQU VARTAB+5 ; NUMBER OF ELEMENTS
*
AGET81
STA ADDR1 ; {3C2B} .A HOLDS ARRAY ADDRESS LOBYTE
STX ADDR1+1 ; {3C2B} .X HOLDS ADDRESS HIBYTE
STY ]IDX ; {3C2B} .Y HOLDS THE INDEX
LDA #0 ; {2C2B} CLEAR INDEX HIBYTE
STA ]IDX+1 ; {3C2B}
LDY #1 ; {2C2B} GET ELEMENT SIZE FROM ARRAY
LDA (ADDR1),Y ; {6C2B} HEADER
STA ]ESIZE ; {3C2B}
STA RETLEN ; {3C2B} STORE IN RETLEN
DEY ; {2C1B} MOVE TO BYTE 0 OF HEADER
LDA (ADDR1),Y ; {6C2B} GET NUMBER OF ELEMENTS
STA ]ALEN ; {3C2B} FROM THE ARRAY HEADER
*
** MULTIPLY INDEX BY ELEMENT SIZE, ADD 2
*
TYA ; {2C1B} Y ALREADY HOLDS ZERO
STY SCRATCH ; {3C2B} RESET LO AND HI TO 0
BEQ :ENTLP ; {3C2B} IF ZERO, SKIP TO LOOP
:DOADD
CLC ; {2C1B} CLEAR CARRY FLAG
ADC ]IDX ; {4C3B} ADD INDEX LOBYTE
TAX ; {2C1B} TEMPORARILY STORE IN .X
TYA ; {2C1B} TRANSFER HIBYTE TO .A
ADC SCRATCH ; {4C3B} ADD HIBYTE
TAY ; {2C1B} STORE BACK INTO .Y
TXA ; {2C1B} RELOAD LOBYTE IN .A
:LP
ASL ]IDX ; {6C3B} MULTIPLY INDEX BY TWO
ROL SCRATCH ; {6C3B} ADJUST HIBYTE CARRY
:ENTLP
LSR ]ESIZE ; {6C3B} DIVIDE ELEMENT SIZE BY 2
BCS :DOADD ; {3C2B} IF >= LOBYTE IN .A, ADD AGAIN
BNE :LP ; {3C2B}
*
STX ]IDX ; {3C2B} STORE LOBYTE
STY ]IDX+1 ; {3C2B} STORE HIBYTE
CLC ; {2C1B} CLEAR CARRY
LDA #2 ; {2C2B} ADD 2 BYTES TO INDEX
ADC ]IDX ; {4C3B} TO ACCOUNT FOR ARRAY HEADER
STA ]RES ; {3C2B} AND STORE IN RESULT
LDA #0 ; {2C2B} ACCOUNT FOR HIBYTE CARRY
ADC ]IDX+1 ; {4C3B}
STA ]RES+1 ; {3C2B}
*
** NOW ADD TO BASE ADDRESS TO GET ELEMENT ADDRESS
*
CLC ; {2C1B} CLEAR CARRY FLAG
LDA ]RES ; {2C2B} LOAD RESULT FROM EARLIER
ADC ADDR1 ; {4C3B} ADD ARRAY ADDRESS LOBYTE
STA ]RES ; {3C2B} STORE BACK IN RESULT
LDA ]RES+1 ; {2C2B} LOAD PRIOR RESULT HIBYTE
ADC ADDR1+1 ; {4C3B} ADD ARRAY ADDRESS HIBYTE
STA ]RES+1 ; {3C2B} STORE BACK IN RESULT HIBYTE
*
** NOW MOVE ELEMENT DATA TO RETURN LOCATION
*
LDY #0 ; {2C2B} RESENT INDEX
LDA ]RES ; {2C2B} LOAD ADDRESS LOBYTE
STA ADDR1 ; {3C2B} PUT IN ZERO PAGE POINTER
LDA ]RES+1 ; {2C2B} GET RESULT HIBYTE
STA ADDR1+1 ; {3C2B} PUT IN ZERO PAGE POINTER
:LDLOOP
LDA (ADDR1),Y ; {2C2B} LOAD BYTE FROM ELEMENT
STA RETURN,Y ; {3C2B} STORE IN RETURN
INY ; {2C1B} INCREASE BYTE INDEX
CPY RETLEN ; {4C2B} IF .Y <= ELEMENT SIZE
BCC :LDLOOP ; {3C2B} CONTINUE LOOPING
BEQ :LDLOOP ; {3C2B} KEEP LOOPING
*
LDX ]RES ; {2C2B} RETURN ELEMENT ADDRESS
LDY ]RES+1 ; {2C2B} IN .X (LOBYTE) AND .Y (HI)
LDA RETLEN ; {2C2B} RETURN ELEMENT LENGTH IN .A
RTS ; {6C1B}
```
---
### THE PUT81 MACRO
_SUMMARY_
| Condition | Value |
| --------------- | ------------------------------------------------------------ |
| Name | `PUT81` |
| Type | Macro |
| File | `MAC.ARR8B1D.ASM` |
| Author | Nathan Riggs |
| Last Revision | 18-MAR-2021 |
| Assembler | Merlin Pro 8 |
| OS | Apple DOS 3.3 |
| Purpose | Initialize an 8-bit, 1-dimensional array |
| Input | ]1 = Address of Source Value<br />]2 = Array Address<br />]3 = Element Index |
| Output | none |
| Dependencies | `SUB.APUT81.ASM` |
| Flags Destroyed | NZCV |
| Cycles | 216+ |
| Bytes | 131 |
| Notes | None |
| See Also | `DIM81` `ADIM81` `GET81` `AGET81` `APUT81` |
---
*DETAILS*
The `PUT81` macro inserts a value into an array's specified element, copied from another memory address. This last part is important, as it does depart in some ways from many other routines: `PUT81` **only** accepts an address, and that address either 1) is passed as a literal, meaning that the value anticipated is located at the address (direct), or 2) is passed as a regular address, meaning that the address itself holds another address that points to where the value is held (indirect). Currently, there is no way to pass a literal value to `PUT81`; all values must be held in an address prior to calling the macro. This may change in later revisions, but it is more likely that an extra macro and subroutine pair will be created for placing literal values into an element.
`LISTING 3.15: The PUT81 Macro Source`
```assembly
*
*``````````````````````````````*
* PUT81 (NATHAN RIGGS) *
* *
* PUTS THE DATA FOUND AT THE *
* GIVEN ADDRESS INTO THE ARRAY *
* AT THE GIVEN INDEX. *
* *
* PARAMETERS *
* *
* ]1 = SOURCE ADDRESS *
* ]2 = ARRAY ADDRESS *
* ]3 = ELEMENT INDEX *
* *
* CYCLES: 216+ *
* SIZE: 131 BYTES *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
PUT81 MAC
_MLIT ]1;WPAR1 ; {16C12B} PARSE SOURCE ADDRESS
_MLIT ]2;WPAR2 ; {16C12B} PARSE DEST ADDRESS
LDA ]3 ; {3C2B} DEST INDEX
STA BPAR1 ; {3C2B}
JSR APUT81 ; {178C103B}
<<<
*
```
---
### THE APUT81 SUBROUTINE
_SUMMARY_
| Condition | Value |
| --------------- | ------------------------------------------------------------ |
| Name | `APUT81` |
| Type | Subroutine |
| File | `SUB.APUT81.ASM` |
| Author | Nathan Riggs |
| Last Revision | 18-MAR-2021 |
| Assembler | Merlin Pro 8 |
| OS | Apple DOS 3.3 |
| Purpose | Initialize an 8-bit, 1-dimensional array |
| Input | WPAR1 = Source Address<br />WPAR2 = Destination Array Address<br />BPAR1 = Element Index |
| Output | none |
| Dependencies | none |
| Flags Destroyed | NZCV |
| Cycles | 172+ |
| Bytes | 100 |
| Notes | None |
| See Also | `DIM81` `ADIM81` `GET81` `AGET81` `PUT81` |
---
*DETAILS*
The `APUT81` subroutine accepts an address that contains a value to be transferred into the specified element of a given array, then copies that value appropriately. The length to be copied is determined by the array's element length, which should be already initialized by the `ADIM81` subroutine. Afterwards, `APUT81` returns the address of the element specified in **.X** (low byte) and **.Y** (high byte), with the element length held in **.A**.
`LISTING 3.16: The APUT81 Subroutine Source`
```assembly
*
*``````````````````````````````*
* APUT81 (NATHAN RIGGS) *
* *
* PUT DATA FROM SRC LOCATION *
* INTO 1D, 8BIT ARRAY AT THE *
* SPECIFIED ELEMENT. *
* *
*------------------------------*
* 8-BIT MULTIPLICATION CODE *
* ADAPTED FROM WHITE FLAME'S *
* WORK ON CODEBASE64. LICENSE *
* MAY VARY. *
*------------------------------*
* *
* INPUT: *
* *
* WPAR1 = SOURCE ADDRESS *
* WPAR2 = DESTINATION ADDRESS *
* BPAR1 = ARRAY INDEX *
* *
* DESTROY: NZCIDV *
* ^^^ ^ *
* *
* CYCLES: 172+ *
* SIZE: 100 BYTES *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]ADDRS EQU WPAR1 ; SOURCE ADDRESS
]ADDRD EQU WPAR2 ; DESTINATION
]AIDX EQU BPAR1 ; ARRAY INDEX
]SCRATCH EQU ADDR1 ; ZEROED HIBYTE
*
]ESIZE EQU VARTAB ; ELEMENT SIZE
]ESIZEBK EQU VARTAB+1 ; ^BACKUP
]ASIZE EQU VARTAB+2 ; # OF ELEMENTS
]IDX EQU VARTAB+5 ; INDEX
]RES EQU VARTAB+7 ; MULTIPLICATION RESULT
*
APUT81
LDA ]AIDX ; {2C2B} STORE IN 2 LOCATIONS
STA ]IDX ; {3C2B} FOR A BACKUP LATER
*
** MULTIPLY INDEX BY ELEM SIZE AND ADD 2
*
LDY #1 ; {2C2B} GET ELEMENT LENGTH FROM
LDA (]ADDRD),Y ; {6C2B} BYTE 1 OF ARRAY
STA ]ESIZE ; {3C2B}
STA ]ESIZEBK ; {3C2B}
LDY #0 ; {2C2B} RESET INDEX
LDA (]ADDRD),Y ; {6C2B} GET NUMBER OF ELEMENTS
STA ]ASIZE ; {3C2B} FROM ARRAY
TYA ; {2C1B} .A = 0
STY ]SCRATCH ; {3C2B} LOBYTE = 0
STY ]SCRATCH+1 ; {3C2B} HIBYTE = 0
BEQ :ENTLPA ; {3C2B} IF 0, SKIP TO LOOP
:DOADD
CLC ; {2C1B} CLEAR CARRY FLAG
ADC ]AIDX ; {4C2B} ADD INDEX LOBYTE
TAX ; {2C1B} TEMPORARILY STORE IN .X
TYA ; {2C1B} TRANSFER HIBYTE TO .A
ADC ]SCRATCH ; {4C2B} ADD HIBYTE
TAY ; {2C1B} STORE BACK IN .Y
TXA ; {2C1B} RELOAD LOBYTE TO .A
:LPA
ASL ]AIDX ; {6C3B} MUL INDEX BY TWO
ROL ]SCRATCH ; {6C3B} ADJUST HIBYTE CARRY
:ENTLPA
LSR ]ESIZE ; {6C3B} DIVIDE ELEMENT SIZE BY 2
BCS :DOADD ; {3C2B} IF >= LOBYTE IN .A, ADD AGAIN
BNE :LPA ; {3C2B}
STX ]IDX ; {3C2B} STORE LOBYTE
STY ]IDX+1 ; {3C2B} STORE HIBYTE
CLC ; {2C1B} CLEAR CARRY FLAG
LDA #2 ; {2C2B} ADD 2 BYTES TO INDEX
ADC ]IDX ; {3C2B} TO ACCOUNT FOR HEADER
STA ]RES ; {3C2B} STORE LOBYTE
LDA #0 ; {2C2B} ACCOUNT FOR HIBYTE CARRY
ADC ]IDX+1 ; {3C2B}
STA ]RES+1 ; {3C2B}
*
** ADD RESULT TO ARRAY ADDRESS TO GET ELEMENT ADDR
*
CLC ; {2C1B} CLEAR CARRY FLAG
LDA ]RES ; {3C2B} LOAD RESULT FROM EARLIER
ADC ]ADDRD ; {3C2B} ADD ARRAY ADDRESS LOBYTE
STA ]RES ; {3C2B} STORE BACK IN RESULT
LDA ]RES+1 ; {3C2B} ADD ARRAY ADDRESS HIBYTE
ADC ]ADDRD+1 ; {3C2B}
STA ]RES+1 ; {3C2B} STORE HIBYTE
*
STA ]ADDRD+1 ; {3C2B} STORE IN ZERO PAGE HIBYTE
LDA ]RES ; {3C2B} STORE LOBYTE TO ZERO PAGE
STA ]ADDRD ; {3C2B}
*
** COPY FROM SRC ADDR3 TO ELEMENT LOCATION ADDR
*
:LP
LDA (]ADDRS),Y ; {6C2B} LOAD BYTE FROM SOURCE
STA (]ADDRD),Y ; {6C2B} STORE IN ELEMENT ADDRESS
INY ; {2C1B} INCREASE BYTE INDEX
CPY ]ESIZEBK ; {4C3B} COMPARE TO ELEMENT SIZE
BNE :LP ; {3C2B} IF !=, KEEP COPYING
*
LDY ]ADDRD+1 ; {3C2B} .Y = ELEMENT ADDRESS HIBYTE
LDX ]ADDRD ; {3C2B} .X = LOBYTE
LDA ]ESIZE ; {3C2B} .A = ELEMENT SIZE
RTS ; {6C1B}
```
---
### 2-Dimensional, 8-Bit Arrays
These macros and subroutines are dedicated to using arrays with a maximum of 255 elements per dimension (in this case, two dimensions), with each element being up to 255 bytes in length. The macros for these arrays are located in the MAC.ARR8B2D.ASM file, named so after the types of macros it includes (**8** **B**ytes, **2 D**imensions).
`LISTING 3.20: MAC.ARR8B2D.ASM Header Source`
```assembly
*
*``````````````````````````````*
* MAC.ARR8B2D.ASM *
* *
* A MACRO LIBRARY FOR 8BIT, *
* 2-DIMENSIONAL ARRAYS. *
* *
* AUTHOR: NATHAN RIGGS *
* CONTACT: NATHAN.RIGGS@ *
* OUTLOOK.COM *
* *
* DATE: 18-MAR-2021 *
* ASSEMBLER: MERLIN 8 PRO *
* OS: DOS 3.3 *
* *
* SUBROUTINE FILES USED *
* *
* SUB.ADIM82 *
* SUB.AGET82 *
* SUB.APUT82 *
* *
* LIST OF MACROS *
* *
* DIM82: DIM A 2D, 8BIT ARRAY *
* GET82: GET ELEMENT IN 8BIT, *
* 2D ARRAY *
* PUT82: PUT VALUE INTO 8BIT, *
* 2D ARRAY AT INDEX *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
```
---
### THE DIM82 MACRO
_SUMMARY_
| Condition | Value |
| --------------- | ------------------------------------------------------------ |
| Name | `DIM82` |
| Type | Macro |
| File | `MAC.ARR8B2D.ASM` |
| Author | Nathan Riggs |
| Last Revision | 18-MAR-2021 |
| Assembler | Merlin Pro 8 |
| OS | Apple DOS 3.3 |
| Purpose | Initialize an 8-bit, 2-dimensional array |
| Input | ]1 = Array Address<br />]2 = First Dimension Length<br />]3 = Second Dimension Length<br />]4 = Default Fill Value |
| Output | none |
| Dependencies | `SUB.ADIM82.ASM` |
| Flags Destroyed | NZCV |
| Cycles | 355+ |
| Bytes | 228 |
| Notes | None |
| See Also | `ADIM82` `GET82` `AGET82` `PUT82` `APUT82` |
---
*DETAILS*
The `DIM82` macro initializes a two-dimensional array that can hold up to 255 elements in each dimension, with a possible element length up to 255 bytes. This macro must be used before `GET82` or `PUT82` can be used, as those macros (and their respective subroutines) expect the data structure that `DIM82` puts into place. This data structure is fairly simple: first, the first dimension (often referred to as the **X** dimension) length is stored at the array's base address, followed by the length of the second dimension (also known as the **Y** dimension). After these two values, which encompass a byte each, the size of the elements is recorded in the third byte. The actual data stored in the array is then placed after these preceding bytes in linear order. After being called, all elements should contain the fill value specified.
Note that `DIM82` can be dangerous due to the fact that there is no value-checking of the address locations it overwrites; if the programmer is not careful, critical data or even commands could be overwritten by the macro. A four-byte value is placed in `RETURN` that indicates the total amount of memory that the array fills, but it is more sensible to know this value *before* declaring the array. To do so, simply multiply the number of elements in the first dimension by the number of elements in the second dimension, then multiply that product by the element length. Add three to this value to account for the preceding length bytes of the array, and you will then have the number of bytes the array will take as a whole.
`LISTING 3.21: The DIM82 Macro Source`
```assembly
*
*``````````````````````````````*
* DIM82 (NATHAN RIGGS) *
* *
* INITIALIZES AN 8-BIT ARRAY *
* WITH TWO DIMENSIONS. *
* *
* PARAMETERS *
* *
* ]1 = ARRAY ADDRESS *
* ]2 = X DIMENSION *
* ]3 = Y DIMENSION *
* ]4 = ELEMENT SIZE *
* ]5 = FILL VALUE *
* *
* CYCLES: 355+ *
* SIZE: 228 BYTES *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
DIM82 MAC
_MLIT ]1;WPAR1 ; {16C12B} PARSE ARRAY ADDRESS
LDA ]2 ; {3C2B} X DIM
STA WPAR2 ; {3C2B}
LDA ]3 ; {3C2B} Y DIM
STA WPAR3 ; {3C2B}
LDA ]4 ; {3C2B} ELEMENT LENGTH
STA BPAR2 ; {3C2B}
LDA ]5 ; {3C2B} FILL VAL
STA BPAR1 ; {3C2B}
JSR ADIM82 ; {315C200B}
<<<
*
```
---
### THE ADIM82 SUBROUTINE
_SUMMARY_
| Condition | Value |
| --------------- | ------------------------------------------------------------ |
| Name | `ADIM82` |
| Type | Subroutine |
| File | `SUB.ADIM82.ASM` |
| Author | Nathan Riggs |
| Last Revision | 18-MAR-2021 |
| Assembler | Merlin Pro 8 |
| OS | Apple DOS 3.3 |
| Purpose | Initialize an 8-bit, 2-dimensional array |
| Input | WPAR1 = Array Address<br />WPAR2 = First Dimension Length<br />WPAR3 = Second Dimension Length<br />BPAR1 = Default Fill Value<br />BPAR2 = Element Length in Bytes<br /> |
| Output | none |
| Dependencies | none |
| Flags Destroyed | NZCV |
| Cycles | 309+ |
| Bytes | 197 |
| Notes | None |
| See Also | `DIM82` `GET82` `AGET82` `PUT82` `APUT82` |
---
*DETAILS*
The `ADIM82` Subroutine creates an array data structure with two dimensions starting at the given address, with a maximum number of elements per dimension of 255. Elements can be specified to be between 1 and 255 bytes long, and a default value is passed that will fill every byte of the array's data. It should be noted that no protections are given against `ADIM82` writing over pre-existing data or commands in memory, and thus should be used with care. The method used for finding the length of an eight-bit, two-dimensional array can be found in the `DIM82` macro listing.
`LISTING 3.22: SUB.ADIM82.ASM Subroutine Source`
```assembly
*
*``````````````````````````````*
* ADIM82 (NATHAN RIGGS) *
* *
* DECLARE THE DIMENSIONS OF A *
* NEW 8-BIT, 2D ARRAY. *
* *
*------------------------------*
* 8-BIT MULTIPLICATION CODE *
* ADAPTED FROM WHITE FLAME'S *
* WORK ON CODEBASE64. LICENSE *
* MAY VARY. *
*------------------------------*
* *
* INPUT: *
* *
* WPAR1 = ARRAY ADDRESS *
* WPAR2 = 1ST DIM LENGTH *
* WPAR3 = 2ND DIM LENGTH *
* BPAR1 = FILL VALUE *
* BPAR2 = ELEMENT LENGTH *
* *
* DESTROY: NZCIDV *
* ^^^ ^ *
* *
* CYCLES: 309+ *
* SIZE: 197 BYTES *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]ADDR EQU WPAR1 ; ARRAY ADDRESS
]AXSIZE EQU WPAR2 ; FIRST DIM # OF ELEMENTS
]AYSIZE EQU WPAR3 ; SECOND DIM # OF ELEMENTS
]FILL EQU BPAR1 ; FILL VALUE
]ESIZE EQU BPAR2 ; ELEMENT SIZE
*
]PROD EQU VARTAB ; PRODUCT
]AXBAK EQU VARTAB+4 ; ARRAY X SIZE BACKUP
]AYBAK EQU VARTAB+5 ; ARRAY Y SIZE BACKUP
]MLIER EQU VARTAB+6 ; MULTIPLIER
]MCAND EQU VARTAB+8 ; MULTIPLICAND, ELEMENT SIZE
*
ADIM82
LDA ]ESIZE ; {2C2B} ELEMENT LENGTH
STA ]MCAND ; {3C2B} AND STORE AS MULTIPLICAND
LDA ]AYSIZE ; {2C2B} GET ARRAY Y SIZE
STA ]AYBAK ; {3C2B} BACK IT UP
LDA ]AXSIZE ; {2C2B}
STA ]AXBAK ; {3C2B} AND BACK THAT UP TOO
LDA #0 ; {2C2B} CLEAR MCAND HIBYTE
STA ]MCAND+1 ; {3C2B}
*
** MULTIPLY X AND Y
*
TAY ; {2C1B} AND LOBYTE
STY SCRATCH ; {3C2B}
BEQ :ENTLP ; {3C2B} IF ZERO, SKIP TO LOOP
:DOADD
CLC ; {2C1B} CLEAR CARRY FLAG
ADC ]AXSIZE ; {5C3B} ADD X LENGTH
TAX ; {2C1B} TEMPORARILY STORE IN .X
TYA ; {2C1B} TRANSFER HIBYTE TO .A
ADC SCRATCH ; {5C3B} ADD HIBYTE
TAY ; {2C1B} STORE BACK IN .Y
TXA ; {2C1B} RELOAD LOBYTE INTO .A
:LP
ASL ]AXSIZE ; {6C3B} MULTIPLY X LENGTH BY 2
ROL SCRATCH ; {6C3B} ADJUST HIBYTE
:ENTLP
LSR ]AYSIZE ; {6C3B} DIVIDE Y LENGTH BY 2
BCS :DOADD ; {3C2B} IF >= LOBYTE IN .A,
BNE :LP ; {3C2B} ADD AGAIN; OTHERWISE, LOOP
STX ]MLIER ; {3C2B} STORE LOBYTE IN MULTIPLIER
STY ]MLIER+1 ; {3C2B} STORE HIBYTE IN MULTIPLIER
*
** NOW MULTIPLY BY LENGTH OF ELEMENTS
*
LDA #0 ; {2C2B} CLEAR PRODUCT LOBYTE
STA ]PROD ; {3C2B}
STA ]PROD+1 ; {3C2B} CLEAR NEXT BYTE
STA ]PROD+2 ; {3C2B} CLEAR NEXT BYTE
STA ]PROD+3 ; {3C2B} CLEAR HIBYTE
LDX #$10 ; {2C2B} LOAD $10 IN .X (#16)
:SHIFTR LSR ]MLIER+1 ; {6C3B} DIVIDE MLIER BY TWO
ROR ]MLIER ; {6C3B} ADJUST LOBYTE
BCC :ROTR ; {3C2B} IF LESS THAN PRODUCT, ROTATE
LDA ]PROD+2 ; {2C2B} LOAD PRODUCT 3RD BYTE
CLC ; {2C1B} CLEAR CARRY
ADC ]MCAND ; {5C3B} ADD MULTIPLICAND
STA ]PROD+2 ; {3C2B} STORE BACK INTO PRODUCT 3RD BYTE
LDA ]PROD+3 ; {2C2B} LOAD PRODUCT HIBYTE
ADC ]MCAND+1 ; {5C3B} ADD MULTIPLICAND HIBYTE
:ROTR
ROR ; {6C3B} ROTATE .A RIGHT
STA ]PROD+3 ; {3C2B} STORE IN PRODUCT HIBYTE
ROR ]PROD+2 ; {6C3B} ROTATE PRODUCT 3RD BYTE
ROR ]PROD+1 ; {6C3B} ROTATE PRODUCT 2ND BYTE
ROR ]PROD ; {6C3B} ROTATE PRODUCT LOBYTE
DEX ; {2C1B} DECREMENT COUNTER
BNE :SHIFTR ; {3C2B} IF NOT 0, BACK TO SHIFTER
*
LDA ]PROD ; {2C2B} LOAD PRODUCT LOBYTE TO .A
CLC ; {2C1B} CLEAR CARRY FLAG
ADC #3 ; {5C3B} ADD 3
STA ]PROD ; {3C2B} STORE BACK INTO PRODUCT LOBYTE
LDA ]PROD+1 ; {2C2B}
ADC #0 ; {5C3B} INITIATE CARRY FOR 2ND BYTE
STA ]PROD+1 ; {3C2B}
LDA ]PROD+2 ; {2C2B}
ADC #0 ; {5C3B} AND THIRD BYTE
STA ]PROD+2 ; {3C2B}
*
** NOW CLEAR MEMORY BLOCKS, WHOLE PAGES FIRST
*
LDA ]FILL ; {2C2B} GET FILL VALUE
LDX ]PROD+1 ; {2C2B} LOAD SECOND BYTE OF PRODUCT
BEQ :PART ; {3C2B} IF 0, THEN ONLY PARTIAL PAGE
LDY #0 ; {2C2B} CLEAR INDEX
:FULL
STA (]ADDR),Y ; {3C2B} COPY FILL BYTE TO ADDRESS
INY ; {2C1B} INCREASE INDEX
BNE :FULL ; {3C2B} IF NO OVERFLOW, KEEP FILL
INC ]ADDR+1 ; {6C3B} INCREASE ADDRESS HIBYTE
DEX ; {2C1B} DECREMENT COUNTER
BNE :FULL ; {3C2B} LOOP UNTIL PAGES DONE
:PART
LDX ]PROD ; {2C2B} LOAD PRODUCT LOBYTE TO X
BEQ :MFEXIT ; {3C2B} IF ZERO, THEN WE'RE DONE
LDY #0 ; {2C2B} RESET INDEX
:PARTLP
STA (]ADDR),Y ; {6C2B} STORE FILL BYTE
INY ; {2C1B} INCREASE INDEX
DEX ; {2C1B} DECREASE COUNTER
BNE :PARTLP ; {3C2B} LOOP UNTIL DONE
:MFEXIT
LDY #0 ; {2C2B} RESET INDEX
LDA ]AXBAK ; {2C2B} PUT X LENGTH INTO
STA (]ADDR),Y ; {6C2B} FIRST BYTE OF ARRAY
INY ; {2C1B} INCREMENT INDEX
LDA ]AYBAK ; {2C2B} PUT Y LENGTH INTO
STA (]ADDR),Y ; {6C2B} SECOND BYTE OF ARRAY
INY ; {2C1B} INCREMENT INDEX
LDA ]MCAND ; {2C2B} PUT ELEMENT SIZE
STA (]ADDR),Y ; {6C2B} INTO 3RD BYTE OF ARRAY
LDX ]ADDR ; {2C2B} RETURN ARRAY ADDR LOBYTE IN .X
LDY ]ADDR+1 ; {2C2B} RETURN ARRAY ADDR HIBYTE IN .Y
LDA ]PROD ; {2C2B} STORE PRODUCT LOBYTE IN RETURN
STA RETURN ; {3C2B}
LDA ]PROD+1 ; {2C2B} STORE NEXT BYTE
STA RETURN+1 ; {3C2B}
LDA ]PROD+2 ; {2C2B} NEXT BYTE
STA RETURN+2 ; {3C2B}
LDA ]PROD+3 ; {2C2B} STORE HIBYTE
STA RETURN+3 ; {3C2B}
LDA #4 ; {2C2B} SIZE OF RETURN
STA RETLEN ; {3C2B} SPECIFY RETURN LENGTH
LDA ]MCAND ; {2C2B} RETURN ELEMENT SIZE IN .A
RTS ; {6C3B}
```
---
### THE GET82 MACRO
_SUMMARY_
| Condition | Value |
| --------------- | ------------------------------------------------------------ |
| Name | `GET82` |
| Type | Macro |
| File | `MAC.ARR8B2D.ASM` |
| Author | Nathan Riggs |
| Last Revision | 18-MAR-2021 |
| Assembler | Merlin Pro 8 |
| OS | Apple DOS 3.3 |
| Purpose | Retrieve an element from an 8-bit, 2-dimensional array |
| Input | ]1 = Array Address<br />]2 = First Dimension Index<br />3] = Second Dimension Index |
| Output | none |
| Dependencies | `SUB.AGET82.ASM` |
| Flags Destroyed | NZCV |
| Cycles | 340+ |
| Bytes | 255 |
| Notes | None |
| See Also | `DIM82` `ADIM82` `AGET82` `PUT82` `APUT82` |
---
*DETAILS*
The `GET82` macro retrieves a value from a given element at the X,Y position (first dimension, second dimension) in an eight-bit, two-dimensional array. The value retrieved is stored in `RETURN`, with the corresponding byte-length of the value stored in `RETLEN`. The length of the value is also passed back via the **.A** register, and the physical address where the element is stored is returned via the **.X** and **.Y** registers (low byte and high byte, respectively).
`LISTING 3.23: The GET82 Macro Source`
```assembly
*
*``````````````````````````````*
* GET82 (NATHAN RIGGS) *
* *
* RETRIEVE VALUE FROM ELEMENT *
* OF 8-BIT, TWO DIMENSIONAL *
* ARRAY. *
* *
* PARAMETERS *
* *
* ]1 = ARRAY ADDRESS *
* ]2 = X INDEX *
* ]3 = Y INDEX *
* *
* CYCLES: 340+ *
* SIZE: 222 BYTES *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
GET82 MAC
_MLIT ]1;WPAR1 ; {16C12B}
LDA ]2 ; {3C2B} X INDEX
STA BPAR1 ; {3C2B}
LDA ]3 ; {3C2B} Y INDEX
STA BPAR2 ; {3C2B}
JSR AGET82 ; {312C192B}
<<<
*
```
---
### THE AGET82 SUBROUTINE
_SUMMARY_
| Condition | Value |
| --------------- | ------------------------------------------------------------ |
| Name | `AGET82` |
| Type | Subroutine |
| File | `SUB.AGET82.ASM` |
| Author | Nathan Riggs |
| Last Revision | 18-MAR-2021 |
| Assembler | Merlin Pro 8 |
| OS | Apple DOS 3.3 |
| Purpose | Retrieve element from an 8-bit, 2-dimensional array |
| Input | WPAR1 = Array Address<br />BPAR1 = First Dimension Index<br />BPAR2 = Second Dimension Index |
| Output | none |
| Dependencies | none |
| Flags Destroyed | NZCV |
| Cycles | 306+ |
| Bytes | 189 |
| Notes | None |
| See Also | `DIM82` `ADIM82` `GET82` `PUT82` `APUT82` |
---
*DETAILS*
The `AGET82` subroutine retrieves an element value from a two-dimension, eight-bit array, returning the value in `RETURN` with its length byte in `RETLEN`. Note that the array in question must first have been initialized with the `DIM82` macro or the `ADIM82` subroutine in order to work properly, unless the exact same data structure is created by the programmer without the aid of the `DIM` functionality. Note also that `GET82`, like all array subroutines, does not do any error-checking: requesting an element that is out-of-bounds will result in trash being returned, if the system does not crash in the first place. A programmer should always be careful to manage array boundaries on her own, like memory management in general.
`LISTING 3.24: The AGET82 Subroutine Source`
```assembly
*
*``````````````````````````````*
* AGET82 (NATHAN RIGGS) *
* *
* RETRIEVE AN ELEMENT VALUE *
* FROM AN 8-BIT, 2D ARRAY AND *
* HOLD IT IN THE RETURN *
* ADDRESS, WITH ITS LENGTH IN *
* RETLEN. *
* *
*------------------------------*
* 8-BIT MULTIPLICATION CODE *
* ADAPTED FROM WHITE FLAME'S *
* WORK ON CODEBASE64. LICENSE *
* MAY VARY. *
*------------------------------*
* *
* INPUT: *
* *
* WPAR1 = ARRAY ADDRESS *
* BPAR1 = 1ST DIM INDEX *
* BPAR2 = 2ND DIM INDEX *
* *
* DESTROY: NZCIDV *
* ^^^ ^ *
* *
* CYCLES: 306+ *
* SIZE: 189 BYTES *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]ADDR EQU WPAR1 ; ARRAY ADDRESS
]XIDX EQU BPAR1 ; 1ST DIMENSION INDEX
]YIDX EQU BPAR2 ; 2ND DIMENSION INDEX
*
]XLEN EQU VARTAB+0 ; X DIMENSION LENGTH
]YLEN EQU VARTAB+2 ; Y DIMENSION LENGTH
]PROD EQU VARTAB+4 ; PRODUCT
]MLIER EQU VARTAB+8 ; MULTIPLIER
]MCAND EQU VARTAB+10 ; MULTIPLICAND
]ELEN EQU VARTAB+12 ; ELEMENT LENGTH
]PBAK EQU VARTAB+14 ; PRODUCT BACKUP
*
AGET82
LDY #0 ; {2C2B} RESET INDEX
LDA (]ADDR),Y ; {6C2B} GET X-LENGTH FROM ARRAY
STA ]XLEN ; {3C2B}
LDY #1 ; {2C2B} INCREMENT INDEX
LDA (]ADDR),Y ; {6C2B} GET Y-LENGTH FROM ARRAY
STA ]YLEN ; {3C2B}
LDY #2 ; {2C2B} INCREMENT INDEX
LDA (]ADDR),Y ; {6C2B} GET ELEMENT LENGTH FROM ARRAY
STA ]ELEN ; {3C2B}
*
** MULTIPLY Y-INDEX BY Y-LENGTH
*
LDA #0 ; {2C2B} RESET LOBYTE
TAY ; {2C1B} RESET HIBYTE
STY SCRATCH ; {3C2B} SAVE HIBYTE IN SCRATCH
BEQ :ENTLP ; {3C2B} IF ZERO, SKIP TO LOOP
:DOADD
CLC ; {2C1B} CLEAR CARRY FLAG
ADC ]YIDX ; {4C3B} ADD Y-INDEX
TAX ; {2C1B} TEMPORARILY STORE IN .X
TYA ; {2C1B} LOAD HIBYTE TO .A
ADC SCRATCH ; {4C3B} ADD HIBYTE
TAY ; {2C1B} TRANSFER BACK INTO .Y
TXA ; {2C1B} RELOAD LOBYTE
:LP
ASL ]YIDX ; {6C3B} MULTIPLY Y-INDEX BY 2
ROL SCRATCH ; {6C3B} DEAL WITH HIBYTE
:ENTLP
LSR ]YLEN ; {6C3B} DIVIDE Y-LENGTH BY 2
BCS :DOADD ; {3C2B} IF >= LOBYTE IN .A, ADD AGAIN
BNE :LP ; {3C2B} ELSE, LOOP
STX ]PBAK ; {3C2B} STORE LOBYTE IN PRODUCT BACKUP
STY ]PBAK+1 ; {3C2B} STORE HIBYTE
*
** NOW MULTIPLY LENGTH OF ELEMENTS BY XIDX
*
LDA ]XIDX ; {3C2B} PUT X-INDEX INTO
STA ]MLIER ; {3C2B} MULTIPLIER
LDA ]ELEN ; {3C2B} ELEMENT LENGTH INTO
STA ]MCAND ; {3C2B} MULTIPLICAND
LDA #0 ; {2C2B} RESET PRODUCT LOBYTE
STA ]MLIER+1 ; {3C2B} RESET MULTIPLIER HIBYTE
STA ]MCAND+1 ; {3C2B} RESET MULTIPLICAND HIBYTE
STA ]PROD ; {3C2B}
STA ]PROD+1 ; {3C2B} RESET PRODUCT 2ND BYTE
STA ]PROD+2 ; {3C2B} RESET PRODUCT 3RD BYTE
STA ]PROD+3 ; {3C2B} RESET PRODUCT HIBYTE
LDX #$10 ; {2C2B} LOAD $10 INTO .X (#16)
:SHIFTR LSR ]MLIER+1 ; {6C3B} DIVIDE MULTIPLIER BY 2
ROR ]MLIER ; {6C3B} ADJUST LOBYTE
BCC :ROTR ; {3C2B} IF < PRODUCT, ROTATE
LDA ]PROD+2 ; {3C2B} LOAD PRODUCT 3RD BYTE
CLC ; {2C1B} CLEAR CARRY FLAG
ADC ]MCAND ; {4C3B} ADD MULTIPLICAND
STA ]PROD+2 ; {3C2B} STORE BACK INTO 3RD
LDA ]PROD+3 ; {3C2B} LOAD HIBYTE
ADC ]MCAND+1 ; {4C3B} ADD MULTIPLICAND HIBYTE
:ROTR
ROR ; {6C3B} ROTATE .A RIGHT
STA ]PROD+3 ; {3C2B} STORE IN PRODUCT HIBYTE
ROR ]PROD+2 ; {6C3B} ROTATE PRODUCT 3RD BYTE
ROR ]PROD+1 ; {6C3B} ROTATE PRODUCT 2ND BYTE
ROR ]PROD ; {6C3B} ROTATE PRODUCT LOBYTE
DEX ; {2C1B} DECREMENT COUNTER
BNE :SHIFTR ; {3C2B} IF NOT 0, BACK TO SHIFTER
LDA ]PROD ; {3C2B} LOAD PRODUCT LOBYTE
CLC ; {2C1B} CLEAR CARRY FLAG
ADC #3 ; {2C2B} INCREASE BY 3
STA ]PROD ; {3C2B} STORE BACK INTO LOBYTE
LDA ]PROD+1 ; {3C2B} ACCOUNT FOR CARRIES
ADC #0 ; {2C2B}
STA ]PROD+1 ; {3C2B}
*
** NOW ADD THAT TO EARLIER CALC
*
CLC ; {2C1B} CLEAR CARRY FLAG
LDA ]PROD ; {3C2B} LOAD PRODUCT LOBYTE
ADC ]PBAK ; {4C3B} ADD PREVIOUS PRODUCT
STA ]PROD ; {3C2B} STORE NEW PRODUCT LOBYTE
LDA ]PROD+1 ; {3C2B} LOAD PRODUCT HIBYTE
ADC ]PBAK+1 ; {4C3B} ADD PREV PRODUCT HIBYTE
STA ]PROD+1 ; {3C2B} STORE PRODUCT HIBYTE
*
** NOW ADD ARRAY ADDRESS TO GET INDEX ADDR
*
CLC ; {2C1B} CLEAR CARRY FLAG
LDA ]PROD ; {3C2B} LOAD PRODUCT LOBYTE
ADC ]ADDR ; {4C3B} ADD ARRAY ADDRESS LOBYTE
STA ]PROD ; {3C2B} STORE BACK IN PRODUCT LOBYTE
LDA ]PROD+1 ; {3C2B} LOAD HIBYTE
ADC ]ADDR+1 ; {4C3B} ADD ADDRESS HIBYTE
STA ]PROD+1 ; {3C2B} STORE IN PRODUCT HIBYTE
*
LDY ]PROD ; {3C2B} LOAD PRODUCT LOBYTE IN .Y
LDX ]PROD+1 ; {3C2B} LOAD HIBYTE IN .X FOR SOME REASON
STY ]ADDR ; {3C2B} TRANSFER TO ZERO PAGE
STX ]ADDR+1 ; {3C2B}
LDY #0 ; {2C2B} RESET INDEX
:RLP
LDA (]ADDR),Y ; {6C2B} LOAD BYTE
STA RETURN,Y ; {3C2B} STORE IN RETURN
INY ; {2C1B} INCREASE INDEX
CPY ]ELEN ; {4C3B} IF INDEX != ELEMENT LENGTH
BNE :RLP ; {3C2B} THEN KEEP COPYING
LDA ]ELEN ; {3C2B} OTHERWISE, STORE ELEMENT LENGTH
STA RETLEN ; {3C2B} INTO RETURN LENGTH
LDA RETLEN ; {3C2B} AND IN .A
LDX ]ADDR ; {3C2B} RETURN ARRAY ADDRESS LOBYTE IN .X
LDY ]ADDR+1 ; {3C2B} RETURN HIBYTE IN .Y
RTS ; {6C1B}
```
---
### THE PUT82 MACRO
_SUMMARY_
| Condition | Value |
| --------------- | ------------------------------------------------------------ |
| Name | `PUT82` |
| Type | Macro |
| File | `MAC.ARR8B2D.ASM` |
| Author | Nathan Riggs |
| Last Revision | 18-MAR-2021 |
| Assembler | Merlin Pro 8 |
| OS | Apple DOS 3.3 |
| Purpose | Write value to element in an 8-bit, 2-dimensional array |
| Input | ]1 = Source Address<br />]2 = Destination Array Address<br />]3 = First Dimension Index<br />]4 = Second Dimension Index |
| Output | none |
| Dependencies | `SUB.APUT82.ASM` |
| Flags Destroyed | NZCV |
| Cycles | 352+ |
| Bytes | 223 |
| Notes | None |
| See Also | `DIM82` `ADIM82` `GET82` `AGET82` `APUT82` |
---
*DETAILS*
The `PUT82` macro inserts the value given in a source memory address range into the specified X,Y element of the destination array's address. The number of bytes copied is determined by the array's element length attribute. As with other array routines, there is no error-checking for out-of-bounds requests; it is up to the programmer to keep track of an array's boundaries once they are set. Using `PUT82` with out-of-bounds dimensional indices will either overwrite important data stored already or will cause a fatal crash. Additionally note that this macro only accepts an address for the value to be copied, *not* a literal value; this too is in common with other array `PUT` macros and subroutines.
After being called, the address of the element in question is returned via the **.X** and **.Y** registers (the low and high byte of the address, respectively), and the element length is returned in the **.A** register.
`LISTING 3.25: The PUT82 Macro Source`
```assembly
*
*``````````````````````````````*
* PUT82 (NATHAN RIGGS) *
* *
* SET VALUE OF AN ELEMENT IN *
* AN 8-BIT, TWO-DIMENSIONAL *
* ARRAY. *
* *
* PARAMETERS *
* *
* ]1 = SOURCE ADDRESS *
* ]2 = DEST ARRAY ADDRESS *
* ]3 = ELEMENT X INDEX *
* ]4 = Y INDEX *
* *
* CYCLES: 352+ *
* SIZE: 223 BYTES *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
PUT82 MAC
_MLIT ]1;WPAR1 ; {16C12B} PARSE SOURCE ADDRESS
_MLIT ]2;WPAR2 ; {16C12B} PARSE DEST ADDRESS
LDA ]3 ; {3C2B} X INDEX
STA BPAR1 ; {3C2B}
LDA ]4 ; {3C2B} Y INDEX
STA BPAR2 ; {3C2B}
JSR APUT82 ; {308C191B}
<<<
*
```
---
### THE APUT82 SUBROUTINE
_SUMMARY_
| Condition | Value |
| --------------- | ------------------------------------------------------------ |
| Name | `APUT82` |
| Type | Subroutine |
| File | `SUB.APUT82.ASM` |
| Author | Nathan Riggs |
| Last Revision | 18-MAR-2021 |
| Assembler | Merlin Pro 8 |
| OS | Apple DOS 3.3 |
| Purpose | Write a value into an 8-bit, 1-dimensional array's element |
| Input | WPAR1 = Source Address<br />WPAR2 = Destination Array Address<br />BPAR1 = First Dimension Index<br />BPAR2 = Second Dimension Index |
| Output | none |
| Dependencies | none |
| Flags Destroyed | NZCV |
| Cycles | 302+ |
| Bytes | 188 |
| Notes | None |
| See Also | `DIM82` `ADIM82` `GET82` `AGET82` `PUT82` |
---
*DETAILS*
The `APUT82` subroutine takes a series of bytes found in a source address and places them into the given X,Y element of the specified eight-bit, two-dimensional array. As stated in the associated macro's entry (`PUT82`), note that 1) no error-checking is involved, thus the boundaries are not protected, and 2) a value can only be transferred from another memory location to the desired array element; that is, direct "putting" into the element cannot be accomplished with this subroutine.
Once finished, the subroutine returns the beginning address of the element in the **.X** register (low byte) and **.Y** register (high byte), with the element length returned in the **.A** register.
`LISTING 3.26: The APUT82 Subroutine Source`
```assembly
*
*``````````````````````````````*
* APUT82 (NATHAN RIGGS) *
* *
* PUT DATA FROM SOURCE INTO *
* A 2D, 8BIT ARRAY ELEMENT. *
* *
* INPUT: *
* *
* WPAR1 = SOURCE ADDRESS *
* WPAR2 = ARRAY ADDRESS *
* BPAR1 = 1ST DIM INDEX *
* BPAR2 = 2ND DIM INDEX *
* *
* DESTROY: NZCIDV *
* ^^^ ^ *
* *
* CYCLES: 302+ *
* SIZE: 188 BYTES *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]ADDRS EQU WPAR1 ; SOURCE ADDRESS
]ADDRD EQU WPAR2 ; ARRAY ADDRESS
]XIDX EQU BPAR1 ; X INDEX
]YIDX EQU BPAR2 ; Y INDEX
*
]ESIZE EQU VARTAB ; ELEMENT LENGTH
]MCAND EQU VARTAB+1 ; MULTIPLICAND
]MLIER EQU VARTAB+3 ; MULTIPLIER
]PROD EQU VARTAB+5 ; PRODUCT
]XLEN EQU VARTAB+9 ; ARRAY X-LENGTH
]YLEN EQU VARTAB+13 ; ARRAY Y-LENGTH
]PBAK EQU VARTAB+15 ; PRODUCT BACKUP
*
APUT82
LDY #0 ; {2C2B} RESET INDEX
LDA (]ADDRD),Y ; {6C2B} GET ARRAY X-LENGTH
STA ]XLEN ; {3C2B}
LDY #1 ; {2C2B} INCREMENT INDEX
LDA (]ADDRD),Y ; {6C2B} GET ARRAY Y-LENGTH
STA ]YLEN ; {3C2B}
LDY #2 ; {2C2B} INCREMENT INDEX
LDA (]ADDRD),Y ; {6C2B} GET ARRAY ELEMENT LENGTH
STA ]ESIZE ; {3C2B}
*
** MULTIPLY Y-INDEX BY Y-LENGTH
*
LDA #0 ; {2C2B} RESET LOBYTE
TAY ; {2C1B} RESET HIBYTE
STY SCRATCH ; {3C2B} SAVE HIBYTE IN SCRATCH
BEQ :ENTLP ; {3C2B} IF ZERO, SKIP TO LOOP
:DOADD
CLC ; {2C1B} CLEAR CARRY FLAG
ADC ]YIDX ; {4C3B} ADD Y-INDEX
TAX ; {2C1B} STORE IN .X
TYA ; {2C1B} LOAD HIBYTE
ADC SCRATCH ; {4C3B} ADD HIBYTE
TAY ; {2C1B} STORE IN .Y
TXA ; {2C1B} RELOAD LOBYTE
:LP
ASL ]YIDX ; {6C3B} MULTIPLY Y-INDEX BY 2
ROL SCRATCH ; {6C3B} DEAL WITH HIBYTE
:ENTLP
LSR ]YLEN ; {6C3B} DIVIDE Y-LENGTH BY 2
BCS :DOADD ; {3C2B} IF >= LOBYTE, ADD AGAIN
BNE :LP ; {3C2B} ELSE, LOOP
STX ]PBAK ; {3C2B} STORE LOBYTE IN PRODUCT BACKUP
STY ]PBAK+1 ; {3C2B} STORE HIBYTE
LDA ]XIDX ; {3C2B} PUT X-INDEX INTO MULTIPLIER
STA ]MLIER ; {3C2B}
LDA #0 ; {2C2B} RESET HIBYTE
STA ]MLIER+1 ; {3C2B} TRANSFER HIBYTE
LDA ]ESIZE ; {3C2B} PUT ELEMENT LENGTH
STA ]MCAND ; {3C2B} INTO MULTIPLICAND
LDA #0 ; {2C2B} RESET HIBYTE
STA ]MCAND+1 ; {3C2B}
*
** NOW MULTIPLY XIDX BY ELEMENT LENGTH
*
STA ]PROD ; {3C2B} RESET PRODUCT LOBYTE
STA ]PROD+1 ; {3C2B} RESET 2ND BYTE
STA ]PROD+2 ; {3C2B} RESET 3RD BYTE
STA ]PROD+3 ; {3C2B} RESET HIBYTE
LDX #$10 ; {2C2B} LOAD $10 INTO .X (#16)
:SHIFTR LSR ]MLIER+1 ; {6C3B} DIVIDE MULTIPLIER BY 2
ROR ]MLIER ; {6C3B} DEAL WITH HIBYTE
BCC :ROTR ; {3C2B} IF < RODUCT, ROTATE
LDA ]PROD+2 ; {3C2B} LOAD PRODUCT 3RD BYTE
CLC ; {2C1B} CLEAR CARRY FLAG
ADC ]MCAND ; {4C3B} ADD MULTIPLICAND
STA ]PROD+2 ; {3C2B} STORE 3RD BYTE
LDA ]PROD+3 ; {3C2B} LOAD HIBYTE
ADC ]MCAND+1 ; {4C3B} ADD MULTIPLICAND HIBYTE
:ROTR
ROR ; {6C3B} ROTATE .A RIGHT
STA ]PROD+3 ; {3C2B} STORE IN PRODUCT HIBYTE
ROR ]PROD+2 ; {6C3B} ROTATE PRODUCT 3RD BYTE
ROR ]PROD+1 ; {6C3B} ROTATE RODUCT 2ND
ROR ]PROD ; {6C3B} ROTATE LOBYTE
DEX ; {2C1B} DECREMENT COUNTER
BNE :SHIFTR ; {3C2B} IF NOT 0, BACK TO SHIFTER
*
** NOW ADD PRODUCT TO REST
*
LDA ]PBAK ; {3C2B} LOAD FIRST PRODUCT LOBYTE
CLC ; {2C1B} CLEAR CARRY FLAG
ADC ]PROD ; {4C3B} ADD 2ND PRODUCT LOBYTE
STA ]PROD ; {3C2B} STORE NEW PRODUCT LOBYTE
LDA ]PBAK+1 ; {3C2B} LOAD FIRST PRODUCT HIBYTE
ADC ]PROD+1 ; {4C3B} ADD 2ND HIBYTE
STA ]PROD+1 ; {3C2B} STORE HIBYTE
LDA ]PROD ; {3C2B} LOAD NEW PRODUCT LOBYTE
CLC ; {2C1B} CLEAR CARRY FLAG
ADC #3 ; {4C3B} INCREASE BY 3
STA ]PROD ; {3C2B} STORE IN LOBYTE
LDA ]PROD+1 ; {3C2B} APPLY CARRY TO HIBYTE
ADC #0 ; {2C2B}
STA ]PROD+1 ; {3C2B}
*
** ADD ARRAY ADDRESS TO GET INDEX
*
CLC ; {2C1B} CLEAR CARRY FLAG
LDA ]PROD ; {3C2B} LOAD PRODUCT LOBYTE
ADC ]ADDRD ; {4C3B} ADD ARRAY ADDRESS LOBYTE
STA ]PROD ; {3C2B} STORE IN PRODUCT
LDA ]PROD+1 ; {3C2B} LOAD PRODUCT HIBYTE
ADC ]ADDRD+1 ; {4C3B} ADD ARRAYH ADDRESS HIBYTE
STA ]PROD+1 ; {3C2B} STORE HIBYTE
LDX ]PROD ; {3C2B} PUT ELEMENT ADDRESS LOBYTE IN .X
LDY ]PROD+1 ; {3C2B} PUT HIBYTE IN Y
STX ADDR2 ; {3C2B} STORE IN ZERO PAGE
STY ADDR2+1 ; {3C2B}
LDY #0 ; {2C2B} RESET INDEX
*
** COPY FROM SRC ADDR TO DEST ADDR
*
:CLP
LDA (]ADDRS),Y ; {6C2B} GET BYTE FROM SOURCE
STA (ADDR2),Y ; {3C2B} STORE IN ELEMENT
INY ; {2C1B} INCREASE INDEX
CPY ]ESIZE ; {4C3B} IF < ELEMENT SIZE,
BNE :CLP ; {3C2B} CONTINUE COPYING
LDX ADDR2 ; {3C2B} PUT ELEMENT LOBYTE IN .X
LDY ADDR2+1 ; {3C2B} PUT HIBYTE IN .Y
LDA ]ESIZE ; {3C2B} PUT ELEMENT SIZE IN .A
RTS ; {6C1B}
```
---
### 1-Dimensional, 16-Bit Arrays
These macros and subroutines are dedicated to using arrays with a maximum of 65,025 elements per dimension (in this case, one dimension), with each element being up to 255 bytes in length. The macros for these arrays are located in the MAC.ARR16B1D.ASM file, named so after the types of macros it includes (**16** **B**ytes, **1 D**imensions).
If it is not obvious: a single array with an element size of one byte each can easily take up more memory than most Apple II computers can contain. The macros and subroutines listed here are inadequate for implementations that require such large arrays, as they would also (usually) need to access extra memory provided by third-party hardware. Even in normal 128k systems like the Apple //c, these macros and subroutines cannot utilize memory to its fullest extent; while bank switching can be enacted by the programmer, an array created with this collection cannot fill part of one bank and continue to another.
`LISTING 3.30: MAC.ARR16B1D.ASM Header Source`
```assembly
*
*``````````````````````````````*
* MAC.ARR16B1D.ASM *
* *
* A MACRO LIBRARY FOR 16-BIT *
* 1-DIMENSIONAL ARRAYS. *
* *
* AUTHOR: NATHAN RIGGS *
* CONTACT: NATHAN.RIGGS@ *
* OUTLOOK.COM *
* *
* DATE: 18-MAR-2021 *
* ASSEMBLER: MERLIN 8 PRO *
* OS: DOS 3.3 *
* *
* SUBROUTINE FILES USED *
* *
* SUB.ADIM161 *
* SUB.AGET161 *
* SUB.APUT161 *
* *
* LIST OF MACROS *
* *
* DIM161: DIM 1D, 16BIT ARRAY *
* GET161: GET ELEMENT FROM 1D, *
* 16BIT ARRAY. *
* PUT161: PUT VALUE INTO A 1D, *
* 16BIT ARRAY INDEX. *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
```
---
### THE DIM161 MACRO
_SUMMARY_
| Condition | Value |
| --------------- | ------------------------------------------------------------ |
| Name | `DIM161` |
| Type | Macro |
| File | `MAC.ARR16B1D.ASM` |
| Author | Nathan Riggs |
| Last Revision | 18-MAR-2021 |
| Assembler | Merlin Pro 8 |
| OS | Apple DOS 3.3 |
| Purpose | Initialize a 16-bit, 1-dimensional array |
| Input | ]1 = Array Address<br />]2 = Number of Elements<br />]3 = Element Byte Length<br />]4 = Default Fill Value |
| Output | none |
| Dependencies | `SUB.ADIM161.ASM` |
| Flags Destroyed | NZCV |
| Cycles | 271+ |
| Bytes | 165 |
| Notes | none |
| See Also | `ADIM161` `GET161` `AGET161` `PUT161` `APUT161` |
---
*DETAILS*
The `DIM161` macro initializes a one-dimensional array that can hold up to 65,025 elements, with each element being less than 255 bytes long. Note that not only can this macro easily fill the entire available memory on many Apple II machines, but it can also accidentally write over data or program statements that are important to the overall stability and functioning of the program and the computer. As such, the programmer should calculate the required amount of memory necessary for the `DIM161` array and make sure the space is clear prior to actually calling the macro. This can be done by multiplying the number of elements being declared by the given element length, then adding to account for the 3-byte header of the array. The header consists of three bytes that hold the number of elements and the size of the elements. The first two bytes indicate the number of elements as a 16-bit number (big-endian) and the third byte indicates the length of each element, which can vary from 1 to 255.
After finishing initialization, the macro returns the total length of the array, in bytes, in `RETURN` (16-bit value). While this might be useful information after the fact, it does nothing to solve the problem of knowing the array's complete size before execution.
`LISTING 3.31: The DIM161 Macro Source`
```assembly
*
*``````````````````````````````*
* DIM161 (NATHAN RIGGS) *
* *
* INITIALIZE A 16-BIT ARRAY *
* WITH A SINGLE DIMENSION. *
* *
* PARAMETERS *
* *
* ]1 = ARRAY ADDRESS *
* ]2 = ARRAY BYTE LENGTH *
* ]3 = ELEMENT BYTE LENGTH *
* ]4 = ARRAY FILL VALUE *
* *
* CYCLES: 271+ *
* SIZE: 165 BYTES *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
DIM161 MAC
_MLIT ]1;WPAR1 ; {16C12B} PARSE ARRAY ADDRESS
_MLIT ]2;WPAR2 ; {16C12B} PARSE BYTE LENGTH
LDA ]3 ; {3C2B} ELEMENT LENGTH
STA WPAR3 ; {3C2B}
LDA ]4 ; {3C2B} FILL VALUE
STA BPAR1 ; {3C2B}
JSR ADIM161 ; {227C133B}
<<<
*
```
---
### THE ADIM161 SUBROUTINE
_SUMMARY_
| Condition | Value |
| --------------- | ------------------------------------------------------------ |
| Name | `ADIM161` |
| Type | Subroutine |
| File | `SUB.ADIM161.ASM` |
| Author | Nathan Riggs |
| Last Revision | 18-MAR-2021 |
| Assembler | Merlin Pro 8 |
| OS | Apple DOS 3.3 |
| Purpose | Initialize a single-dimension, 16-bit array |
| Input | WPAR1 = Array Address<br />WPAR2 = Number of Elements<br />WPAR3 = Element Byte-length<br />BPAR1 = Default Fill Value |
| Output | none |
| Dependencies | none |
| Flags Destroyed | NZCV |
| Cycles | 221+ |
| Bytes | 130 |
| Notes | None |
| See Also | `DIM161` `GET161` `AGET161` `PUT161` `APUT161` |
---
*DETAILS*
The `ADIM161` subroutine creates the data structure for a 16-bit, one-dimensional array, which can hold up to 65,025 elements of a length between 1 and 255 bytes. Please see the listing for the `DIM161` macro for a short description of how the routine works as well as any necessary precautions to be considered.
`LISTING 3.32: The ADIM161 Subroutine Source`
```assembly
*
*``````````````````````````````*
* ADIM161 (NATHAN RIGGS) *
* *
* INITIALIZE A 16BIT, 1D ARRAY *
* *
*------------------------------*
* MULTIPLICATION CODE ADAPTED *
* FROM WHITE FLAME'S WORK ON *
* CODEBASE64. LICENSE MAY VARY *
*------------------------------*
* *
* INPUT: *
* *
* WPAR1 = ARRAY ADDRESS *
* WPAR2 = # OF ELEMENTS *
* WPAR3 = ELEMENT LENGTH *
* BPAR1 = FILL VALUE *
* *
* DESTROY: NZCIDV *
* ^^^ ^ *
* *
* CYCLES: 221+ *
* SIZE: 130 BYTES *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]ADDRD EQU WPAR1 ; ARRAY ADDRESS
]ASIZE EQU WPAR2 ; # OF ELEMENTS
]ESIZE EQU WPAR3 ; ELEMENT BYTE LENGTH
]FILL EQU BPAR1 ; FILL VALUE
*
]MSIZE EQU VARTAB ; TOTAL ARRAY BYTES
]ASZBAK EQU VARTAB+4 ; BACKUP OF ELEMENT #
]ESZBAK EQU VARTAB+7 ; BACKUP
*
ADIM161
LDA ]ESIZE ; {3C2B} ELEMENT SIZE
STA ]ESZBAK ; {3C2B} ELEMENT LENGTH BACKUP
LDA ]ASIZE ; {3C2B}
STA ]ASZBAK ; {3C2B} ARRAY SIZE BACKUP
LDA ]ASIZE+1 ; {3C2B}
STA ]ASZBAK+1 ; {3C2B} BACKUP
STA SCRATCH ; {3C2B} HIBYTE FOR MULTIPLICATION
LDA ]ADDRD ; {3C2B}
STA ADDR2 ; {3C2B}
LDA ]ADDRD+1 ; {3C2B}
STA ADDR2+1 ; {3C2B}
LDY #0 ; {2C2B} CLEAR INDEX
LDA #0 ; {2C2B} CLEAR ACCUMULATOR
BEQ :ENTLP ; {3C2B} IF 0, SKIP TO LOOP
*
** MULTIPLY ARRAY SIZE BY ELEMENT SIZE
*
:DOADD
CLC ; {2C1B} CLEAR CARRY FLAG
ADC ]ASIZE ; {4C3B} ADD ARRAY SIZE
TAX ; {2C1B} HOLD IN .X
TYA ; {2C1B} LOAD HIBYTE
ADC SCRATCH ; {4C3B} ADD HIBYTE
TAY ; {2C1B} HOLD IN .Y
TXA ; {2C1B} RELOAD LOBYTE
:LP
ASL ]ASIZE ; {6C3B} MULTIPLY ARRAY SIZE BY 2
ROL SCRATCH ; {6C3B} ADJUST HIBYTE
:ENTLP
LSR ]ESIZE ; {6C3B} DIVIDE ELEMENT SIZE BY 2
BCS :DOADD ; {3C2B} IF >= LOBYTE IN .A,
BNE :LP ; {3C2B} ADD AGAIN--ELSE, LOOP
CLC ; {2C1B} CLEAR CARRY
TXA ; {2C1B} LOBYTE TO .A
ADC #3 ; {4C3B} ADD 2 FOR HEADER
STA ]MSIZE ; {3C2B} STORE IN TOTAL LOBYTE
TYA ; {2C1B} HIBYTE TO .A
ADC #0 ; {4C3B} DO CARRY
STA ]MSIZE+1 ; {3C2B} STORE IN TOTAL HIBYTE
*
** CLEAR MEMORY BLOCKS
*
LDA ]FILL ; {3C2B} GET FILL VALUE
LDX ]MSIZE+1 ; {3C2B} LOAD TOTAL SIZE LOBYTE
BEQ :PART ; {3C2B} IF NO WHOLE PAGES, JUST PART
LDY #0 ; {2C2B} RESET INDEX
:FULL
STA (]ADDRD),Y ; {6C2B} COPY BYTE TO ADDRESS
INY ; {2C1B} NEXT BYTE
BNE :FULL ; {3C2B} LOOP UNTIL PAGE DONE
INC ]ADDRD+1 ; {6C2B} GO TO NEXT PAGE
DEX ; {2C1B} DECREMENT COUNTER
BNE :FULL ; {3C2B} LOOP IF PAGES LEFT
:PART
LDX ]MSIZE ; {3C2B} PARTIAL PAGE BYTES
BEQ :MFEXIT ; {3C2B} EXIT IF = 0
LDY #0 ; {2C2B} RESET INDEX
:PARTLP
STA (]ADDRD),Y ; {6C2B} STORE BYTE
INY ; {2C1B} INCREMENT INDEX
DEX ; {2C1B} DECREMENT COUNTER
BNE :PARTLP ; {3C2B} LOOP UNTIL DONE
:MFEXIT
LDY #0 ; {2C2B} RESET INDEX
LDA ]ASZBAK ; {3C2B} STORE ARRAY SIZE IN HEADER
STA (ADDR2),Y ; {6C2B}
INY ; {2C1B} INCREASE INDEX
LDA ]ASZBAK+1 ; {3C2B} STORE ARRAY SIZE HIBYTE
STA (ADDR2),Y ; {6C2B}
INY ; {2C1B} INCREMENT INDEX
LDA ]ESZBAK ; {3C2B} STORE ELEMENT SIZE
STA (ADDR2),Y ; {6C2B} IN HEADER
LDX ]ADDRD ; {3C2B} .X HOLDS ARRAY ADDRESS LOBYTE
LDY ]ADDRD+1 ; {3C2B} .Y HOLDS HIBYTE
LDA ]MSIZE ; {3C2B} STORE TOTAL ARRAY SIZE
STA RETURN ; {3C2B} IN RETURN
LDA ]MSIZE+1 ; {3C2B}
STA RETURN+1 ; {3C2B}
LDA #2 ; {2C2B}
STA RETLEN ; {3C2B} 2 BYTE LENGTH
LDA ]ASZBAK ; {3C2B} .A HOLDS # OF ELEMENTS
RTS ; {6C1B}
```
---
### THE GET161 MACRO
_SUMMARY_
| Condition | Value |
| --------------- | ------------------------------------------------------- |
| Name | `GET161` |
| Type | Macro |
| File | `MAC.ARR16B1D.ASM` |
| Author | Nathan Riggs |
| Last Revision | 18-MAR-2021 |
| Assembler | Merlin Pro 8 |
| OS | Apple DOS 3.3 |
| Purpose | Get an element value from a 16-bit, 1-dimensional array |
| Input | ]1 = Array Address<br />]2 = Element Index |
| Output | none |
| Dependencies | `SUB.AGET161.ASM` |
| Flags Destroyed | NZCV |
| Cycles | 223+ |
| Bytes | 130 |
| Notes | none |
| See Also | `DIM161` `ADIM161` `AGET161` `PUT161` `APUT161` |
---
*DETAILS*
The `GET161` macro retrieves a value from a desired element in a one-dimensional, 16-bit array. After retrieval, the data is stored in `RETURN`, with the byte length of the data held in `RETLEN` (the length is determined by the array's element length attribute). Additionally, the element length is held in **.A** after execution, and the address of the given element is held in the **.X** and **.Y** registers (low byte and high byte of address, respectively).
While requesting an out-of-bounds element is unlikely to freeze the system on its own (unless a soft switch happens to be in the area accessed), using the trash data collected from such an attempt may lead to crashes. As such, the programmer should take care to always know the bounds of the given array.
`LISTING 3.33: The GET161 Macro`
```assembly
*
*``````````````````````````````*
* GET161 (NATHAN RIGGS) *
* *
* GET THE VALUE STORED IN THE *
* ELEMENT OF A 16-BIT, ONE- *
* DIMENSIONAL ARRAY. *
* *
* PARAMETERS *
* *
* ]1 = SOURCE ADDRESS *
* ]2 = ARRAY ADDRESS *
* *
* CYCLES: 223+ *
* SIZE: 130 BYTES *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
GET161 MAC
_MLIT ]1;WPAR1 ; {16C12B} PARSE SOURCE ADDRESS
_MLIT ]2;WPAR2 ; {16C12B} PARSE INDEX
JSR AGET161 ; {191C116B}
<<<
*
```
---
### THE AGET161 SUBROUTINE
_SUMMARY_
| Condition | Value |
| --------------- | -------------------------------------------------------- |
| Name | `AGET161` |
| Type | Subroutine |
| File | `SUB.AGET161.ASM` |
| Author | Nathan Riggs |
| Last Revision | 18-MAR-2021 |
| Assembler | Merlin Pro 8 |
| OS | Apple DOS 3.3 |
| Purpose | read an element value in a 16-bit, one-dimensional array |
| Input | WPAR1 = Array Address<br />WPAR2 = Element Index |
| Output | none |
| Dependencies | none |
| Flags Destroyed | NZCV |
| Cycles | 185+ |
| Bytes | 113 |
| Notes | None |
| See Also | `DIM161` `ADIM161` `GET161` `PUT161` `APUT161` |
---
*DETAILS*
The `AGET161` subroutine receives its parameters from `WPAR1` and `WPAR2`, where the Array Address and the Element Index is held (respectively), then retrieves the value in the given array's indexed element. This data is held in `RETURN`, with the length of the value (in bytes) held in both `RETLEN` and the **.A** register. Note that like all array `GET` macros and subroutines, this follows the TITO rule: trash in, trash out. If an element is requested that is out of bounds for the given array, then a trash value will be returned--an error will not interrupt the flow of execution. Thus, the programmer is responsible for keeping track of all array boundaries.
`LISTING 3.34: The AGET161 Subroutine Source`
```assembly
*
*``````````````````````````````*
* AGET161 (NATHAN RIGGS) *
* *
* GET DATA IN 16-BIT, 2D ARRAY *
* *
*------------------------------*
* MULTIPLICATION CODE ADAPTED *
* FROM WHITE FLAME'S WORK ON *
* CODEBASE64. LICENSE MAY VARY *
*------------------------------*
* *
* INPUT: *
* *
* WPAR1 = ARRAY ADDRESS *
* WPAR2 = ELEMENT INDEX *
* *
* DESTROY: NZCIDV *
* ^^^ ^ *
* *
* CYCLES: 185+ *
* SIZE: 113 BYTES *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]AIDX EQU WPAR2 ; ELEMENT INDEX
]ADDR EQU WPAR1 ; ARRAY ADDRESS
*
]ESIZE EQU VARTAB ; ELEMENT LENGTH
]ESIZEB EQU VARTAB+1 ; ^BACKUP
]ASIZE EQU VARTAB+2 ; NUMBER OF ELEMENTS
]IDX EQU VARTAB+6 ; INDEX BACKUP
*
AGET161
LDA ]AIDX ; {3C2B}
STA ]IDX ; {3C2B}
LDA ]AIDX+1 ; {3C2B} GET INDEX HIBYTE
STA ]AIDX+1 ; {3C2B}
STA SCRATCH ; {3C2B}
LDY #0 ; {2C2B} RESET INDEX
LDA (]ADDR),Y ; {6C2B} GET NUMBER OF
STA ]ASIZE ; {3C2B} ARRAY ELEMENTS
LDY #1 ; {2C2B} GET HIBYTE OF
LDA (]ADDR),Y ; {6C2B} # OF ARRAY ELEMENTS
STA ]ASIZE+1 ; {3C2B}
INY ; {2C1B} INCREASE BYTE INDEX
LDA (]ADDR),Y ; {6C2B} GET ELEMENT LENGTH
STA ]ESIZE ; {3C2B}
STA ]ESIZEB ; {3C2B}
*
** MULTIPLY INDEX BY ELEMENT SIZE, ADD 3
*
LDY #0 ; {2C2B} RESET .Y AND .A
LDA #0 ; {2C2B}
BEQ :ENTLPA ; {3C2B} IF ZERO, SKIP TO LOOP
:DOADD
CLC ; {2C1B} CLEAR CARRY
ADC ]AIDX ; {4C3B} ADD INDEX TO .A
TAX ; {2C1B} HOLD IN .X
TYA ; {2C1B} LOAD HIBYTE
ADC SCRATCH ; {4C3B} ADD HIBYTE
TAY ; {2C1B} HOLD IN .Y
TXA ; {2C1B} RELOAD LOBYTE
:LPA
ASL ]AIDX ; {6C3B} MULTIPLY INDEX BY 2
ROL SCRATCH ; {6C3B} ADJUST HIBYTE
:ENTLPA
LSR ]ESIZE ; {6C3B} DIVIDE ELEMENT LENGTH BY 2
BCS :DOADD ; {3C2B} IF BIT1 SHIFTED IN CARRY +MORE
BNE :LPA ; {3C2B} CONTINUE LOOPING IF Z FLAG UNSET
STX ]IDX ; {3C2B} STORE LOBYTE
STY ]IDX+1 ; {3C2B} STORE HIBYTE
LDA #3 ; {2C2B} ADD 3 TO INDEX LOBYTE
CLC ; {2C1B} CLEAR CARRY
ADC ]IDX ; {4C3B}
STA ADDR2 ; {3C2B} STORE ON ZERO PAGE
LDA ]IDX+1 ; {3C2B} ADJUST HIBYTE
ADC #0 ; {2C2B}
STA ADDR2+1 ; {3C2B}
*
LDA ADDR2 ; {3C2B} ADD ARRAY ADDRESS
CLC ; {2C1B}
ADC ]ADDR ; {4C3B} LOBYTE
STA ADDR2 ; {3C2B}
LDA ADDR2+1 ; {3C2B} HIBYTE
ADC ]ADDR+1 ; {4C3B}
STA ADDR2+1 ; {3C2B}
LDY #0 ; {2C2B} RESET BYTE INDEX
:LP
LDA (ADDR2),Y ; {6C2B} GET BYTE FROM ELEMENT
STA RETURN,Y ; {3C2B} PUT INTO RETURN
INY ; {2C1B} INCREASE BYTE INDEX
CPY ]ESIZEB ; {4C3B} IF INDEX != ELEMENT LENGTH
BNE :LP ; {3C2B} CONTINUE LOOP
LDA ]ESIZEB ; {3C2B} .A = ELEMENT SIZE
STA RETLEN ; {3C2B} STORE IN RETLEN
LDY ADDR2+1 ; {3C2B} .Y = ELEMENT ADDRESS HIBYTE
LDX ADDR2 ; {3C2B} .X = ELEMENT ADDRESS LOBYTE
RTS ; {6C1B}
```
---
### THE PUT161 MACRO
_SUMMARY_
| Condition | Value |
| --------------- | ------------------------------------------------------------ |
| Name | `PUT161` |
| Type | Macro |
| File | `MAC.ARR16B1D.ASM` |
| Author | Nathan Riggs |
| Last Revision | 18-MAR-2021 |
| Assembler | Merlin Pro 8 |
| OS | Apple DOS 3.3 |
| Purpose | Copy a value to an element in a 16-bit, 1-dimensional array |
| Input | ]1 = Source Address<br />]2 = Array Address<br />]3 = Element Index |
| Output | none |
| Dependencies | `SUB.APUT161.ASM` |
| Flags Destroyed | NZCV |
| Cycles | 238+ |
| Bytes | 148 |
| Notes | none |
| See Also | `DIM161` `ADIM161` `GET161` `AGET161` `APUT161` |
---
*DETAILS*
The `PUT161` macro copies a value from a memory address and places it in the specified element of the given 16-bit, one-dimensional array. It should be noted that this macro does not accept a literal value to be placed in the array--only the address of a value is accepted. Additionally, there is no error-checking; thus, if the programmer enters an out-of-bounds element index, it is possible that the subroutine will write over data or commands that should not be written over, possibly causing the computer to crash.
After executing, the **.A** register holds the array's element byte length, as well as the physical memory location of the index in question in **.X** and **.Y** (low byte and high byte of address, respectively).
`LISTING 3.35: The PUT161 Macro Source`
```assembly
*
*``````````````````````````````*
* PUT161 (NATHAN RIGGS) *
* *
* SET THE VALUE OF AN INDEX *
* ELEMENT IN A 16-BIT, ONE- *
* DIMENSIONAL ARRAY. *
* *
* PARAMETERS *
* *
* ]1 = SOURCE ADDRESS *
* ]2 = ARRAY ADDRESS *
* ]3 = ELEMENT INDEX *
* *
* CYCLES: 238+ *
* SIZE: 148 BYTES *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
PUT161 MAC
_MLIT ]1;WPAR1 ; {16C12B} PARSE SOURCE ADDRESS
_MLIT ]2;WPAR2 ; {16C12B} PARSE ARRAY ADDRESS
_MLIT ]3;WPAR3 ; {16C12B} PARSE INDEX
JSR APUT161 ; {190C112B}
<<<
*
```
---
### THE APUT161 SUBROUTINE
_SUMMARY_
| Condition | Value |
| --------------- | ------------------------------------------------------------ |
| Name | `APUT161` |
| Type | Subroutine |
| File | `SUB.APUT161.ASM` |
| Author | Nathan Riggs |
| Last Revision | 18-MAR-2021 |
| Assembler | Merlin Pro 8 |
| OS | Apple DOS 3.3 |
| Purpose | Put a value in the specified element of a 16-bit, one-dimensional array |
| Input | WPAR1 = Source Address<br />WPAR2 = Destination Array Address<br />WPAR3 = Destination Element Index |
| Output | none |
| Dependencies | none |
| Flags Destroyed | NZCV |
| Cycles | 184+ |
| Bytes | 109 |
| Notes | None |
| See Also | `DIM161` `ADIM161` `GET161` `AGET161``APUT161` |
---
*DETAILS*
The `APUT161` subroutine takes the value from a given source address range of bytes and puts that data into a specified element of a 16-bit, one-dimensional array. Afterward, the array's element byte-length is held in the .A register, with the element's physical address passed back via the **.X** and **.Y** registers (low byte and high byte, respectively).
`LISTING 3.36: The APUT161 Subroutine Source`
```assembly
*
*``````````````````````````````*
* APUT161 (NATHAN RIGGS) *
* *
*------------------------------*
* MULTIPLICATION CODE ADAPTED *
* FROM WHITE FLAME'S WORK ON *
* CODEBASE64. MICENSE MAY VARY *
*------------------------------*
* *
* INPUT: *
* *
* WPAR1 = SOURCE ADDRESS *
* WPAR2 = ARRAY ADDRESS *
* WPAR3 = ELEMENT INDEX *
* *
* DESTROY: NZCIDV *
* ^^^ ^ *
* *
* CYCLES: 184+ *
* SIZE: 109 BYTES *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]ADDRS EQU WPAR1 ; SOURCE ADDRESS
]ADDRD EQU WPAR2 ; ARRAY DESTINATION ADDRESS
]AIDX EQU WPAR3 ; ELEMENT INDEX
*
]ESIZE EQU VARTAB ; ELEMENT SIZE
]ESIZEB EQU VARTAB+1 ; ^BACKUP
]ASIZE EQU VARTAB+2 ; NUMBER OF ELEMENTS
]IDX EQU VARTAB+6 ; ANOTHER INDEX
*
APUT161
LDA ]AIDX ; {3C2B}
STA ]IDX ; {3C2B}
LDA ]AIDX+1 ; {3C2B}
STA ]IDX+1 ; {3C2B}
STA SCRATCH ; {3C2B}
LDY #0 ; {2C2B} RESET BYTE COUNTER
LDA (]ADDRD),Y ; {6C2B} GET NUMBER OF ELEMENTS
STA ]ASIZE ; {3C2B} LOBYTE
LDY #1 ; {2C2B} INCREMENT INDEX
LDA (]ADDRD),Y ; {6C2B} GET NUMBER OF ELEMENTS
STA ]ASIZE+1 ; {3C2B} HIBYTE
INY ; {2C1B} INCREMENT INDEX
LDA (]ADDRD),Y ; {6C2B} GET ELEMENT LENGTH
STA ]ESIZE ; {3C2B}
STA ]ESIZEB ; {3C2B} BACKUP
*
** MULTIPLY INDEX BY ELEMENT SIZE, THEN ADD 3
*
LDY #0 ; {2C2B} RESET LOBYTE
LDA #0 ; {2C2B} AND HIBYTE
BEQ :ENTLPA ; {3C2B} SKIP TO LOOP
:DOADD
CLC ; {2C1B} CLEAR CARRY
ADC ]AIDX ; {4C3B} ADD INDEX LOBYTE
TAX ; {2C1B} HOLD IN .X
TYA ; {2C1B} LOAD HIBYTE
ADC SCRATCH ; {4C3B} ADD HIBYTE
TAY ; {2C1B} HOLD BACK IN .Y
TXA ; {2C1B} RETURN LOBYTE TO .A
:LPA
ASL ]AIDX ; {6C2B} MULTIPLY INDEX BY 2
ROL SCRATCH ; {6C2B} ADJUST HIBYTE
:ENTLPA
LSR ]ESIZE ; {6C2B} DIVIDE ELEM LENGTH BY 2
BCS :DOADD ; {3C2B} IF 1 SHIFTED TO CARRY, ADD AGAIN
BNE :LPA ; {3C2B} CONTINUE LOOP IF ZERO UNSET
STX ]IDX ; {3C2B} LOBYTE IN .X
STY ]IDX+1 ; {3C2B} HIBYTE IN .Y
CLC ; {2C1B}
LDA #3 ; {2C2B} ADD 3 TO LOBYTE
ADC ]IDX ; {4C3B}
STA ADDR2 ; {3C2B} STORE ON ZERO PAGE
LDA ]IDX+1 ; {3C2B} ADJUST HIBYTE
ADC #0 ; {4C3B}
STA ADDR2+1 ; {3C2B}
*
CLC ; {2C1B} CLEAR CARRY
LDA ADDR2 ; {3C2B} ADD ARRAY ADDRESS
ADC ]ADDRD ; {4C3B} LOBYTE
STA ADDR2 ; {3C2B} ADD ARRAY ADDRESS
LDA ADDR2+1 ; {3C2B} HIBYTE
ADC ]ADDRD+1 ; {4C3B}
STA ADDR2+1 ; {3C2B}
LDY #0 ; {2C2B}
:LP
*
** OOPS; NEED TO CONVERT THIS TO 16 BITS
*
LDA (]ADDRS),Y ; {6C2B} GET BYTE FROM SOURCE
STA (ADDR2),Y ; {3C2B} STORE IN ELEMENT
INY ; {2C1B} INCREMENT BYTE INDEX
CPY ]ESIZEB ; {4C3B} IF INDEX != ELEMENT LENGTH
BNE :LP ; {3C2B} KEEP LOOPING
LDY ADDR2+1 ; {3C2B} HIBYTE OF ELEMENT ADDRESS
LDX ADDR2 ; {3C2B} LOBYTE
LDA ]ESIZEB ; {3C2B} .A = ELEMENT SIZE
RTS ; {6C1B}
```
---
### 2-Dimensional, 16-Bit Arrays
These macros and subroutines are dedicated to using arrays with a maximum of 65,025 elements per dimension (in this case, two dimensions), with each element being up to 255 bytes in length. The macros for these arrays are located in the MAC.ARR16B2D.ASM file, named so after the types of macros it includes (**16** **B**ytes, **2 D**imensions).
**WARNING:** using 16-bit two-dimensional arrays can fill up memory *fast*. As an example, imagine have an array with 255 elements in the first dimension and 255 elements in the second dimension, with only two bytes per element. To calculate the amount of memory required for this, we multiply the elements on the first dimension by the elements in the second dimension, then multiply that by the number of bytes in each element--two. We then add four more bytes for the array header, and we have the total number that the array will take: 130,054 bytes! This is more memory than any Apple II computer has without special additional hardware (except the IIGS), and the limits of the subroutine are not even close to being met. Special care must be taken to control the size of an array *before* the array is even declared.
`LISTING 3.40: MAC.ARR16B2D.ASM Header Source`
```assembly
*
*``````````````````````````````*
* MAC.ARR16B2D.ASM *
* *
* A MACRO LIBRARY FOR 16-BIT, *
* 2-DIMENSIONAL ARRAYS. *
* *
* AUTHOR: NATHAN RIGGS *
* CONTACT: NATHAN.RIGGS@ *
* OUTLOOK.COM *
* *
* DATE: 18-MAR-2021 *
* ASSEMBLER: MERLIN 8 PRO *
* OS: DOS 3.3 *
* *
* SUBROUTINE FILES USED *
* *
* SUB.ADIM162 *
* SUB.AGET162 *
* SUB.APUT162 *
* *
* LIST OF MACROS *
* *
* DIM162: DIM 2D, 16BIT ARRAY *
* GET162: GET ELEMENT FROM 2D, *
* 16BIT ARRAY. *
* PUT162: PUT VALUE INTO A 2D, *
* 16BIT ARRAY INDEX. *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
```
---
### THE ADIM162 MACRO
_SUMMARY_
| Condition | Value |
| --------------- | ------------------------------------------------------------ |
| Name | `DIM162` |
| Type | Macro |
| File | `MAC.ARR16B2D.ASM` |
| Author | Nathan Riggs |
| Last Revision | 18-MAR-2021 |
| Assembler | Merlin Pro 8 |
| OS | Apple DOS 3.3 |
| Purpose | Initialize a 16-bit, two-dimensional array |
| Input | ]1 = Array Address<br />]2 = First Dimension Element Count<br />]3 = Second Dimension Element Count<br />]4 = Byte size of Elements<br />]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<br />WPAR2 = Second Dimension Length<br />WPAR3 = Array Address<br />BPAR1 = Element Byte Length<br />BPAR2 = Default Fill Value |
| Output | none |
| Dependencies | none |
| Flags Destroyed | NZCV |
| Cycles | 398+ |
| Bytes | 226 |
| Notes | None |
| See Also | `DIM162` `GET162` `AGET162` `PUT162` `APUT162` |
---
*DETAILS*
The `ADIM162` subroutine declares and initializes a 16-bit, two-dimensional array. This is a fairly simple data type, though it is also usually a fundamental data type as well. At the starting two bytes of the array, the length of the first dimension is stored, with the lowest byte of the address coming first (big-endian); then the next two bytes are dedicated to the length of the second dimension. Finally, the fifth byte holds the length of each element in the array, and every byte afterwards holds the actual data placed in each element of the array.
After executing, `ADIM162` holds the total array size in bytes in `RETURN`, with the length of the value in `RETLEN`.
`LISTING 3.42: The ADIM162 Subroutine Source`
```assembly
*
*``````````````````````````````*
* ADIM162 (NATHAN RIGGS) *
* *
* INITIALIZE A 1-DIMENSIONAL, *
* 16-BIT ARRAY. *
* *
*------------------------------*
* MULTIPLICATION ADAPTED FROM *
* WHITE FLAME'S WORK ON *
* CODEBASE64. LICENSE MAY VARY *
*------------------------------*
* *
* INPUT: *
* *
* WPAR1 = 1ST DIM LENGTH *
* WPAR2 = 2ND DIM LENGTH *
* WPAR3 = ARRAY ADDRESS *
* BPAR1 = ELEMENT LENGTH *
* BPAR2 = FILL VALUE *
* *
* DESTROY: NZCIDV *
* ^^^ ^ *
* *
* CYCLES: 398+ *
* SIZE: 226 BYTES *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]AXSIZE EQU WPAR1 ; FIRST DIMENSION LENGTH
]AYSIZE EQU WPAR2 ; SECOND DIMENSION LENGTH
]ELEN EQU BPAR1 ; ELEMENT BYTE LENGTH
]FILL EQU BPAR2 ; FILL VALUE
]ADDR EQU WPAR3 ; ARRAY ADDRESS
]ADDR2 EQU ADDR1 ; ZERO-PAGE WORKING SPACE
*
]PROD EQU VARTAB ; PRODUCT
]AXBAK EQU VARTAB+4 ; X SIZE BACKUP
]AYBAK EQU VARTAB+6 ; Y SIZE BACKUP
]MLIER EQU VARTAB+8 ; MULTIPLIER
]MCAND EQU VARTAB+10 ; MULTIPLICAND
*
ADIM162
LDA ]AYSIZE ; {3C2B}
STA ]AYBAK ; {3C2B}
STA ]MCAND ; {3C2B}
LDA ]AYSIZE+1 ; {3C2B}
STA ]AYBAK+1 ; {3C2B}
STA ]MCAND+1 ; {3C2B}
LDA ]AXSIZE ; {3C2B}
STA ]AXBAK ; {3C2B}
STA ]MLIER ; {3C2B}
LDA ]AXSIZE+1 ; {3C2B}
STA ]AXBAK+1 ; {3C2B}
STA ]MLIER+1 ; {3C2B}
LDA ]ADDR ; {3C2B} GET ARRAY ADDRESS
STA ]ADDR2 ; {3C2B} LOBYTE; PUT IN ZERO PAGE
LDA ]ADDR+1 ; {3C2B} GET ARRAY ADDRESS HIBYTE
STA ]ADDR2+1 ; {3C2B}
*
** MULTIPLY X AND Y
*
LDA #0 ; {2C2B} RESET HIBYTE,LOBYTE
STA ]PROD+2 ; {3C2B} CLEAR PRODUCT BYTE 3
STA ]PROD+3 ; {3C2B} CLEAR PRODUCT BYTE 4
LDX #$10 ; {2C2B} (#16)
:SHIFT_R
LSR ]MLIER+1 ; {6C2B} DIVIDE MLIER BY TWO
ROR ]MLIER ; {6C2B} ADJUST LOBYTE
BCC :ROT_R ; {3C2B} IF 0 IN CARRY, ROTATE MORE
LDA ]PROD+2 ; {3C2B} GET 3RD BYTE OF PRODUCT
CLC ; {2C1B}
ADC ]MCAND ; {4C3B} ADD MULTIPLICAND
STA ]PROD+2 ; {3C2B} STORE 3RD BYTE
LDA ]PROD+3 ; {3C2B} LOAD 4TH BYTE
ADC ]MCAND+1 ; {4C3B} ADD MULTIPLICAND HIBYTE
:ROT_R
ROR ; {6C2B} ROTATE PARTIAL PRODUCT
STA ]PROD+3 ; {3C2B} STORE IN HIBYTE
ROR ]PROD+2 ; {6C2B} ROTATE THIRD BYTE
ROR ]PROD+1 ; {6C2B} ROTATE 2ND BYTE
ROR ]PROD ; {6C2B} ROTATE LOBYTE
DEX ; {2C1B} DECREASE COUNTER
BNE :SHIFT_R ; {3C2B} IF NOT ZERO, BACK TO SHIFTER
*
LDA ]ELEN ; {3C2B} PUT ELEMENT LENGTH
STA ]MCAND ; {3C2B} INTO MULTIPLICAND
LDA #0 ; {2C2B} CLEAR HIBYTE
STA ]MCAND+1 ; {3C2B}
LDA ]PROD ; {3C2B} LOAD EARLIER PRODUCT
STA ]MLIER ; {3C2B} STORE LOBYTE IN MULTIPLIER
LDA ]PROD+1 ; {3C2B} DO SAME FOR HIBYTE
STA ]MLIER+1 ; {3C2B}
*
** NOW MULTIPLY BY LENGTH OF ELEMENTS
*
LDA #0 ; {2C2B} CLEAR PRODUCT
STA ]PROD ; {3C2B}
STA ]PROD+1 ; {3C2B}
STA ]PROD+2 ; {3C2B}
STA ]PROD+3 ; {3C2B}
LDX #$10 ; {2C2B}
:SHIFTR LSR ]MLIER+1 ; {6C2B} SHIFT BYTES LEFT (/2)
ROR ]MLIER ; {6C2B} ADJUST LOBYTE
BCC :ROTR ; {3C2B} IF CARRY = 0, ROTATE
LDA ]PROD+2 ; {3C2B} LOAD 3RD BYTE OF PRODUCT
CLC ; {2C1B}
ADC ]MCAND ; {4C3B} ADD MULTIPLICAND
STA ]PROD+2 ; {3C2B} STORE IN 3RD BYTE
LDA ]PROD+3 ; {3C2B} LOAD HIBYTE
ADC ]MCAND+1 ; {4C3B} ADD MULTIPLICAND HIBYTE
:ROTR
ROR ; {6C2B} ROTATE .A RIGHT
STA ]PROD+3 ; {3C2B} ROTATE 4TH
ROR ]PROD+2 ; {6C2B} ROTATE 3RD
ROR ]PROD+1 ; {6C2B} ROTATE 2ND
ROR ]PROD ; {6C2B} ROTATE LOBYTE
DEX ; {2C1B} DECREMENT COUNTER
BNE :SHIFTR ; {3C2B}IF NOT 0, BACK TO SHIFTER
*
CLC ; {2C1B} CLEAR CARRY
LDA ]PROD ; {3C2B} INCREASE BY 5
ADC #5 ; {2C2B}
STA ]PROD ; {3C2B} SAVE LOBYTE
LDA ]PROD+1 ; {3C2B}
ADC #0 ; {2C2B}
STA ]PROD+1 ; {3C2B} SAVE HIBYTE
*
** NOW CLEAR MEMORY BLOCKS, WHOLE PAGES FIRST
*
LDA ]FILL ; {3C2B} GET FILL VALUE
LDX ]PROD+1 ; {3C2B} LOAD PRODUCT 2ND BYTE
BEQ :PART ; {3C2B} IF 0, THEN PARTIAL PAGE
LDY #0 ; {2C2B} CLEAR INDEX
:FULL
STA (]ADDR),Y ; {6C2B} COPY FILL BYTE TO ADDRESS
INY ; {2C1B} INCREASE BYTE COUNTER
BNE :FULL ; {3C2B} LOOP UNTIL PAGES DONE
INC ]ADDR+1 ; {6C2B} INCREASE HIBYTE
DEX ; {2C1B} DECREASE COUNTER
BNE :FULL ; {3C2B} LOOP UNTIL PAGES DONE
*
** NOW DO REMAINING BYTES
*
:PART
LDX ]PROD ; {3C2B} LOAD PRODUCT LOBYTE IN X
BEQ :MFEXIT ; {3C2B} IF 0, THEN WE'RE DONE
LDY #0 ; {2C2B} CLEAR BYTE INDEX
:PARTLP
STA (]ADDR),Y ; {6C2B} STORE FILL BYTE
INY ; {2C1B} INCREASE BYTE INDEX
DEX ; {2C1B} DECREASE COUNTER
BNE :PARTLP ; {3C2B} LOOP UNTIL DONE
:MFEXIT
LDY #0 ; {2C2B} CLEAR BYTE INDEX
LDA ]AXBAK ; {3C2B} LOAD ORIGINAL X LENGTH
STA (]ADDR2),Y ; {6C2B} STORE IN ARRAY HEADER
INY ; {2C1B} INCREASE BYTE COUNTER
LDA ]AXBAK+1 ; {3C2B} STORE HIBYTE
STA (]ADDR2),Y ; {6C2B}
INY ; {2C1B} INCREASE BYTE INDEX
LDA ]AYBAK ; {3C2B} LOAD Y LENGTH LOBYTE
STA (]ADDR2),Y ; {6C2B} STORE IN ARRAY HEADER
INY ; {2C1B} INCREMENT BYTE INDEX
LDA ]AYBAK+1 ; {3C2B} STORE Y HIBYTE
STA (]ADDR2),Y ; {6C2B}
INY ; {2C1B} INCREMENT BYTE INDEX
LDA ]ELEN ; {3C2B} STORE ELEMENT LENGTH
STA (]ADDR2),Y ; {6C2B}
*
LDY ]ADDR2 ; {3C2B} LOBYTE OF ARRAY ADDRESS
LDX ]ADDR2+1 ; {3C2B} ARRAY ADDRESS HIBYTE
LDA ]PROD ; {3C2B} STORE TOTAL ARRAY SIZE
STA RETURN ; {3C2B} IN BYTES IN RETURN
LDA ]PROD+1 ; {3C2B}
STA RETURN+1 ; {3C2B}
LDA ]PROD+2 ; {3C2B}
STA RETURN+2 ; {3C2B}
LDA ]PROD+3 ; {3C2B}
STA RETURN+3 ; {3C2B}
LDA #4 ; {2C2B} SIZE OF RETURN
STA RETLEN ; {3C2B}
RTS ; {6C1B}
```
---
### THE AGET162 MACRO
_SUMMARY_
| Condition | Value |
| --------------- | ------------------------------------------------------------ |
| Name | `GET162` |
| Type | Macro |
| File | `MAC.ARR16B2D.ASM` |
| Author | Nathan Riggs |
| Last Revision | 18-MAR-2021 |
| Assembler | Merlin Pro 8 |
| OS | Apple DOS 3.3 |
| Purpose | Retrieve a value held in an element of a 16-bit, 2-dimensional array |
| Input | ]1 = Array Address<br />]2 = First Dimension Element Address<br />]3 = Second Dimension Element Address |
| Output | none |
| Dependencies | `SUB.AGET162.ASM` |
| Flags Destroyed | NZCV |
| Cycles | 436+ |
| Bytes | 263 |
| Notes | none |
| See Also | `DIM162` `ADIM162` `AGET162` `PUT162` `APUT162` |
---
*DETAILS*
The `GET162` macro retrieves a value from a specified element in a 16-bit, two-dimensional array and puts it in `RETURN`, with the element value length held in `RETLEN`. Note that if an out-of-bounds element index is given for either the first or second dimension, the value returned will be trash; there is no error-handling built into arrays, and thus it must be handled by the programmer.
`LISTING 3.43: The GET162 Macro Source`
```assembly
*
*``````````````````````````````*
* GET162 (NATHAN RIGGS) *
* *
* GET THE VALUE STORED AT AN *
* ELEMENT OF A 16-BIT, TWO- *
* DIMENSIONAL ARRAY. *
* *
* PARAMETERS *
* *
* ]1 = ARRAY ADDRESS *
* ]2 = ELEMENT X INDEX *
* ]3 = Y INDEX *
* *
* CYCLES: 436+ *
* SIZE: 263 BYTES *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
GET162 MAC
_MLIT ]1;WPAR1 ; {16C12B} PARSE ARAY ADDRESS
_MLIT ]2;WPAR2 ; {16C12B} PARSE X INDEX
_MLIT ]3;WPAR3 ; {16C12B} PARSE Y INDEX
JSR AGET162 ; {388C227B}
<<<
*
```
---
### THE AGET162 SUBROUTINE
_SUMMARY_
| Condition | Value |
| --------------- | ------------------------------------------------------------ |
| Name | `AGET162` |
| Type | Subroutine |
| File | `SUB.AGET162.ASM` |
| Author | Nathan Riggs |
| Last Revision | 18-MAR-2021 |
| Assembler | Merlin Pro 8 |
| OS | Apple DOS 3.3 |
| Purpose | Retrieve an indexed value in a two-dimensional, 16-bit array |
| Input | WPAR1 = Array Address<br />WPAR2 = First Dimension Index<br />WPAR3 = Second Dimension Index |
| Output | none |
| Dependencies | none |
| Flags Destroyed | NZCV |
| Cycles | 382+ |
| Bytes | 224 |
| Notes | None |
| See Also | |
---
*DETAILS*
The `AGET162` subroutine retrieves the value stored in an element of a two-dimensional, 16-bit array, holding the value in `RETURN` with its length in `RETLEN`. Additionally, the value length is held in the **.A** register, and the starting address of the element being read is held in the **.X** register (low byte of address) and the **.Y** register (high byte of address).
Parameters are passed to `AGET162` via the zero page to save cycles.
`LISTING 3.44: The AGET162 Subroutine Source`
```assembly
*
*``````````````````````````````*
* AGET162 (NATHAN RIGGS) *
* *
* GET A VALUE FROM AN ELEMENT *
* IN A 2-DIMENSIONAL, 16-BIT *
* ARRAY. *
* *
*------------------------------*
* MULTIPLICATION CODE ADAPTED *
* FROM WHITE FLAME'S WORK ON *
* CODEBASE64. LICENSE MAY VARY *
*------------------------------*
* *
* INPUT: *
* *
* WPAR1 = ARRAY ADDRESS *
* WPAR2 = 1ST DIM INDEX *
* WPAR3 = 2ND DIM INDEX *
* *
* DESTROY: NZCIDV *
* ^^^ ^ *
* *
* CYCLES: 382+ *
* SIZE: 224 BYTES *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]ADDR EQU WPAR1 ; ARRAY ADDRESS
]XIDX EQU WPAR2 ; FIRST DIMENSION LENGTH
]YIDX EQU WPAR3 ; SECOND DIMENSION LENGTH
*
]ESIZE EQU VARTAB ; ELEMENT LENGTH
]MCAND EQU VARTAB+2 ; MULTIPLICAND
]MLIER EQU VARTAB+4 ; MULTIPLIER
]PROD EQU VARTAB+6 ; PRODUCT
]PBAK EQU VARTAB+10 ; ^BACKUP
]XLEN EQU VARTAB+12 ; X-DIM LENGTH
]YLEN EQU VARTAB+14 ; Y-DIM LENGTH
*
AGET162
LDY #4 ; {2C2B} READ BYTE 4 FROM HEADER
LDA (]ADDR),Y ; {6C2B} TO GET ELEMENT SIZE
STA ]ESIZE ; {3C2B}
LDY #0 ; {2C2B} READ BYTE 0 FROM HEADER
LDA (]ADDR),Y ; {6C2B} TO GET X-DIM LENGTH LOBYTE
STA ]XLEN ; {3C2B}
LDY #1 ; {2C2B} READ BYTE 1 FROM HEADER
LDA (]ADDR),Y ; {6C2B} TO GET X-DIM LENGTH HIBYTE
STA ]XLEN+1 ; {3C2B}
LDY #2 ; {2C2B} READ BYTE 2 FROM HEADER
LDA (]ADDR),Y ; {6C2B} TO GET Y-DIM LENGTH LOBYTE
STA ]YLEN ; {3C2B}
LDY #3 ; {2C2B} READ BYTE 3 OF HEADER
LDA (]ADDR),Y ; {6C2B} TO GET Y-DIM LENGTH HIBYTE
STA ]YLEN+1 ; {3C2B}
LDY #0 ; {2C2B} RESET BYTE INDEX
*
** MULTIPLY Y-INDEX BY Y-LENGTH
*
LDA ]YIDX ; {3C2B} PUT Y-INDEX INTO
STA ]MLIER ; {3C2B} MULTIPLIER
LDA ]YIDX+1 ; {3C2B} ALSO HIBYTE
STA ]MLIER+1 ; {3C2B}
LDA ]YLEN ; {3C2B} PUT Y-DIM LENGTH LOBYTE
STA ]MCAND ; {3C2B} INTO MULTIPLICAND
LDA ]YLEN+1 ; {3C2B} ALSO HIBYTE
STA ]MCAND+1 ; {3C2B}
LDA #00 ; {2C2B} RESET
STA ]PROD ; {3C2B} PRODUCT BYTES
STA ]PROD+1 ; {3C2B}
STA ]PROD+2 ; {3C2B}
STA ]PROD+3 ; {3C2B}
LDX #$10 ; {2C2B} LOAD #16 INTO X REGISTER
:SHIFT_R
LSR ]MLIER+1 ; {6C2B} DIVIDE MULTIPLIER BY 2
ROR ]MLIER ; {6C2B} ADJUST HIBYTE
BCC :ROT_R ; {3C2B} IF 0 PUT IN CARRY, ROTATE MORE
LDA ]PROD+2 ; {3C2B} LOAD PRODUCT 3RD BYTE
CLC ; {2C1B} CLEAR CARRY
ADC ]MCAND ; {4C3B} ADD MULTIPLICAND
STA ]PROD+2 ; {3C2B} STORE IN PRODUCT 3RD
LDA ]PROD+3 ; {3C2B} LOAD PRODUCT HIBYTE
ADC ]MCAND+1 ; {4C3B} ADD MULTIPLICAN HIBYTE
:ROT_R
ROR ; {6C2B} ROTATE .A RIGHT
STA ]PROD+3 ; {3C2B} STORE IN PRODUCT HIBYTE
ROR ]PROD+2 ; {6C2B} ROTATE 3RD BYTE
ROR ]PROD+1 ; {6C2B} ROTATE 2ND BYTE
ROR ]PROD ; {6C2B} ROTATE LOBYTE
DEX ; {2C1B} DECREASE X COUNTER
BNE :SHIFT_R ; {3C2B} IF NOT ZERO, SHIFT AGAIN
*
** NOW MULTIPLY XIDX BY ELEMENT SIZE
*
LDA ]PROD ; {3C2B} BACKUP PREVIOUS PRODUCT
STA ]PBAK ; {3C2B} 1ST AND 2ND BYTES; THE
LDA ]PROD+1 ; {3C2B} 3RD AND 4TH ARE NOT USED
STA ]PBAK+1 ; {3C2B}
LDA ]XIDX ; {3C2B} LOAD X-INDEX LOBYTE
STA ]MLIER ; {3C2B} AND STORE IN MULTIPLIER
LDA ]XIDX+1 ; {3C2B} LOAD HIBYTE AND STORE
STA ]MLIER+1 ; {3C2B}
LDA ]ESIZE ; {3C2B} LOAD ELEMENT SIZE AND
STA ]MCAND ; {3C2B} STORE LOBYTE IN MULTIPLICAND
LDA #0 ; {2C2B} CLEAR MULTIPLICAND HIBYTE
STA ]MCAND+1 ; {3C2B}
*
STA ]PROD ; {3C2B} CLEAR ALL PRODUCT BYTES
STA ]PROD+1 ; {3C2B}
STA ]PROD+2 ; {3C2B}
STA ]PROD+3; ; {3C2B}
LDX #$10 ; {2C2B} LOAD #16 IN COUNTER
:SHIFTR LSR ]MLIER+1 ; {6C2B} DIVIDE MULTIPLIER HIBYTE BY 2
ROR ]MLIER ; {6C2B} ADJUST LOBYTE
BCC :ROTR ; {3C2B} IF 0 PUT IN CARRY, ROTATE
LDA ]PROD+2 ; {3C2B} LOAD PRODUCT 3RD BYTE
CLC ; {2C1B} CLEAR CARRY
ADC ]MCAND ; {4C3B} ADD MULTIPLICAND LOBYTE
STA ]PROD+2 ; {3C2B} STORE PRODUCT 3RD BYTE
LDA ]PROD+3 ; {3C2B} LOAD PRODUCT HIBYTE
ADC ]MCAND+1 ; {4C3B} ADD MULTIPLICAND HIBYTE
:ROTR
ROR ; {6C2B} ROTATE .A RIGHT
STA ]PROD+3 ; {3C2B} STORE IN PRODUCT HIBYTE
ROR ]PROD+2 ; {6C2B} ROTATE PRODUCT 3RD BYTE
ROR ]PROD+1 ; {6C2B} ROTATE 2ND BYTE
ROR ]PROD ; {6C2B} ROTATE LOBYTE
DEX ; {2C1B} DECREMENT X COUNTER
BNE :SHIFTR ; {3C2B} IF != 0, SHIFT AGAIN
*
** NOW ADD X * ESIZE TO RUNNING PRODUCT
*
CLC ; {2C1B} CLEAR CARRY
LDA ]PROD ; {3C2B} ADD PREVIOUS PRODUCT
ADC ]PBAK ; {4C3B} LOBYTE TO CURRENT
STA ]PROD ; {3C2B} AND STORE IN PRODUCT
LDA ]PROD+1 ; {3C2B} DO THE SAME WITH HIBYTES
ADC ]PBAK+1 ; {4C3B}
STA ]PROD+1 ; {3C2B}
CLC ; {2C1B} CLEAR CARRY
LDA ]PROD ; {3C2B} ADD 5 BYTES TO PRODUCT
ADC #5 ; {4C3B} TO ACCOUNT FOR ARRAY HEADER
STA ]PROD ; {3C2B}
LDA ]PROD+1 ; {3C2B}
ADC #0 ; {2C2B} ADJUST HIBYTE
STA ]PROD+1 ; {3C2B}
*
** NOW ADD BASE ADDRESS OF ARRAY TO GET
** THE ADDRESS OF THE INDEX VALUE
*
CLC ; {2C1B} CLEAR CARRY
LDA ]PROD ; {3C2B} ADD PRODUCT TO ARRAY
ADC ]ADDR ; {4C3B} ADDRESS, LOBYTES
STA ADDR2 ; {3C2B} STORE IN ZERO PAGE
LDA ]PROD+1 ; {3C2B} DO THE SAME WITH HIBYTES
ADC ]ADDR+1 ; {4C3B}
STA ADDR2+1 ; {3C2B}
LDY #0 ; {2C2B} RESET BYTE INDEX
*
** COPY FROM SRC ADDR TO DEST ADDR
*
:CLP
LDA (ADDR2),Y ; {6C2B} LOAD BYTE FROM ELEMENT
STA RETURN,Y ; {5C3B} AND STORE IN RETURN
INY ; {2C1B} INCREMENT BYTE COUNTER
CPY ]ESIZE ; {4C3B} IF != ELEMENT LENGTH,
BNE :CLP ; {3C2B} CONTINUE LOOPING
LDA ]ESIZE ; {3C2B} .A = ELEMENT SIZE
STA RETLEN ; {3C2B} ALSO IN RETLEN
LDY ADDR2+1 ; {3C2B} .Y = ELEMENT ADDRESS HIBYTE
LDX ADDR2 ; {3C2B} .X = ELEMENT ADDRESS LOBYTE
RTS ; {6C1B}
```
---
### THE PUT162 MACRO
_SUMMARY_
| Condition | Value |
| --------------- | ------------------------------------------------------------ |
| Name | `PUT162` |
| Type | Macro |
| File | `MAC.ARR16B2D.ASM` |
| Author | Nathan Riggs |
| Last Revision | 18-MAR-2021 |
| Assembler | Merlin Pro 8 |
| OS | Apple DOS 3.3 |
| Purpose | Place a value in an element of a 16-bit, 2-dimensional array |
| Input | ]1 = Source Address<br />]2 = Destination Array Address<br />]3 = First Dimension Element Address<br />]4 = Second Dimension Element Address |
| Output | none |
| Dependencies | `SUB.APUT162.ASM` |
| Flags Destroyed | NZCV |
| Cycles | 352+ |
| Bytes | 223 |
| Notes | none |
| See Also | `DIM162` `ADIM162` `GET162` `AGET162` `APUT162` |
---
*DETAILS*
The `PUT162` macro uses the `APUT162` subroutine to place a value in an element of a 16-bit, two-dimensional array. Like other array `PUT` macros and subroutines, `APUT162` only accepts an address that either 1) points to the address of the value to be written, or 2) is the address of the value to be written. This is determined by either sending a literal value as the address (preceded by a # sign), which indicates that the value is held at the given address, or simply by passing the address as it is, which indicates it is an indirect reference. This is how addresses are treated throughout the library as a whole; the difference is that one might expect to send a value in itself as a parameter, when this is simply not the case. In the future, extra functionality may be added to allow for direct and literal placement of a value as a parameter, but this is not currently a pressing issue.
`LISTING 3.45: The PUT162 Macro Source`
```assembly
*
*``````````````````````````````*
* PUT82 (NATHAN RIGGS) *
* *
* SET VALUE OF AN ELEMENT IN *
* AN 8-BIT, TWO-DIMENSIONAL *
* ARRAY. *
* *
* PARAMETERS *
* *
* ]1 = SOURCE ADDRESS *
* ]2 = DEST ARRAY ADDRESS *
* ]3 = ELEMENT X INDEX *
* ]4 = Y INDEX *
* *
* CYCLES: 352+ *
* SIZE: 223 BYTES *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
PUT82 MAC
_MLIT ]1;WPAR1 ; {16C12B} PARSE SOURCE ADDRESS
_MLIT ]2;WPAR2 ; {16C12B} PARSE DEST ADDRESS
LDA ]3 ; {3C2B} X INDEX
STA BPAR1 ; {3C2B}
LDA ]4 ; {3C2B} Y INDEX
STA BPAR2 ; {3C2B}
JSR APUT82 ; {308C191B}
<<<
*
```
---
### THE APUT162 SUBROUTINE
_SUMMARY_
| Condition | Value |
| --------------- | ------------------------------------------------------------ |
| Name | `APUT162` |
| Type | Subroutine |
| File | `SUB.APUT162.ASM` |
| Author | Nathan Riggs |
| Last Revision | 18-MAR-2021 |
| Assembler | Merlin Pro 8 |
| OS | Apple DOS 3.3 |
| Purpose | Initialize a two-dimensional, 16-bit array |
| Input | WPAR1 = Source Address<br />WPAR2 = Array Address<br />WPA32 = First Dimension Index<br />ADDR1 = Second Dimension Index |
| Output | none |
| Dependencies | none |
| Flags Destroyed | NZCV |
| Cycles | 378+ |
| Bytes | 220 |
| Notes | None |
| See Also | `DIM162` `ADIM162` `GET162` `AGET162` `PUT162` |
---
*DETAILS*
The `APUT162` subroutine places a value found at one address into the location of an element in a 16-bit, two-dimensional array. The length to be copied is determined by the array's element length. After executing, the **.A** register holds the length of the value, and the **.X** and **.Y** registers hold the starting address of the element in question (**.X** holds the low byte of the address, while **.Y** holds the high byte). Note that providing an out-of-bounds value for either dimensional index will result in overwriting either data or code that should not be written to, and this will likely crash the system. As such, care should be taken by the programmer to keep all array requests within their given boundaries.
`LISTING 3.46: The APUT182 Subroutine Source`
```assembly
*
*``````````````````````````````*
* APUT162 (NATHAN RIGGS) *
* *
* PLACE A VALUE HELD IN ONE *
* ADDRESS INTO THE SPECIFIED *
* ELEMENT IN A 16-BIT, TWO- *
* DIMENSIONAL ARRAY. *
* *
*------------------------------*
* MULTIPLICATION ADAPTED FROM *
* WHITE FLAME'S WORK ON *
* CODEBASE64. LICENSE MAY VARY *
*------------------------------*
* *
* INPUT: *
* *
* WPAR1 = SOURCE ADDRESS *
* WPAR2 = ARRAY ADDRESS *
* WPAR3 = 1ST DIM INDEX *
* ADDR1 = 2ND DIM INDEX *
* *
* DESTROY: NZCIDV *
* ^^^ ^ *
* *
* CYCLES: 378+ *
* SIZE: 220 BYTES *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]ADDRS EQU WPAR1 ; VALUE SOURCE ADDRESS
]ADDRD EQU WPAR2 ; ARRAY ADDRESS
]XIDX EQU WPAR3 ; FIRST DIMENSION INDEX
]YIDX EQU ADDR1 ; SECOND DIMENSION INDEX
*
]ESIZE EQU VARTAB ; ELEMENT LENGTH
]MCAND EQU VARTAB+6 ; MULTIPLICAND
]MLIER EQU VARTAB+8 ; MULTIPLIER
]PBAK EQU VARTAB+10 ; PRODUCT BACKUP
]XLEN EQU VARTAB+12 ; X-DIMENSION LENGTH
]YLEN EQU VARTAB+14 ; Y-DIMENSION LENGTH
]PROD EQU VARTAB+16 ; PRODUCT OF MULTIPLICATION
*
APUT162
LDY #4 ; {2C2B} LOAD BYTE 4 OF ARRAY
LDA (]ADDRD),Y ; {6C2B} HEADER TO GET ELEM LENGTH
STA ]ESIZE ; {3C2B}
LDY #0 ; {2C2B} LOAD BYTE 0 TO GET
LDA (]ADDRD),Y ; {6C2B} X-DIMENSION LENGTH LOBYTE
STA ]XLEN ; {3C2B}
LDY #1 ; {2C2B} LOAD BYTE 1 TO GET
LDA (]ADDRD),Y ; {6C2B} X-DIMENSION LENGTH HIBYTE
STA ]XLEN+1 ; {3C2B}
LDY #2 ; {2C2B} LOAD BYTE 2 TO GET THE
LDA (]ADDRD),Y ; {6C2B} Y-DIMENSION LENGTH LOBYTE
STA ]YLEN ; {3C2B}
LDY #3 ; {2C2B} LOAD BYTE 3 TO GET THE
LDA (]ADDRD),Y ; {6C2B} Y-DIMENSION LENGTH HIBYTE
STA ]YLEN+1 ; {3C2B}
LDY #0 ; {2C2B} RESET BYTE INDEX
*
** MULTIPLY Y-INDEX BY Y-LENGTH
*
LDA ]YIDX ; {3C2B} LOAD Y-INDEX LOBYTE
STA ]MLIER ; {3C2B} PUT IN MULTIPLIER LOBYTE
LDA ]YIDX+1 ; {3C2B} DO SAME FOR HIBYTES
STA ]MLIER+1 ; {3C2B}
LDA ]YLEN ; {3C2B} PUT Y-DIM LENGTH LOBYTE
STA ]MCAND ; {3C2B} INTO MULTIPLICAND
LDA ]YLEN+1 ; {3C2B} DO SAME FOR HIBYTE
STA ]MCAND+1 ; {3C2B}
LDA #00 ; {2C2B} CLEAR PRODUCT BYTES
STA ]PROD ; {3C2B}
STA ]PROD+1 ; {3C2B}
STA ]PROD+2 ; {3C2B}
STA ]PROD+3 ; {3C2B}
LDX #$10 ; {2C2B} INIT COUNTER TO #16
:SHIFT_R
LSR ]MLIER+1 ; {6C2B} DIVIDE MULTIPLIER HIBYTE BY 2
ROR ]MLIER ; {6C2B} ADJUST LOBYTE
BCC :ROT_R ; {3C2B} IF 0 PUT IN CARRY, ROTATE PRODUCT
LDA ]PROD+2 ; {3C2B} LOAD PRODUCT 3RD BYTE
CLC ; {2C1B} CLEAR CARRY
ADC ]MCAND ; {4C3B} ADD MULTIPLICAND
STA ]PROD+2 ; {3C2B} STORE 3RD BYTE
LDA ]PROD+3 ; {3C2B} LOAD PRODUCT HIBYTE
ADC ]MCAND+1 ; {4C3B} ADD MULTIPLICAND HIBYTE
:ROT_R
ROR ; {6C2B} ROTATE .A RIGHT
STA ]PROD+3 ; {3C2B} STORE IN PRODUCT HIBYTE
ROR ]PROD+2 ; {6C2B} ROTATE 3RD BYTE
ROR ]PROD+1 ; {6C2B} ROTATE 2ND
ROR ]PROD ; {6C2B} ROTATE LOBYTE
DEX ; {2C1B} DECREASE X COUNTER
BNE :SHIFT_R ; {3C2B} IF NOT ZERO, LOOP AGAIN
*
** NOW MULTIPLY XIDX BY ELEMENT SIZE
*
LDA ]PROD ; {3C2B} BACKUP PREVIOUS
STA ]PBAK ; {3C2B} PRODUCT FOR USE LATER
LDA ]PROD+1 ; {3C2B} DO SAME FOR HIBYTE
STA ]PBAK+1 ; {3C2B}
LDA ]XIDX ; {3C2B} PUT X-INDEX LOBYTE
STA ]MLIER ; {3C2B} INTO MULTIPLIER
LDA ]XIDX+1 ; {3C2B} DO SAME FOR HIBYTE
STA ]MLIER+1 ; {3C2B}
LDA ]ESIZE ; {3C2B} PUT ELEMENT SIZE
STA ]MCAND ; {3C2B} INTO MULTIPLICAND
LDA #0 ; {2C2B} CLEAR MULTIPLICAND HIBYTE
STA ]MCAND+1 ; {3C2B}
*
STA ]PROD ; {3C2B} CLEAR PRODUCT
STA ]PROD+1 ; {3C2B}
STA ]PROD+2 ; {3C2B}
STA ]PROD+3 ; {3C2B}
LDX #$10 ; {2C2B} INIT X COUNTER TO #16
:SHIFTR LSR ]MLIER+1 ; {6C2B} DIVIDE MULTIPLIER BY 2
ROR ]MLIER ; {6C2B} ADJUST LOBYTE
BCC :ROTR ; {3C2B} IF 0 PUT INTO CARRY, ROTATE PROD
LDA ]PROD+2 ; {3C2B} LOAD PRODUCT 3RD BYTE
CLC ; {2C1B} CLEAR CARRY
ADC ]MCAND ; {4C3B} ADD MULTIPLICAND LOBYTE
STA ]PROD+2 ; {3C2B}
LDA ]PROD+3 ; {3C2B} LOAD PRODUCT HIBYTE
ADC ]MCAND+1 ; {4C3B} HAD MULTIPLICAND HIBYTE
:ROTR
ROR ; {6C2B} ROTATE .A RIGHT
STA ]PROD+3 ; {3C2B} STORE PRODUCT HIBYTE
ROR ]PROD+2 ; {6C2B} ROTATE 3RD BYTE
ROR ]PROD+1 ; {6C2B} ROTATE 2ND BYTE
ROR ]PROD ; {6C2B} ROTATE LOBYTE
DEX ; {2C1B} DECREASE X COUNTER
BNE :SHIFTR ; {3C2B} IF NOT 0, KEEP LOOPING
*
** NOW ADD X * ESIZE TO RUNNING PRODUCT
*
CLC ; {2C1B} CLEAR CARRY
LDA ]PROD ; {3C2B} ADD CURRENT PRODUCT
ADC ]PBAK ; {4C3B} TO PREVIOUS PRODUCT
STA ]PROD ; {3C2B} AND STORE BACK IN PRODUCT
LDA ]PROD+1 ; {3C2B}
ADC ]PBAK+1 ; {4C3B}
STA ]PROD+1 ; {3C2B}
CLC ; {2C1B} CLEAR CARRY
LDA ]PROD ; {3C2B} INCREASE LOBYTE BY 5
ADC #5 ; {2C2B} TO ACCOUNT FOR ARRAY
STA ]PROD ; {3C2B} HEADER
LDA ]PROD+1 ; {3C2B}
ADC #0 ; {2C2B} ADJUST HIBYTE
STA ]PROD+1 ; {3C2B}
*
** ADD ARRAY ADDRESS TO GET INDEX
*
CLC ; {2C1B} CLEAR CARRY
LDA ]PROD ; {3C2B} ADD ARRAY ADDRESS
ADC ]ADDRD ; {4C3B} TO PRODUCT TO GET
STA ADDR2 ; {3C2B} ELEMENT ADDRESS; STORE
LDA ]PROD+1 ; {3C2B} ADDRESS ON ZERO PAGE
ADC ]ADDRD+1 ; {4C3B}
STA ADDR2+1 ; {3C2B}
LDY #0 ; {2C2B} RESET BYTE INDEX
:CLP
LDA (]ADDRS),Y ; {6C2B} LOAD BYTE FROM SOURCE
STA (ADDR2),Y ; {6C2B} STORE AT ELEMENT ADDRESS
INY ; {2C1B} INCREASE BYTE INDEX
CPY ]ESIZE ; {4C3B} IF != ELEMENT LENGTH, LOOP
BNE :CLP ; {3C2B}
LDY ADDR2+1 ; {3C2B} .Y = ELEMENT ADDRESS HIBYTE
LDX ADDR2 ; {3C2B} .X = ELEMENT ADDRESS LOBYTE
LDA ]ESIZE ; {3C2B} .A = ELEMENT LENGTH
RTS ; {6C1B}
```
---
# PART II: THE ARRAYS COLLECTION DEMO FILE
The Arrays Collection Demo File includes basic methods for calling each macro (and thus subroutine) in the collection, along with substantial commentary that explains how the macros are being used. This demo is not meant to be exhaustive; rather, it simply shows the most common ways to use the collection, and serves as a proof-of-concept rather than a rigorous test of each macro and subroutine. Ultimately, this demo--as well as others in the library--is provided for pedagogical reasons rather than practical reasons, and should serve a beginner well in trying to learn how to use the library, or how to use 6502 Assembly with macros in general.
`LISTING 3.5: The DEMO.ARRAYS.ASM File Source`
```assembly
*
*``````````````````````````````*
* DEMO.ARRAYS *
* *
* A DECIDEDLY NON-EXHAUSTIVE *
* DEMO OF ARRAY FUNCTIONALITY *
* IN THE APPLEIIASM LIBRARY. *
* *
* AUTHOR: NATHAN RIGGS *
* CONTACT: NATHAN.RIGGS@ *
* OUTLOOK.COM *
* *
* DATE: 18-MAR-2021 *
* ASSEMBLER: MERLIN 8 PRO *
* OS: DOS 3.3 *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
** ASSEMBLER DIRECTIVES
*
CYC AVE
EXP OFF
TR ON
DSK DEMO.ARRAYS
OBJ $BFE0
ORG $6000
*
*``````````````````````````````*
* TOP INCLUDES (HOOKS,MACROS) *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
PUT MIN.HEAD.REQUIRED.ASM
USE MIN.MAC.REQUIRED.ASM
USE MIN.MAC.ARR8B1D.ASM
USE MIN.MAC.ARR8B2D.ASM
USE MIN.MAC.ARR16B1D.ASM
USE MIN.MAC.ARR16B2D.ASM
PUT MIN.HEAD.ARRAYS.ASM
*
]HOME2 EQU $FC58
*
*``````````````````````````````*
* PROGRAM MAIN BODY *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
** THE ARRAY COLLECTION OF THE APPLEIIASM LIBRARY
** CONTAINS THE CORE FUNCTIONALITY NECESSARY FOR
** USING ARRAYS. CURRENTLY, THERE ARE 4 TYPES OF
** ARRAYS SUPPORTED: 8-BIT, 1 DIMENSIONAL ARRAYS,
** WHICH CAN HOLD UP TO 255 KEYS, 8-BIT, 2-DIMENSIONAL
** ARRAYS THAT CAN HOLD UP TO 255 X 255 ELEMENTS
** ACCESSED VIA AN X,Y COORDINATE; 16-BIT,
** 1-DIMENSION ARRAYS THAT CAN HOLD JUST OVER 65K
** ELEMENTS (A MAXIMUM THAT WOULD TAKE UP MOST OF THE
** MEMORY ON A TYPICAL APPLE II SYSTEM), AND 16-BIT,
** 2-DIMENSIONAL ARRAYS THAT CAN HOLD A MAXIMUM OF
** 65K X 65K ELEMENTS ACCESSED VIA X,Y COORDINATES.
*
** AS IT IS, EACH TYPE OF ARRAY HAS THREE MACROS DEDICATED
** TO IT: AN ARRAY INITIALIZATION MACRO (DIM), A MACRO
** THAT GETS AN ELEMENT VALUE, AND A MACRO THAT ENTERS
** A VALUE AT THE SPECIFIED INDEX ELEMENT. SUPPORT FOR
** SPECIFIC MACROS AND SUBROUTINES THAT ACT ON ARRAYS, SUCH
** AS SORTING ALGORITHMS, MERGING, SEARCHING AND SO ON,
** ARE LIKELY TO BE ADDED IN THE FUTURE, BUT THIS WILL
** REQUIRE A SECOND DISK DEDICATED TO ARRAYS, GIVEN SPACE
** CONSTRAINTS. SUPPORT FOR 8-BIT 3-DIMENSIONAL ARRAYS
** MAY ALSO BE SUPPORTED, BUT THAT DEPENDS ON NECESSITY OR
** DEMAND.
*
** THIS DEMO FILE SHOWS THE USAGE AND PERFORMANCE OF THE
** FOLLOWING MACROS (AND THE SUBROUTINES BEHIND THEM):
*
** DIM81 : INITIALIZE AN 8-BIT, 1 DIMENSIONAL ARRAY
** GET81 : RETRIEVE ELEMENT VALUE FROM 8-BIT, 1-D ARRAY
** PUT81 : PLACE VALUE IN ELEMENT OF 8-BIT, 1-D ARRAY
** DIM82 : INITIALIZE AN 8-BIT, 2 DIMENSIONAL ARRAY
** GET82 : RETRIEVE ELEMENT VALUE FROM 8-BIT, 2-D ARRAY
** PUT82 : PLACE VALUE IN ELEMENT OF 8-BIT, 2-D ARRAY
** DIM161 : INITIALIZE A 16-BIT, 1 DIMENSIONAL ARRAY
** GET161 : RETRIEVE ELEMENT VALUE FROM 16-BIT, 1-D ARRAY
** PUT161 : PLACE VALUE IN ELEMENT OF 16-BIT, 1-D ARRAY
** DIM162 : INITIALIZE A 16-BIT, 2 DIMENSIONAL ARRAY
** GET162 : RETRIEVE ELEMENT VALUE FROM 16-BIT, 2-D ARRAY
** PUT162 : PLACE VALUE IN ELEMENT OF 16-BIT, 2-D ARRAY
*
*``````````````````````````````*
* 8-BIT ARRAY MACRO EXAMPLES *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
** SINCE THESE ROUTINES DO NOT NORMALLY OUTPUT ANYTHING
** ON THEIR OWN, WE WILL BE USING THE DUMP MACRO FROM THE
** REQUIRED LIBRARY COLLECTION IN ORDER TO PEEK AT THE
** ARRAY CONTENTS.
*
** FIRST, LET'S TACKLE ONE-DIMENSIONAL 8-BIT ARRAYS. WE START
** BY INITIALIZING THE ARRAY, WHICH ALWAYS MUST BE DONE BEFORE
** ANY OTHER USE OF AN ARRAY. WE DO THIS WITH THE DIM81 MACRO,
** AS SUCH:
*
DIM81 #$300;#10;#2;#$EE
*
** HERE WE ARE INITIALIZING AN ARRAY AT THE ADDRESS #$300,
** WITH 10 ELEMENTS THAT ARE EACH 2 BYTES LONG. EACH ELEMENT
** IS FILLED WITH THE VALUE #$EE TO BEGIN WITH. NOW WE CAN
** USE DUMP TO VIEW THE ARRAY:
*
JSR ]HOME2
_PRN "8-BIT, 1-DIMENSIONAL ARRAYS",8D
_PRN "===========================",8D8D8D
DUMP #$300;#2
DUMP #$302;#10
DUMP #$30C;#10
_PRN " ",8D8D
_WAIT
*
** IT IS IMPORTANT TO NOTE HOW WE ARE VIEWING THE ARRAY HERE.
** FIRST, WE ARE DUMPING THE FIRST TWO BYTES OF THE ARRAY;
** THESE HOLD THE NUMBER OF ELEMENTS IN THE ARRAY AND THE
** LENGTH OF EACH ELEMENT, RESPECTIVELY. AFTERWARDS, WE
** DUMP THE FOLLOWING 20 BYTES, DETERMINED BY THE ELEMENT
** SIZE TIMES THE NUMBER OF ELEMENTS, WHICH HOLDS THE DATA
** OF THE ARRAY. ALL FOUR TYPES OF ARRAYS ARE BUILT IN A
** SIMILAR FASHION, BUT VARY BASED ON THE NEEDS OF EACH TYPE.
** THE DIFFERENCES WILL BE EASY TO DISCERN ONCE WE DUMP
** A 2-DIMENSIONAL ARRAY.
*
** NOW WE CAN PUT A VALUE IN AN ELEMENT. THIS IS EASILY DONE VIA
** THE PUT81 MACRO:
*
PUT81 #]WORD1;#$300;#5
DUMP #$300;#2
DUMP #$302;#10
DUMP #$30C;#10
_PRN " ",8D8D
_WAIT
*
** TWO THINGS SHOULD BECOME APPARENT AFTER VIEWING THE
** CONTENTS OF THIS ARRAY, AND THESE EXTEND TO ALL TYPES
** OF ARRAYS: FIRST, AN ITEM IS COPIED TO THE ARRAY FROM
** ANOTHER MEMORY ADDRESS, AND THE NUMBER OF BYTES COPIED
** IS DETERMINED BY THE ELEMENT LENGTH OF THE ARRAY;
** SECOND, IT SEEMS AT FIRST AS THOUGH THE ELEMENT ALTERED
** IS AT INDEX 6 RATHER THAN 5, BUT THIS IS BECAUSE ARRAYS
** START AT ELEMENT 0. NOT ELEMENT 1. WHILE THERE WOULD NOT
** BE MUCH OF A PROBLEM TREATING ARRAYS AS STARTING AT
** ELEMENT 1, PROVIDED YOU ARE CAREFUL NOT TO OVERFLOW THE
** ARRAY, INDEXING MIGHT GET CONFUSING.
*
** NOW WE CAN GET BACK THE VALUE WE PLACED IN THE ARRAY
** WITH THE GET81 MACRO, AS SUCH:
*
GET81 #$300;#5
DUMP #RETURN;RETLEN
_WAIT
*
** NOTE HERE THAT THE VALUE IS PLACED IN #RETURN, AND ITS
** LENGTH IS PLACED IN RETLEN. THIS IS HOW ALL ARRAY GET
** MACROS WORK.
*
** NOW THAT WE HAVE ONE TYPE OF ARRAY DOWN, THE REST OF THEM
** ARE SIMPLE TO UNDERSTAND BECAUSE THEY ALL WORK THE SAME
** WAY. LET'S LOOK AT HOW TWO-DIMENSION ARRAYS ARE HANDLED AT
** THE 8-BIT LEVEL TO HAVE A MORE COMPLETE UNDERSTANDING BEFORE
** MOVING TO 16 BITS, HOWEVER. FIRST, WE INITIALIZE THE ARRAY:
*
JSR ]HOME2
_PRN "8-BIT, 2-DIMENSIONAL ARRAYS",8D
_PRN "---------------------------",8D8D8D
DIM82 #$300;#5;#5;#1;#$AA
DUMP #$300;#3
DUMP #$303;#5
DUMP #$308;#5
DUMP #$30D;#5
DUMP #$312;#5
DUMP #$317;#5
_WAIT
_PRN " ",8D8D
*
** AND NOW THAT WE HAVE DECLARED THE 2D, 8BIT ARRAY,
** ADDING A VALUE AND RETRIVING IT IS EASY:
*
PUT82 #]WORD1;#$300;#3;#3
GET82 #$300;#3;#3
DUMP #RETURN;RETLEN
_WAIT
*
*
** IN THE ABOVE CODE, YOU CAN SEE THAT WE INITIALIZE
** THE 2-DIMENSIONAL ARRAY AT #$300, WHICH WRITES OVER
** THE PREVIOUS 1-DIMENSIONAL ARRAY. THERE ARE NO
** PROTECTIONS AGAINST THIS; YOU ARE EXPECTED TO KEEP
** TRACK OF THE LOCATIONS YOURSELF. AFTER THE ADDRESS,
** THE LENGTHS OF EACH DIMENSION ARE DECLARED (IN THIS
** CASE, 5 EACH), THEN THE LENGTH OF EACH ELEMENT IN
** THE ARRAY. FINALLY, THE DEFAULT VALUE, #$AA, IS
** PROVIDED. THE ARRAY IS THEN LISTED WITH THE DUMP
** COMMAND, 5 ELEMENTS AT A TIME.
*
** NOW ARMED WITH EXAMPLES OF 1 DIMENSIONAL AND TWO
** DIMENSIONAL ARRAYS, IT CAN BE EASILY INFERRED HOW
** THE REST OF THE MACROS WORK. THE ONLY REAL DIFFERENCE
** IS THAT 16-BIT ARRAYS USE TWO BYTES FOR THE NUMBER OF
** ELEMENTS IN EACH DIMENSION, RATHER THAN A SINGLE BYTE
** (THUS THE 16-BIT DESCRIPTION). THUS, WE CAN DECLARE
** A 16-BIT, 1-DIMENSIONAL ARRAY AS WELL AS PUT AND GET
** TO AND FROM IT AS SUCH:
*
JSR ]HOME2
DIM161 #$300;#10;#1;#$55
PUT161 #]WORD1;#$300;#3
GET161 #$300;#3
_PRN "16-BIT, 1-DIMENSIONAL ARRAYS",8D
_PRN "============================",8D8D8D
DUMP #$300;#3
DUMP #$303;#10
_PRN " ",8D8D
DUMP #RETURN;RETLEN
_WAIT
*
** AND AS YOU WOULD EXPECT, 16-BIT 2 DIMENSIONAL ARRAYS
** WORK IN MUCH THE SAME WAY:
*
JSR ]HOME2
DIM162 #$300;#3;#3;#1;#$66
PUT162 #]WORD1;#$300;#0;#0
GET162 #$300;#0;#0
_PRN "16-BIT, 2-DIMENSIONAL ARRAYS",8D
_PRN "============================",8D8D8D
DUMP #$300;#5
DUMP #$305;#3
DUMP #$308;#3
DUMP #$30B;#3
_PRN " ",8D8D
DUMP #RETURN;RETLEN
_WAIT
JSR ]HOME2
_PRN " ",8D8D
_PRN "FIN.",8D8D
*
JMP REENTRY
*
*``````````````````````````````*
* BOTTOM INCLUDES (ROUTINES) *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
PUT MIN.LIB.REQUIRED.ASM
*
** INDIVIDUAL SUBROUTINE INCLUDES
*
** 8-BIT 1-DIMENSIONAL ARRAY SUBROUTINES
*
PUT MIN.SUB.ADIM81.ASM
PUT MIN.SUB.AGET81.ASM
PUT MIN.SUB.APUT81.ASM
*
** 8-BIT 2-DIMENSIONAL ARRAY SUBROUTINES
*
PUT MIN.SUB.ADIM82.ASM
PUT MIN.SUB.AGET82.ASM
PUT MIN.SUB.APUT82.ASM
*
** 16-BIT 1-DIMENSIONAL ARRAYS
*
PUT MIN.SUB.ADIM161.ASM
PUT MIN.SUB.APUT161.ASM
PUT MIN.SUB.AGET161.ASM
*
** 16-BIT 2-DIMENSIONAL ARRAYS
*
PUT MIN.SUB.ADIM162.ASM
PUT MIN.SUB.APUT162.ASM
PUT MIN.SUB.AGET162.ASM
*
** VARIABLES
*
]WORD1 HEX FFFF
```