Simple Array Manager is a simplified version of the Array Manager utility I posted in the forum a while ago. It provides a way to coerce a block of memory within a 24-bit address space to a fixed item-length array.
* Each array has an unattached "header" defined statically in memory, accessed using a Handle system.
* An Array can contain 0 to 256 Items.
* An Item is 1 to 256 bytes in length.
A variety of string termination types can be used for the data transfers, including Item Size (default), zero-terminated, 255-terminated and Pascal-style run-length.
There is simple internalised program-flow support for both the data source/destination and the array index. Both are preserved and optionally incremented, exposed through the CPU registers.
The Interface is simple and lightweight - accessed via CPU registers and user-defined vectors.
Integration
To get the code running you'll need to include some of the files I've posted previously, such as Globals.asm and Helper.asm.
Design Overview
This was written as a testbed for some experiments with binary arrays and sets, and independent indexes. The main aim was to write a tool that allows a programmer to think about design and data structures instead of muck about with "plumbing" code.
Function parameters are consistent: rA = Array Handle, rX = Array Index, rY = zero-page vector that points to the source or destination for array data.
For example, assume an array contains a list of five variable length menu options that are to be dumped to the screen, aligned vertically:
The Tab feature is set to 40, location $FB/$FC is set with the screen location ($0400).
Index mode is set to Increment.
Register A is set with the array handle (you can create as many arrays as you want).
Register X is set with the index of the first menu entry (0).
Register Y is set with $FB (the destination for the data to be read from the array.
Code: Select all
...
lda Array_Handle
ldx #File_Menu_Index
ldy #$FB
Loop jsr Array_Read
cpx #End_Of_File_Menu
bne Loop
...
The result is five menu entries printed vertically, starting at the top left of the screen.
The code is patchy, and there are some sections that I want to rewrite. But it was a case of pushing out now something that worked, or trying to perfect it for an undetermined future date. Emphasis is on a small clean code footprint over speed of execution. The general interface to the array is designed so that a minimal amount of code is needed to use it, and much of the information can be emplaced statically (tables, etc) instead of code.
Only useful functions are included. For example, there is no Insert() function because a planned Index feature will make it unnecessary to order the actual array data. Basically, this utility makes it simple to push structured data around memory. The data read/write code is compatible with 65816 extended addressing.
Example
Code: Select all
jsr SimAry_Init$
; Create
lda #<Descriptor1
ldx #>Descriptor1
ldy #0
jsr SimAry_Create$
sta Test_Handle
; Write
lda Test_Handle
ldx #3 ; Increment data source and Array Index
jsr SimAry_SetIndexMode$
ldy #$FB ; Set Y with ZP vector
ldx #<MyData ; Populate vector
stx $00,Y
ldx #>MyData
stx $01,Y
lda Test_Handle
ldx #$00 ; Index 0
jsr SimAry_Write$
; After the write, X will be returned incremented to 1
; The address at $FB will have 8 added to it
rts
Descriptor1 word Array1 ; Base Address - 16 bits
byte 0 ; Base Address - Bank
byte 7 ; Item Size
byte 3 ; Max Index
byte 3 ; Top Index
byte 0 ; Index Mode
byte 0 ; Termination
byte 0 ; Tab
Array1 byte 0,0,0,0,0,0,0,0
byte 0,0,0,0,0,0,0,0
byte 0,0,0,0,0,0,0,0
byte 0,0,0,0,0,0,0,0
MyData byte 3,2,1,0,1,2,3,4
Code: Select all
;*=$C600
; --------------------------
; Simple Array Manager
; --------------------------
; ARRAY OPERATIONS
; Init Array
; Create Array (rAXY Descriptor Address) : Handle
; Delete Array (Handle)
;
; INTERROGATION
; Get Array Base (rA Handle) : rAXY
; Get Item Size (rA Handle) : rX
; Get Max Index (rA Handle) : rA ; useful for an iterator
; Get Item Address (rA Handle, rX Index) : rAXY ; Calculated
; Get Tab (rA Handle) : rA
;
; CONFIGURATION
; Set Base Bank (ra Handle, rX Bank)
; Set Base Address (ra Handle, rXY Address)
; Set Tab (rA Handle, rX Tab Value)
; If >0 this replaces ItemSize when Index Incr
; Set Index Mode (rA Handle, rX mode )
; 0=None, 1 = Index, 2 = Data, 3 = Both
; Set Top Index (rA Handle, rX Value)
; ITEM OPERATION - READ
; Read (rA Handle, rX Index, rY Indirect Destination Address) : rX
; ITEM OPERATION - WRITE
; Add (rA Handle, rY Indirect Source Address) : rX ; rX is set with TopIndex
; Write (rA Handle, rX Index, rY Indirect Source Address) : rX
; Clear (rA Handle, rX Index, rY Value) :rX
; Delete (rA Handle, rX Index) : rX
; DESCRIPTOR
; E Base Address
; B Item Size
; B Max Index
; B Top Index
; B Index Mode
; B Termination
; B Tab
; ----------
; ZERO-PAGE
zSimAryDesVec = zSimArrayMan$ ; 2 bytes
; ----------
; PUBLIC
SimAry_Init$ = Init
SimAry_Create$ = Create
SimAry_Delete$ = Delete
; Interrogation
SimAry_GetBase$ = SimAryGetBase
SimAry_GetTab$ = SimAryGetTab
SimAry_GetIndexMode$ = SimAryGetIndexMode
SimAry_GetItemAddress$ = SimAryGetItemAddress
SimAry_GetItemSize$ = SimAryGetItemSize
SimAry_GetMaxIndex$ = SimAryGetMaxIndex
SimAry_GetTopIndex$ = SimAryGetTopIndex
; Configuration
SimAry_SetBaseBank$ = SimArySetBaseBank
SimAry_SetBaseAddress$ = SimArySetBaseAddress
SimAry_SetTab$ = SimArySetTab
SimAry_SetIndexMode$ = SimArySetIndexMode
SimAry_SetTopIndex$ = SimArySetTopIndex
; Item Operations - Read
SimAry_Read$ = SimAryRead
; Item Operations - Write
SimAry_Add$ = SimAryAdd
SimAry_Clear$ = SimAryClear
SimAry_DeleteItem$ = SimAryDeleteItem
SimAry_Write$ = SimAryWrite
; ----------
; LOCAL
MaxSimArrays = 16
SimAryDescAdr words MaxSimArrays
SimArrayHdl byte 0,0
bytes MaxSimArrays
; Temporary storage
SimAryItemSize byte 0
SimAryMaxIndex byte 0
SimAryDeleteIndex byte 0
SimAryDataVec byte 0
SimAryReadOperFlag byte 0
CurSimAryA byte 0
CurSimAryX byte 0
CurSimAryY byte 0
; ----------
; Simple Array Descriptor Indices: Use an index with zSimAryDesVec to fetch array info.
BaseAddress = 0 ; L
ItemSize = BaseAddress + 3 ; B
MaxIndex = ItemSize + 1 ; B
TopIndex = MaxIndex + 1 ; B
IndexMode = TopIndex + 1 ; B
Term = IndexMode + 1 ; B
Tab = Term + 1 ; B
; SIMPLE ARRAY OPERATIONS
; ----------------------------------------------------------------------
; Array Management
; ----------------------------------------------------------------------
Init ldx #<SimArrayHdl
ldy #>SimArrayHdl
lda #MaxSimArrays
jmp Hdl_Create$
Create pha
txa
pha
ldx #<SimArrayHdl
ldy #>SimArrayHdl
jsr Hdl_Allocate$
bcc @C1 ; OKAY
pla ; ERROR
tax
pla
rts
; Copy Descriptor address into a list indexed by its Handle
@C1 asl ; Array Index x 2 for storing in Word list
tay
pla
sta SimAryDescAdr + 1,Y
pla
sta SimAryDescAdr,Y
tya
lsr
clc ; OKAY
rts ; Return with Array Handle
Delete ldx #<SimArrayHdl
ldy #>SimArrayHdl
jsr Hdl_Deallocate$
; ----------------------------------------------------------------------
; Interrogation
; ----------------------------------------------------------------------
; ----------
; SimAryGetBase
; Get base address of array
; ----------
SimAryGetBase jsr IniSimAryDesVec
ldy #BaseAddress
lda (zSimAryDesVec),Y
pha
iny
lda (zSimAryDesVec),Y
tax
iny
lda (zSimAryDesVec),Y
tay
pla
rts
; ----------
; SimAryGetTab
; Get Tab
; ----------
SimAryGetTab jsr IniSimAryDesVec
ldy #Tab
lda (zSimAryDesVec),Y
rts
; ----------
; SimAryGetIndexMode
; Get index mode
; ----------
SimAryGetIndexMode jsr IniSimAryDesVec
ldy #IndexMode
lda (zSimAryDesVec),Y
rts
; ----------
; SimAryGetItemAddress
; Get item address
; ----------
SimAryGetItemAddress jsr IniSimAryDesVec
; SimAryItemSize us set up here because SimAryInitXfer is bypassed
ldy #ItemSize
lda (zSimAryDesVec),Y
sta SimAryItemSize
jsr CalcItmAdr
lda zA1$
ldx zA1$ + 1
ldy zA1$ + 2
rts
; ----------
; SimAryGetItemSize
; Get item size
; ----------
SimAryGetItemSize jsr IniSimAryDesVec
ldy #ItemSize
lda (zSimAryDesVec),Y
rts
; ----------
; SimAryGetMaxIndex
; Get max index
; ----------
SimAryGetMaxIndex jsr IniSimAryDesVec
ldy #MaxIndex
lda (zSimAryDesVec),Y
rts
; ----------
; SimAryGetTopIndex
; Get top index
; ----------
SimAryGetTopIndex jsr IniSimAryDesVec
ldy #TopIndex
lda (zSimAryDesVec),Y
rts
; ----------------------------------------------------------------------
; Configuration
; ----------------------------------------------------------------------
; ----------
; SimArySetBaseBank
; Set Bank (Ext byte)
; ----------
SimArySetBaseBank jsr IniSimAryDesVec
ldy #BaseAddress
iny
iny
txa
sta (zSimAryDesVec),Y
rts
; ----------
; SimArySetBaseAddress
; Set Address (16-bit)
; ----------
SimArySetBaseAddress jsr IniSimAryDesVec
ldy #BaseAddress
txa
sta (zSimAryDesVec),Y
lda CurSimAryY
sta (zSimAryDesVec),Y
rts
; ----------
; SimArySetTab
; Set Tab
; ----------
SimArySetTab jsr IniSimAryDesVec
ldy #Tab
txa
sta (zSimAryDesVec),Y
rts
; ----------
; SimArySetIndexMode
; Set index mode
; ----------
SimArySetIndexMode jsr IniSimAryDesVec
ldy #IndexMode
txa
sta (zSimAryDesVec),Y
rts
; ----------
; SimArySetTopIndex
; Set top index
; ----------
SimArySetTopIndex jsr IniSimAryDesVec
ldy #TopIndex
txa
sta (zSimAryDesVec),Y
rts
; ----------------------------------------------------------------------
; Array Item Operations - Read
; ----------------------------------------------------------------------
; ----------
; SimAryRead
; Read from array
; ----------
SimAryRead jsr IniSimAryDesVec
lda #1 ; For the Read Operation Flag
jmp SimAryWriteJ
; ----------------------------------------------------------------------
; Array Item Operations - Write
; ----------------------------------------------------------------------
; ----------
; SimAryClear
; Clear array item
; ----------
SimAryClear jsr IniSimAryDesVec
tya
pha ; Preserve Clear value
jsr SimAryInitXfer
ldy SimAryItemSize
pla ; Restore Clear value
@Loop sta (zTW1$),Y
dey
cpy #255
bne @Loop
@Exit jmp SimAryCleanExit
; ----------
; SimAryDeleteItem
; Delete Item (Note: No range testing - allows new data to be dragged in from above)
; ----------
SimAryDeleteItem jsr IniSimAryDesVec
stx SimAryDeleteIndex
jsr SimAryInitXfer
ldx CurSimAryX
inx
jsr CalcItmAdr ; Set Data address to zA1$ - safe if not using math routines
jmp @LEntry ; Entry point avoids incr of Src and Dst
@LMain lda SimAryItemSize ; Add ItemSize to ItemAddress
ldx #zTW1$
jsr ZPWAddB
lda #1
jsr ZPWAddB
lda SimAryItemSize ; Add ItemSize to Data
ldx #zA1$
jsr ZPWAddB
lda #1
jsr ZPWAddB
inc SimAryDeleteIndex
@LEntry ldy SimAryItemSize
@LInner lda (zA1$),Y ; 16-bit address at index (24-bit with SCPU)
sta (zTW1$),Y
dey
cpy #255
bne @LInner
lda SimAryDeleteIndex
cmp SimAryMaxIndex
bne @LMain
; Test TopIndex for possible Decr.
@Exit ldy #TopIndex
lda (zSimAryDesVec),Y
beq @ExitC
sec
sbc #1
sta (zSimAryDesVec),Y
@ExitC jmp SimAryCleanExit
; ----------
; SimAryWrite
; Write to array
; Termination conditions handled: 0 (default, 1 and 2)
; ----------
SimAryWrite jsr IniSimAryDesVec
lda #0
SimAryWriteJ sta SimAryReadOperFlag
jsr SimAryInitXfer
ldy #Term
lda (zSimAryDesVec),Y
pha
lda SimAryReadOperFlag
bne @ReadOper
ldy CurSimAryY
ldx #ztW1$
bne @Cont1 ; Branch Always
@ReadOper ldx CurSimAryY
ldy #ztW1$
; Termination conditions
@Cont1 pla
cmp #0
beq @Term0
cmp #1
beq @Term1
cmp #2
beq @Term2
cmp #3
beq @Term3
@Term0 sty @T0LoopR + 1
stx @T0LoopW + 1
ldy SimAryItemSize
@T0LoopR lda ($00),Y ; 16-bit address at index (24-bit with SCPU)
@T0LoopW sta (zTW1$),Y
dey
cpy #255
bne @T0LoopR
beq @Exit
; End marker = 0
@Term1 lda #0
@Term1J sty @T1LoopR + 1
stx @T1LoopW + 1
sta @Term1W + 1
@T1LoopR lda ($00),Y ; 16-bit address at index (24-bit with SCPU)
@Term1W cmp #0 ; Termination condition
beq @Exit
@T1LoopW sta (zTW1$),Y
iny
bne @T1LoopR
@Exit jmp SimAryCleanExit
; End marker = 255
@Term2 lda #255
bne @Term1J ; Always
; Run-length value 0-255
@Term3 sty @Term3R + 1
ldy #0
@Term3R lda ($00),Y
sta SimAryItemSize
ldy @Term3R + 1
jmp @Term0
; ----------
; SimAryAdd
; Write to array with TopIndex as index
; ----------
SimAryAdd jsr SimAryGetTopIndex
sta CurSimAryX
ldy #TopIndex
lda (zSimAryDesVec),Y
ldy #MaxIndex
cmp (zSimAryDesVec),Y
bne @Exit
; Incr TopIndex if <MaxIndex
clc
adc #1
sta (zSimAryDesVec),Y
@Exit tax
ldy CurSimAryY
jmp SimAryWriteJ
; Handle Incr flags, restore CPU registers.
SimAryCleanExit ldy #IndexMode
lda (zSimAryDesVec),Y
beq @Exit
jsr SimAryDataIdxInc
@Exit jmp SimAryExit
; ----------------------------------------------------------------------
; Subroutines
; ----------------------------------------------------------------------
; ----------
; SimAryInitXfer : rY SimAryItemSize
; Used by methods that xfer data between array item and memory.
; ----------
SimAryInitXfer
; Store Max Index
ldy #MaxIndex
lda (zSimAryDesVec),Y
sta SimAryMaxIndex
; rY = Item size
ldy #ItemSize
lda (zSimAryDesVec),Y
sta SimAryItemSize
; Set SimAryDataVec
ldx CurSimAryX
jsr CalcItmAdr
ldx CurSimAryY
stx SimAryDataVec
; Set zTW1$
ldx #zTW1$
lda zA1$
sta $00,X
lda zA1$ + 1
sta $01,X
lda zA1$ + 2
sta $02,X
rts
; ----------
; SimAryDataIdxInc
; Increment data vector and/or Index. On entry, rA contains the incr flag.
; ----------
SimAryDataIdxInc lsr ; Test Bit 0 (Incr Index)
bcc @SimAryDataInc
; Incr index to be returned in rX
inc CurSimAryX
@SimAryDataInc lsr ; Test Bit 1 (Incr Data)
bcc @Exit
ldx SimAryDataVec
ldy #Tab
lda (zSimAryDesVec),Y
bne @Tab
; Incr data address in zp vector to which rY points
lda SimAryItemSize
jsr ZPWAddB
lda #1 ; +1 needed because item byte size is 1-256
@Tab jmp ZPWAddB
@Exit rts
; ----------
; ZPWAddB
; Add rA to rX ZP Word
; ----------
ZPWAddB clc
adc $00,X
sta $00,X
lda #0
adc $01,X
sta $01,X
rts
; Clean exit, restore registers
SimAryExit lda CurSimAryA
ldx CurSimAryX
ldy CurSimAryY
rts
; ----------
; IniSimAryDesVec
; Copy Descriptor address into its ZP vector
; ----------
IniSimAryDesVec sta CurSimAryA
stx CurSimAryX
sty CurSimAryY
tya
pha
lda CurSimAryA
asl ; Array Handle * 2
tay
lda SimAryDescAdr,Y
sta zSimAryDesVec
lda SimAryDescAdr + 1,Y
sta zSimAryDesVec + 1
pla
tay
lda CurSimAryA
rts
; ----------
; CalcItmAdr( rX Index ) : rAXY
; Multiply rX + 1 by Item Size
; ItemSize begins at 1 but is 0-based, so add 1 before it is used as a multiplier.
; ----------
CalcItmAdr stx zA1$
lda #0
sta zA1$ + 1
clc
lda SimAryItemSize
adc #1
sta zA2$
lda #0 ; Add 0, only affected by a carry
adc #0
sta zA2$ + 1
jsr MultWWL$ ; Mult Index by Item size, Long result
; Add Base Address to zA1$
ldy #BaseAddress
clc
@Loop lda (zSimAryDesVec),Y
adc zA1$ - BaseAddress,Y
sta zA1$ - BaseAddress,Y
iny
cpy #3
bne @Loop
rts