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