Description
Array Manager provides a way to coerce a block of memory within a 24-bit address space to a fixed item-length array.
An Array can contain 0 to 65535 Items.
An Item can be 1 to 65536 bytes in length, although that length is the same for each item in the Array.
Each Array can have a unique Record Type associated with it and there are operations to support Record Operations on the Items.
Read/Write operations are supported by a dynamic Cursor.
The Cursor Step is a signed 8-bit integer that steps each operation. The Cursor operation modes include constraints - in which Lower and Upper constraints are recognised, and Loop Flag - which lets the Cursor either Wrap or Stop at a Constraint or Array Boundary.
Caching is used for commonly accessed information that would normally be calculated. Also, the most recently used Array Handle is stored, and is used by some routines.
Why?
It tames memory and serves as a structural base for related tools.
Integration
Although presented here in isolation, the Array Manager slots neatly into a suite of tools I've written such as Record Manager and Function Manager. Plus there are the soon-to-come goodies including Search/Replace, Binary Set Operations and a General Purpose Serialiser.
Design Overview
The Array Descriptor (as well as the actual Array data) can be created in a static block by the user in order to retain direct access to its elements. It maintains an external reference to itself, which allows other programs to access its information via a vector/index system. This minimises the amount of memory the Array Manager reserves to just the handle list and a table of addresses.
Final Notes
The code is spread across several Project Files, and so some refitting will have to be done to get them to work. Once it's all complete I'll make the full set of project files available for download.
The Item Insert, Delete and Clear subroutines are not present, but are listed in the comments.
The Transfer Routine is temporary. It is a safe 16-bit only transfer routine and works only in the first 64K of RAM.
Some of the subroutines in the main Array code may seem wasteful, but were written like that for bug-testing purposes. I'll optimise the code for the final release, and will update the older comments.
Bonus: The Record system can be used as an indexed linked list.
Subroutines
The table below, plus the examples elsewhere, should be self-explanatory.
Code: Select all
; ARRAY OPERATIONS
; Init
; Create array: Create ( rAX Base ) : rA Handle, rSC ErrorF
; Delete array: Delete ( rA Handle ) : rSC ErrorF
; Clear array: Clear ( rA Handle )
; Get Array Address: GetAryAdr ( rA Handle ) : rAXY Address
; Get Array Max Index: GetAryMaxIdx ( rA Handle ) : rAX Address
; Get Array Top Index: GetAryTopIdx ( rA Handle ) : rAX Address
; Get Current Array: GetCurAry : rA Handle
; Set Array Top Index: SetAryTopIdx ( rA Handle, rXY Top Index )
; Set Current Array: SetCurAry ( rA Handle )
; ITEM OPERATIONS
; Get Array Item Size: GetAryItmSiz ( rA Handle ) : rAX Address
; Get Array Item Address: GetAryItmAdr ( rA Array Handle, rXY Item Index ) : rAXY Address
; Set Array Item Size: SetAryItmSiz ( rA Handle, rXY Item Size )
; CURSOR OPERATIONS
; Get Array Cursor Position: GetAryCsrPos ( rA Handle ) : rAX
; Get Array Cursor Address: GetAryCsrAdr ( rA Handle ) : rAXY
; Get Array Cursor Mode: GetAryCsrMod ( rA Handle ) : rA
; Get Array Cursor Step: GetAryCsrStp ( rA Handle ) : rA
; Get Array Cursor Min: GetAryCsrMin ( rA Handle ) : rAX
; Get Array Cursor Max: GetAryCsrMax ( rA Handle ) : rAX
; Set Array Cursor Position: SetAryCsrPos (.A Handle, rXY Cursor Position )
; Set Array Cursor Mode: SetAryCsrMod ( rA Handle, rX Cursor Mode )
; Set Array Cursor Step: SetAryCsrStp ( rA Handle, rX Cursor Step )
; Set Array Cursor Min: SetAryCsrMin ( rA Handle, rXY Cursor Min )
; Set Array Cursor Max: SetAryCsrMax ( rA Handle, rXY Cursor Max )
; RECORD OPERATIONS
; Get Array Record Type: GetAryRecType ( rA Handle ) : rA
; Get Current Field: GetAryCurFld ( rA Handle ) : rA
; Get Current Field Datatype: GetAryCurFldDat ( rA Handle ) : rA (via Record Manager)
; Get Current Field Length: GetAryCurFldLen ( rA Handle ) : rAX (via Record Manager)
; Get Current Field Offset: GetAryCurFldOs ( rA Handle ) : rAX (via Record Manager)
; Get Cursors' Current Field Address: GetAryCsrFldAdr ( rA Handle ) : rAXY (Calculated)
; Set Array Record Type: SetAryRecType ( rA Handle B, rX Record Type )
; Set Current Field: SetAryCurFld ( rA Handle, rX Field )
; FIELD-CURSOR OPERATIONS
; Write Field: CsrFldWrite ( rAXY 24-bit Source (uses Current Array) )
; Read Field: CsrFldRead ( rAXY 24-bit Destination (uses Current Array) )
; ITEM-CURSOR OPERATIONS
; Write Item: CsrWrite ( rAXY 24-bit Source (uses Current Array) )
; Read Item: CsrRead ( rAXY 24-bit Destination (uses Current Array) )
; ***TODO
; DeleteItem: DelItem ( rA Handle )
; InsertItem: InsItem ( rAXY 24-bit Source (uses Current Array) )
; Clear Item: ClrItem ( rA Handle, rX Value, rY Direction )
Code: Select all
jsr Ary_Init$
; Ary_Create
lda #<Array1Des
ldx #>Array1Des
jsr Ary_Create$
; Set the Cursor to the 2nd Item
lda Array1Des
ldx #<$0001
ldy #>$0001
jsr Ary_SetAryCsrPos$
; Copy the 2nd Array Item to Text Screen Line 1 (24-bit address)
lda #<$0400
ldx #>$0400
ldy #0
jsr Ary_CsrRead$
; The Cursor automatically stepped forward. This copies the 3rd Array Item to Screen line 2
lda #<$0428
ldx #>$0428
ldy #0
jmp Ary_CsrRead$
Array1Des
@Id byte 255
@Base word Array1Base
byte 0
@ItmSiz word 5 ; Actual length in bytes
@MaxIdx word 4
@TopIdx word 3
@CleafF byte 0
@RecTyp byte 255
@CurFld byte 0
@CurFldOs word 0
@CsrPos word 0
@CsrAdr byte 0,0,0
@CsrMod byte 0 ; Bit 1 = Wrap, Bit 2 = Constrain, Bit
@CsrStp byte 1
@CsrMin word 0
@CsrMax word 0
Array1Base ; 5 Items, each 6 bytes in size
byte 00,01,02,03,04,06 ; Index 0
byte 10,11,12,13,14,15 ; Index 1
byte 20,21,22,23,24,26 ; Index 2
byte 30,31,32,33,34,36 ; Index 3
byte 00,00,00,00,00,00 ; Index 4
Code: Select all
;* = $C600
; --------------------------
; Array Manager
; --------------------------
; zArrayManager$ = $FB ; If isolated
; ----------
; ZERO-PAGE
zAryDesVec = zArrayMan$ ; 2 bytes
zAryDesVal = zAryDesVec + 2 ; 3 bytes, usually address in reverse E,H,L order
; ----------
; PUBLIC
Ary_Init$ = Init
Ary_Create$ = Create
Ary_Delete$ = Delete
Ary_Clear$ = Clear
Ary_GetAryAdr$ = GetAryAdr
Ary_GetAryMaxIdx$ = GetAryMaxIdx
Ary_GetAryTopIdx$ = GetAryTopIdx
Ary_GetCurAry$ = GetCurAry
Ary_SetAryTopIdx$ = SetAryTopIdx
Ary_SetCurAry$ = SetCurAry
Ary_GetAryItmSiz$ = GetAryItmSiz
Ary_GetAryItmAdr$ = GetAryItmAdr
Ary_SetAryItmSiz$ = SetAryItmSiz
Ary_GetAryCsrPos$ = GetAryCsrPos
Ary_GetAryCsrAdr$ = GetAryCsrAdr
Ary_GetAryCsrMod$ = GetAryCsrMod
Ary_GetAryCsrStp$ = GetAryCsrStp
Ary_GetAryCsrMin$ = GetAryCsrMin
Ary_GetAryCsrMax$ = GetAryCsrMax
Ary_SetAryCsrPos$ = SetAryCsrPos
Ary_SetAryCsrMod$ = SetAryCsrMod
Ary_SetAryCsrStp$ = SetAryCsrStp
Ary_SetAryCsrMin$ = SetAryCsrMin
Ary_SetAryCsrMax$ = SetAryCsrMax
Ary_GetAryRecType$ = GetAryRecType
Ary_GetAryCurFld$ = GetAryCurFld
Ary_GetAryCurFldDat$ = GetAryCurFldDat
Ary_GetAryCurFldLen$ = GetAryCurFldLen
Ary_GetAryCurFldOs$ = GetAryCurFldOs
Ary_GetAryCsrFldAdr$ = GetAryCsrFldAdr
Ary_SetAryRecType$ = SetAryRecType
Ary_SetAryCurFld$ = SetAryCurFld
Ary_CsrFldWrite$ = CsrFldWrite
Ary_CsrFldRead$ = CsrFldRead
Ary_CsrWrite$ = CsrWrite
Ary_CsrRead$ = CsrRead
Ary_DelItem$ = DelItem
Ary_InsItem$ = InsItem
Ary_ClrItem$ = ClrItem
; ----------
; LOCAL
MaxArrays = 16
AryDescAdr words MaxArrays
ArrayHdl byte 0,0
bytes MaxArrays
CurArray byte 0 ; Current array. Negates need to pass in Array Handle for some operations
; ----------
#Region ; Description
; ARRAY OPERATIONS
; Init
; Create array: Create ( rAX Base ) : rA Handle, rSC ErrorF
; Delete array: Delete ( rA Handle ) : rSC ErrorF
; Clear array: Clear ( rA Handle )
; Get Array Address: GetAryAdr ( rA Handle ) : rAXY Address
; Get Array Max Index: GetAryMaxIdx ( rA Handle ) : rAX Address
; Get Array Top Index: GetAryTopIdx ( rA Handle ) : rAX Address
; Get Current Array: GetCurAry : rA Handle
; Set Array Top Index: SetAryTopIdx ( rA Handle, rXY Top Index )
; Set Current Array: SetCurAry ( rA Handle )
; ITEM OPERATIONS
; Get Array Item Size: GetAryItmSiz ( rA Handle ) : rAX Address
; Get Array Item Address: GetAryItmAdr ( rA Array Handle, rXY Item Index ) : rAXY Address
; Set Array Item Size: SetAryItmSiz ( rA Handle, rXY Item Size )
; CURSOR OPERATIONS
; Get Array Cursor Position: GetAryCsrPos ( rA Handle ) : rAX
; Get Array Cursor Address: GetAryCsrAdr ( rA Handle ) : rAXY
; Get Array Cursor Mode: GetAryCsrMod ( rA Handle ) : rA
; Get Array Cursor Step: GetAryCsrStp ( rA Handle ) : rA
; Get Array Cursor Min: GetAryCsrMin ( rA Handle ) : rAX
; Get Array Cursor Max: GetAryCsrMax ( rA Handle ) : rAX
; Set Array Cursor Position: SetAryCsrPos (.A Handle, rXY Cursor Position )
; Set Array Cursor Mode: SetAryCsrMod ( rA Handle, rX Cursor Mode )
; Set Array Cursor Step: SetAryCsrStp ( rA Handle, rX Cursor Step )
; Set Array Cursor Min: SetAryCsrMin ( rA Handle, rXY Cursor Min )
; Set Array Cursor Max: SetAryCsrMax ( rA Handle, rXY Cursor Max )
; RECORD OPERATIONS
; Get Array Record Type: GetAryRecType ( rA Handle ) : rA
; Get Current Field: GetAryCurFld ( rA Handle ) : rA
; Get Current Field Datatype: GetAryCurFldDat ( rA Handle ) : rA (via Record Manager)
; Get Current Field Length: GetAryCurFldLen ( rA Handle ) : rAX (via Record Manager)
; Get Current Field Offset: GetAryCurFldOs ( rA Handle ) : rAX (via Record Manager)
; Get Cursor's' Current Field Address: GetAryCsrFldAdr ( rA Handle ) : rAXY (Calculated)
; Set Array Record Type: SetAryRecType ( rA Handle B, rX Record Type )
; Set Current Field: SetAryCurFld ( rA Handle, rX Field )
; FIELD-CURSOR OPERATIONS
; Write Field: CsrFldWrite ( rAXY 24-bit Source (uses Current Array) )
; Read Field: CsrFldRead ( rAXY 24-bit Destination (uses Current Array) )
; ITEM-CURSOR OPERATIONS
; Write Item: CsrWrite ( rAXY 24-bit Source (uses Current Array) )
; Read Item: CsrRead ( rAXY 24-bit Destination (uses Current Array) )
; ***TODO
; DeleteItem: DelItem ( rA Handle )
; InsertItem: InsItem ( rAXY 16-bit Source (uses Current Array) )
; Clear Item: ClrItem ( rA Handle, rX Value, rY Direction )
; ----------
; NOTES
; Array Info is a static block of data that describes an array.
; The AryDescAdr vectors point to it, indexed by a Handle.
;
; Before using routines that use the Array Desc Index, set the vector
; - Use PIniAryDesVec to preserve registers, or UIniAryDesVec which will modify registers.
;
; To fetch a value from the Array Info, set .Y with the Array Desc Index, eg Base=0
; then call GetAryDesX[Y], where X=L,W or B, and Y is the A,X or Y - the first Register
; in which to place the sequence.
; If left clear, the values can be obtained from zAryDesVal.
;To set a value, from Array Info, set .Y with the Array Desc Index, eg Base=0
; then call SetAryDesX[Y]. Y can be A or X only. In most cases it's easier to
; just set zAryDesVal first then call SetAryDesX.
#EndRegion
; Array Descriptor Indices: Use an index with zAryDesVec to fetch array info.
ArrayId = 0 ; B
BaseAddress = ArrayID + 1 ; L
ItemSize = BaseAddress + 3 ; W
MaxIndex = ItemSize + 2 ; W
TopIndex = MaxIndex + 2 ; W - Highest used, used by Add() and Cursor loop
ClearF = TopIndex + 2 ; B - If no values Added
; Record indices
RecType = ClearF + 1 ; B
CurField = RecType + 1 ; B
; Record cache
CurFldOs = CurField + 1 ; W - Cached when RecordType or CurField changes. Needed to get address at field
; Cursor indices
CsrPos = CurFldOs + 2 ; W
CsrAdr = CsrPos + 2 ; L
CsrMode = CsrAdr + 3 ; B ; Bit 1 = Wrap, Bit 2 = Constrain, Bit
CsrStep = CsrMode + 1 ; B
CsrMin = CsrStep + 1 ; W
CsrMax = CsrMin + 2 ; W
#Region ; Array Operations
; ARRAY OPERATIONS
; Array Management
Init ldx #<ArrayHdl
ldy #>ArrayHdl
lda #MaxArrays
jmp Hdl_Create$
Create pha
txa
pha
ldx #<ArrayHdl
ldy #>ArrayHdl
jsr Hdl_Allocate$
bcc @C1 ; OKAY
pla ; ERROR
tax
pla
rts
@C1 asl ; Array Index x 2 for storing in Word list
tay
pla
sta AryDescAdr + 1,Y
sta zAryDesVec + 1
pla
sta AryDescAdr,Y
sta zAryDesVec
; Store valid Array Id (Handle). It's useful for user code to fetch the handle from here.
; A value of 255 means it is unallocated.
tya
lsr
ldy #ArrayId
sta (zAryDesVec),Y
pha ; Preserve Handle
; Initialise the Cursor Address cache
jsr ICalcCsrPosAdr ; Calculate the Cursor Address
jsr ISetAryCsrAdr ; Put rAXY into the Cursor Address cache
pla ; Retrieve Handle
clc ; OKAY
rts ; Return with Array Handle
Delete ldx #<ArrayHdl
ldy #>ArrayHdl
jsr Hdl_Deallocate$
; Set ArrayId to 255
@C1 jsr PIniAryDesVec ; NOTE: This sets Current Array to the deallocated ID.
lda #255
ldy #ArrayId
sta (zAryDesVec),Y
rts
rts
Clear jsr PIniAryDesVec
; ClearF = 1
ldy #ClearF
lda #1
sta (zAryDesVec),Y
lda #0
ldx #3
@L1 ldy @Table,X ; Clear a set of value caches
sta (zAryDesVec),Y
iny
sta (zAryDesVec),Y
dex
bpl @L1
rts
@Table byte TopIndex, CsrPos, CsrMin, CsrMax ; Lookup table of some Array Descriptor indices
; Array Interrogation
GetAryAdr jsr PIniAryDesVec
IGetAryAdr ldy #BaseAddress
jmp GetAryDesLA
GetAryMaxIdx jsr PIniAryDesVec
IGetAryMaxIdx ldy #MaxIndex
jmp GetAryDesWA
GetAryTopIdx jsr PIniAryDesVec
IGetAryTopIdx ldy #TopIndex
jmp GetAryDesWA
GetCurAry lda CurArray
rts
; Array Modification
SetAryTopIdx jsr PIniAryDesVec
ISetAryTopIdx stx zAryDesVal
sty zAryDesVal + 1
ldy #TopIndex
jmp SetAryDesW
SetCurAry sta CurArray
rts
#EndRegion
#Region ; Item Operations
; ITEM OPERATIONS
; Item Interrogation
GetAryItmSiz jsr PIniAryDesVec
IGetAryItmSiz ldy #ItemSize
jmp GetAryDesWA
GetAryItmAdr jsr PIniAryDesVec ; slow, brute-force calculation. Use if Item <> Cursor Position
IGetAryItmAdr jmp CalcAryXYAdr
SetAryItmSiz jsr PIniAryDesVec
ISetAryItmSiz stx zAryDesVal
sty zAryDesVal + 1
ldy #ItemSize
jmp SetAryDesW
#EndRegion
#Region ; Cursor Operations
; CURSOR OPERATIONS
; Cursor Interrogation
GetAryCsrPos jsr PIniAryDesVec
IGetAryCsrPos ldy #CsrPos
jmp GetAryDesWA
GetAryCsrAdr jsr PIniAryDesVec
IGetAryCsrAdr ldy #CsrAdr
jmp GetAryDesLA
GetAryCsrMod jsr PIniAryDesVec
IGetAryCsrMod ldy #CsrMode
jmp GetAryDesBA
GetAryCsrStp jsr PIniAryDesVec
IGetAryCsrStp ldy #CsrStep
jmp GetAryDesBA
GetAryCsrMin jsr PIniAryDesVec
IGetAryCsrMin ldy #CsrMin
jmp GetAryDesWA
GetAryCsrMax jsr PIniAryDesVec
IGetAryCsrMax ldy #CsrMax
jmp GetAryDesWA
; Cursor Modification
SetAryCsrPos jsr PIniAryDesVec
ISetAryCsrPos stx zAryDesVal ; NOTE: New Item Address Info has to be set
sty zAryDesVal + 1
ldy #CsrPos
jsr SetAryDesW
; Calculate new Cursor Address, Long result in zA1
jsr CalcAryItemAdr
; Set Cursor Address
ISetAryCsrAdr sta zAryDesVal
stx zAryDesVal + 1
sty zAryDesVal + 2
ldy #CsrAdr
jmp SetAryDesL
SetAryCsrMod jsr PIniAryDesVec
ISetAryCsrMod stx zAryDesVal
ldy #CsrMode
jmp SetAryDesB
SetAryCsrStp jsr PIniAryDesVec
ISetAryCsrStp stx zAryDesVal
ldy #CsrStep
jmp SetAryDesB
SetAryCsrMin jsr PIniAryDesVec
ISetAryCsrMin stx zAryDesVal
sty zAryDesVal + 1
ldy #CsrMin
jmp SetAryDesW
SetAryCsrMax jsr PIniAryDesVec
ISetAryCsrMax stx zAryDesVal
sty zAryDesVal + 1
ldy #CsrMax
jmp SetAryDesW
#EndRegion
#Region ; Record Operations
; RECORD OPERATIONS
; Record interrogation
GetAryRecType jsr PIniAryDesVec
IGetAryRecType ldy #RecType
jmp GetAryDesBA
GetAryCurFld jsr PIniAryDesVec
IGetAryCurFld ldy #CurField
jmp GetAryDesBA
GetAryCurFldDat jsr PIniAryDesVec
IGetAryCurFldDat ldy #RecType
jsr GetAryDesBA
pha
ldy #CurField
jsr GetAryDesBA
tax
pla
jmp Rec_GetFldDt$ ; A=RecType, X=Field, Result in A
GetAryCurFldLen jsr PIniAryDesVec
IGetAryCurFldLen ldy #RecType
jsr GetAryDesBA
pha
ldy #CurField
jsr GetAryDesBA
tax
pla
jmp Rec_GetFldLen$ ; A=RecType, X=Field, Result in AX
; This is calculated, but I've decided to cache it instead. Will require a rename for its use.
GetAryCurFldOs jsr PIniAryDesVec
IGetAryCurFldOs ldy #RecType
jsr GetAryDesBA
pha
ldy #CurField
jsr GetAryDesBA
tax
pla
IGetAryFldOs jsr Rec_GetFldOs$ ; A=RecType, X=Field, Result in AX
; Store in cache, return result in rAX
ldy #CurFldOs
sta (zAryDesVec),Y
pha
txa
iny
sta (zAryDesVec),Y
tax
pla
rts
; Gets address of field at current Cursor
GetAryCsrFldAdr jsr PIniAryDesVec
IGetAryCsrFldAdr jsr IGetAryCsrAdr ; Address of Item at Cursor
sta zA1$
stx zA1$ + 1
sty zA1$ + 2
ldy #CurFldOs ; Get Field Offset
jsr GetAryDesWA
sta zA2$
stx zA2$ + 1
; Add Field Offset to Item address
jmp AddLWLA$ ; Long + Word = Long, return in Registers
; Record modification
SetAryRecType jsr PIniAryDesVec
ldy #RecType
txa
jsr SetAryDesBA
lda #0
ldy #CurField
jsr SetAryDesBA ; Set Current Field to 0
jmp IGetAryCurFldOs ; Cache the Offset for the Current Field
SetAryCurFld jsr PIniAryDesVec
txa
ldy #CurField
jsr SetAryDesBA
jmp IGetAryCurFldOs ; Cache the Offset for this Field
#EndRegion
#Region ; Field-Cursor Operations
; FIELD-CURSOR OPERATIONS
; Note, incomplete. Does not use Field Datatype to constrain or convert.
; *** To use callbacks for conversion?
AryOpMode byte 0
CsrWrapF byte 0
CsrConstF byte 0
CsrStepValue byte 0
CsrLim word 0
CsrLimWrap word 0
CsrValue word 0
AOWrite = 0
AORead = 1
AOVerify = 2
AOStep = 3
; Write to Cursor Item Field from Long Address, AXY = Source Address, uses Current Array
CsrFldWrite jsr AIniAryDesVec
ICsrFldWrite sta zTFerS$
stx zTFerS$ + 1
sty zTFerS$ + 2
jsr IGetAryCsrFldAdr ; Get Long Address of Field at Cursor Position
sta zTFerD$
stx zTFerD$ + 1
sty zTFerD$ + 2
jsr IGetAryCurFldLen ; Get Field Size in AX
pha
txa
tay
pla
tax
jmp CsrWriteC1
; Read from Cursor Item Field to Long Address, AXY = Source Address, uses Current Array
CsrFldRead jsr AIniAryDesVec
ICsrFldRead sta zTFerD$
stx zTFerD$ + 1
sty zTFerD$ + 2
jsr IGetAryCsrFldAdr ; Get Long Address of Field at Cursor Position
sta zTFerS$
stx zTFerS$ + 1
sty zTFerS$ + 2
jsr IGetAryCurFldLen ; Get Field Size in AX
pha
txa
tay
pla
tax
jmp CsrReadC1
#EndRegion
#Region ; Item-Cursor Operations
; ITEM-CURSOR OPERATIONS (Done)
; Write Item: CsrWrite ( .X.Y 16-bit Source (uses Current Array) )
; Read Item: CsrRead ( .A.X.Y 16-bit Destination (uses Current Array) ) : .A.X.Y Length
; ***TODO
; DeleteItem: DelItem ( .A Handle )
; InsertItem: InsItem ( .A.X.Y 16-bit Source (uses Current Array) )
; Clear Item: ClrItem ( .A Handle, .X Value, .Y Direction )
; Write from Word Address, A = Array, XY = Source Address
CsrWriteWA jsr PIniAryDesVec
sta CurArray
lda #0 ; Ext byte 0
; Write AXY to Ary Csr Pos
; NOTE: Uses Current Array
; NOTE: Set up 16-bit transfer (temporary, until 24-bit transfer routine is written)
; Write to Cursor Item from Long Address, AXY = Source Address, uses Current Array
CsrWrite jsr AIniAryDesVec
ICsrWrite sta zTFerS$
stx zTFerS$ + 1
sty zTFerS$ + 2
ldy #CsrAdr ; Get Long Address at Cursor Position
jsr GetAryDesLA
sta zTFerD$
stx zTFerD$ + 1
sty zTFerD$ + 2
ldy #ItemSize
jsr GetAryDesWX
CsrWriteC1 stx zTFerL$
sty zTFerL$ + 1
lda #0
sta zTFerL$ + 2
; Setup for Array Data Operation subroutine
lda #AOWrite ; Operation mode, eg Read, Write or Verify
ldx #<TFer$ ; Setup Callback
ldy #>TFer$
jmp AryDatOper
; Read from Word Address, A = Array, XY = Destination Address
CsrReadWA jsr PIniAryDesVec
sta CurArray
lda #0 ; Ext byte 0
; Read to Long Address, AXY = Source Address, uses Current Array
CsrRead jsr AIniAryDesVec
ICsrRead sta zTFerD$
stx zTFerD$ + 1
sty zTFerD$ + 2
ldy #CsrAdr ; Get Long Address at Cursor Position
jsr GetAryDesLA
sta zTFerS$
stx zTFerS$ + 1
sty zTFerS$ + 2
ldy #ItemSize ; Get Item Size
jsr GetAryDesWX
CsrReadC1 stx zTFerL$
sty zTFerL$ + 1
lda #0
sta zTFerL$ + 2
; Setup for Array Data Operation subroutine
lda #AORead ; Operation mode, eg Read, Write or Verify
ldx #<TFer$ ; Setup Callback
ldy #>TFer$
jmp AryDatOper
#EndRegion
#Region ; Data Subroutines
; OPERATIONS
; Thee are a series of operations that are common to many of the routines (read, write, etc).
; For example, all operations that use the cursor share step movement, step constraints and wrap.
; The operation that uses these features is passed in as a Callback address to AryDatOper.
; NOTE 1: The design of AryDatOper is not optimal and I should try a different approach SITF.
; NOTE 2: There is no OOB range testing. It's left up to the user to ensure their step size
; does not go past a limit. Thus, beyond a step size of 1 there be dragons.
; NOTE 3: Single steps fwd or back could be optimised by adding/subbing field length
; instead of using the multiply routine, but I'm unsure how beneficial this will be.
; Manage cursor operation
AryDatOper sta AryOpMode ; Operation mode, eg Read, Write, Verify or Step
stx @Operation + 1 ; Setup Callback
sty @Operation + 2
lda AryOpMode
cmp #AOWrite
beq @Write
cmp #AOStep ; No Operation but still Step
beq @C1
bne @Operation ; Read and Verify operation
; This first section updates TopIndex if a write operation occurs,
; and so assumes that the operation
; may have been directed at an index higher than TopIndex.
@Write ldy #TopIndex ; If CsrPos > TopIndex then TopIndex = CsrPos
jsr GetAryDesWA
sta zTW1$
stx zTW1$ + 1
ldy #CsrPos
jsr GetAryDesWA
sta zTW2$
stx zTW2$ + 1
ldx #zTW1$
ldy #zTW2$
jsr CompareZpW
bcc @Operation ; Branch if Cursor Position <= Top Index
; Set new Top Index
lda zTW2$
ldx zTW2$ + 1
ldy #MaxIndex
jsr SetAryDesWA
@Operation jsr $0000; Callback for operation. Could be a Read, Write or Verify
; Get the Curser Step value. If it -1 or then apply Loop, Constraint or MinMax
@C1 ldy #CsrStep
jsr GetAryDesBA
beq @C1a
jmp StepExit
@C1a sta CsrStepValue
; Setup Cursor's min and max limits
ldy #CsrPos
jsr GetAryDesWA
sta CsrValue
stx CsrValue + 1
lda #0
sta CsrWrapF
sta CsrConstF
ldy #CsrMode
jsr GetAryDesBA
lsr
rol CsrWrapF
lsr
rol CsrConstF
; Check directiom
lda CsrStepValue
bpl StepFwd
; Cursor step backward - test for constraint, then test for loop
StepBkwd lda CsrConstF
bne @ConstLim
lda #0
tay
jmp @ConstLimC1
@ConstLim ldy #CsrMin
jsr GetAryDesWA
@ConstLimC1 sta CsrLim
stx CsrLim + 1
; Test Cursor Position against limit
lda CsrValue
cmp CsrLim
bne @BackStep
lda CsrValue + 1
cmp CsrLim + 1
beq @BackLimit
@BackStep clc
lda #0
sbc CsrStepValue
sta CsrStepValue
sec
lda CsrValue
sbc CsrStepValue
tax
lda CsrValue + 1
sbc CsrValue + 1
tay
jmp ISetAryCsrPos ; XY = New CsrPos
@BackLimit lda CsrWrapF
bne @BackLimit_C1
jmp StepExit ; Stop cursor
@BackLimit_C1 lda CsrConstF
bne @ConstWrap
ldy #MaxIndex
jsr GetAryDesWX
jmp ISetAryCsrPos ; XY = New CsrPos
@ConstWrap ldy #CsrMax
jsr GetAryDesWX
Jmp ISetAryCsrPos ; XY = New CsrPos
; Cursor step forward - test for constraint, then test for loop
StepFwd lda CsrConstF
bne @ConstLim
ldy #MaxIndex
jsr GetAryDesWA
jmp @ConstLimC1
@ConstLim ldy #CsrMax
jsr GetAryDesWA
@ConstLimC1 sta CsrLim
stx CsrLim + 1
; Test Cursor Position against limit
; NOTE: This does not test for Cursor Position outside constrained range.
; I think this should be retained as a "feature" because sometimes
; the user will want to temporarily break out from any MinMax constraints.
lda CsrValue
cmp CsrLim
bne @FwdStep
lda CsrValue + 1
cmp CsrLim + 1
beq @FwdLimit
; Add step value to Cursor Position
@FwdStep clc
lda CsrValue
adc CsrStepValue
tax
lda CsrValue + 1
adc CsrValue + 1
tay
jmp ISetAryCsrPos ; XY = New CsrPos
@FwdLimit lda CsrWrapF
beq StepExit ; Stop cursor
lda CsrConstF
bne @ConstWrap
ldx #0
ldy #0
Jmp ISetAryCsrPos ; XY = New CsrPos
@ConstWrap ldy #CsrMin
jsr GetAryDesWX
Jmp ISetAryCsrPos ; XY = New CsrPos
StepExit rts
; --------------------------------------------------
#Region ; Array Helper Subroutines
; HELPER SUBROUTINES
; The following routines are general get and set
; for Long, Word or Byte from Array Descriptor lookup.
GetAryDes stx zTB1$
tya
clc
adc zTB1$
tay
@Loop lda (zAryDesVec),Y
sta zAryDesVal,X
dey
dex
bpl @Loop
rts
; .A Handle, .Y index to Info item
GetAryDesLA ldx #2
jsr GetAryDes
lda zAryDesVal + 0
ldx zAryDesVal + 1
ldy zAryDesVal + 2
rts
GetAryDesWA ldx #1
jsr GetAryDes
lda zAryDesVal + 0
ldx zAryDesVal + 1
rts
GetAryDesWX ldx #1
jsr GetAryDes
ldx zAryDesVal + 0
ldy zAryDesVal + 1
rts
GetAryDesBA ldx #0
jsr GetAryDes
lda zAryDesVal + 0
rts
; Set Long, Word or Byte from Array Information
SetAryDes stx zTB1$
tya
clc
adc zTB1$
tay
@Loop lda zAryDesVal,X
sta (zAryDesVec),Y
dey
dex
bpl @Loop
rts
SetAryDesLA sta zAryDesVal
stx zAryDesVal + 1
sty zAryDesVal + 2
SetAryDesL ldx #2
jmp SetAryDes
SetAryDesWA sta zAryDesVal
stx zAryDesVal + 1
SetAryDesW ldx #1
jmp SetAryDes
SetAryDesBA sta zAryDesVal
SetAryDesB ldx #0
jmp SetAryDes
; Multiply .X.Y by Item Size then Add the 16-bit Base address, result returned in rAXY
; NOTE: The Mult Result may be > 16-bit, but the 17th bit is discarded.
CalcAryItemAdr ldx zAryDesVal
ldy zAryDesVal + 1
CalcAryXYAdr stx zA1$ ; 16-bit Index
sty zA1$ + 1
; ItemSize begins at 1 but is 0-based, so add 1 before it is used as a multiplier.
ldy #ItemSize
clc
lda (zAryDesVec),Y
adc #1
sta zA2$
iny
lda (zAryDesVec),Y
adc #0
sta zA2$ + 1
jsr MultWWL$ ; Mult Index by Item size, Long result
; Add Result to base address of array
ldx #2
ldy #BaseAddress
clc
@Loop lda (zAryDesVec),Y
adc zA1$ - BaseAddress,Y
sta zA1$ - BaseAddress,Y
iny
dex
bpl @Loop
lda zA1$
ldx zA1$ + 1
ldy zA1$ + 2
rts
; Calculate Address at current Cursor Position
ICalcCsrPosAdr ldy #CsrPos
lda (zAryDesVec),Y
tax
iny
lda (zAryDesVec),Y
tay
jmp CalcAryXYAdr
; Assign Arrays Descriptor to the vector. AXY preserved. Current Array byte is used.
AIniAryDesVec pha
txa
pha
tya
pha
lda CurArray
jmp PInArDeVeC
; Assign Arrays Descriptor to the vector. AXY preserved. Array handle in .A.
PIniAryDesVec sta CurArray
pha
txa
pha
tya
pha
lda CurArray
PInArDeVeC jsr UIniAryDesVec
pla
tay
pla
tax
pla
rts
UIniAryDesVec asl ; Array Handle
tay
lda AryDescAdr,Y
sta zAryDesVec,Y
lda AryDescAdr + 1,Y
sta zAryDesVec + 1,Y
rts
#EndRegion
These are in a seperate project file. There is little optimisation on the Multiply routines, and they will be replaced when I get time to do so. The transfer routine is 16-bit and will also be replaced soon.
Code: Select all
;* = $C110
#Region ; Handle Manager
; --------------------
; HANDLE MANAGER
; ----------
; PUBLIC
Hdl_Create$ = Hdl_Create
Hdl_Allocate$ = Hdl_Allocate
Hdl_Deallocate$ = Hdl_Deallocate
; ----------
; LOCAL
TermFlag$ byte 0
Profile$ byte 0
; ----------
; Create Handle
; Address = <.X >.Y
; Max Amount = .A (1..253)
Hdl_Create stx zTW1$
sty zTW1$ + 1
ldy #0
sta (zTW1$),Y ; Write Max
iny
sta (zTW1$),Y ; Write
tax
dex
@L1 iny ; Write sequence 0..n
txa
sta (zTW1$),Y
dex
cpx #255
bne @L1
rts
; Allocate Handle
; Address = <.X >.Y
; Return = .A
Hdl_Allocate stx zTW1$
sty zTW1$ + 1
ldy #1
lda (zTW1$),Y
bne @C1
sec
bcs @C2 ; ERROR
@C1 sec
sbc #1
sta (zTW1$),Y
tay ; Inc index to correct position
iny
iny
lda (zTW1$),Y
clc
@C2 rts
; Deallocate Handle
; Address = <.X >.Y
; Handle = .A
Hdl_Deallocate stx zTW1$
sty zTW1$ + 1
tax
ldy #1
lda (zTW1$),Y
dey
cmp (zTW1$),Y
bne @C1
sec ; ERROR
bcs @C2
@C1 clc
adc #1 ; Inc pointer
iny
sta (zTW1$),Y
tay
iny
txa ; Handle to return
sta (zTW1$),Y
clc
@C2 rts
#endregion
#Region ; Maths
; --------------------
; MATHS ROUTINES
; ----------
; PUBLIC
MultWWW$ = MultWWW
MultWWL$ = MultWWL
MultWWE$ = MultWWE
; ----------
; LOCAL
Z = 8
PreCalc_Mul16L$
byte <0*Z, <1*Z, <2*Z, <3*Z, <4*Z, <5*Z, <6*Z, <7*Z
byte <8*Z, <9*Z, <10*Z, <11*Z, <12*Z, <13*Z, <14*Z, <15*Z
PreCalc_Mul16H$
byte >0*Z, >1*Z, >2*Z, >3*Z, >4*Z, >5*Z, >6*Z, >7*Z
byte >8*Z, >9*Z, >10*Z, >11*Z, >12*Z, >13*Z, >14*Z, >15*Z
; ----------
; Multiply the 16-bit contents of zp Accu 1 & 2, result in 1 (16-bit result)
MultWWW ldx #0
stx zA3$
stx zA3$ + 1
ldy #16
bne @C2 ; Always
@C1 clc
lda zA3$
adc zA2$
sta zA3$
lda zA3$ + 1
adc zA2$ + 1
sta zA3$ + 1
@C2 lsr zA3$ + 1
ror zA3$
ror zA1$ + 1
ror zA1$
dey
bmi @Exit
bcc @C2
bcs @C1
@Exit rts
; Multiply the 16-bit contents of zp Accu 1 & 2, result in 1 (24-bit result)
MultWWL ldx #0
stx zA1$ + 2 ; Clear Ext byte
stx zA2$ + 2
stx zA3$
stx zA3$ + 1
stx zA3$ + 2
ldy #24
bne @C2 ; Always
@C1 clc
lda zA3$
adc zA2$
sta zA3$
lda zA3$ + 1
adc zA2$ + 1
sta zA3$ + 1
lda zA3$ + 2
adc zA2$ + 2
sta zA3$ + 2
@C2 lsr zA3$ + 2
ror zA3$ + 1
ror zA3$
ror zA1$ + 2
ror zA1$ + 1
ror zA1$
dey
bmi @Exit
bcc @C2
bcs @C1
@Exit rts
; Multiply the 32-bit contents of zp Accu 1 & 2, result in 1 (32-bit result)
MultWWE ldx #0
stx zA1$ + 2 ; Clear Ext byte
stx zA2$ + 2
stx zA1$ + 3 ; Clear 4th byte
stx zA2$ + 3
stx zA3$
stx zA3$ + 1
stx zA3$ + 2
stx zA3$ + 3
ldy #32
bne @C2 ; Always
@C1 clc
lda zA3$
adc zA3$
sta zA3$
lda zA3$ + 1
adc zA2$ + 1
sta zA3$ + 1
lda zA3$ + 2
adc zA2$ + 2
sta zA3$ + 2
lda zA3$ + 3
adc zA2$ + 3
sta zA3$ + 3
@C2 lsr zA3$ + 3
ror zA3$ + 2
ror zA3$ + 1
ror zA3$
ror zA1$ + 3
ror zA1$ + 2
ror zA1$ + 1
ror zA1$
dey
bmi @Exit
bcc @C2
bcs @C1
@Exit rts
; --------------------
; 16-bit Addition : .X byte width pf values to add. Carries into higher byte, 32-bit cutoff
AddAcc$ ldy #0
clc
@L1 lda zA1$,Y
adc zA2$,Y
sta zA3$,Y
iny
dex
bne @L1
cpy #4
bne @Exit ; cutoff at 32 bits
txa ; Carry into next byte
rol
sta zA3$,Y
@Exit rts
SubAcc$ ldy #0
sec
@L1 lda zA1$,Y
sbc zA2$,Y
sta zA3$,Y
iny
dex
bne @L1
cpy #4
bne @Exit ; cutoff at 32 bits
txa
sbc #0 ; Carry into next byte
sta zA3$,Y
@Exit lda zA3$ ; 4th byte will have to be recovered by the user
ldx zA3$ + 1
ldy zA3$ + 2
rts
@Table byte 3,2,1,0 ; Indexed to byte, word, long and extended
; --------------------
; Methods to add values
; .X = number of bytes to add 1=B, 2=W, 3=L 4=E
AddLLLA$ ldx #3
jsr AddAcc$
jmp GetAccLA$
AddLWLA$ lda #0 ; Set 3rd byte to 0
sta zA2$ + 2
ldx #3
jsr AddAcc$
jmp GetAccLA$
AddWWLA$ ldx #2
jsr AddAcc$
jmp GetAccLA$
AddWBLA$ lda #0 ; Set 2nd byte to 0
sta zA2$ + 1
ldx #2
jsr AddAcc$
jmp GetAccLA$
; Methods to return Accumulator results
GetAccLA$ lda zA3$
ldx zA3$ + 1
ldy zA3$ + 2
rts
GetAccWA$ lda zA3$
ldx zA3$ + 1
rts
GetAccWX$ ldx zA3$
ldy zA3$ + 1
rts
; Compare two zero-page 16-bit values, X and Y
CompareZpW lda $01,X
cmp $0001,Y
bcc @Exit
lda $00,X
cmp $0000,Y
bcc @Exit ; Branch if X < Y
@Exit rts
#EndRegion
#Region ; Safe Memory Transfer
; SAFE MEMORY TRANSFER
; Currently 16-bit, but will eventually be full 24-bit and support SuperRAM and REU.
; 16-bit safe memory transfer
; W Source, W Dest, W Length 1-65535
; Uses zTW1$-3
; ----------
; ZERO-PAGE
; NOTE: These are aligned with the related 24-bit addresses
zTFerS = zMemTransfer$
zTFerD = zTFerS + 3
zTFerL = zTFerD + 3
; ----------
; PUBLIC
TFer$ = Tfer16s
SwapTfrSD$ = SwapTfrSD
zTFerS$ = zTFerS
zTFerD$ = zTFerD
zTFerL$ = zTFerL
; ----------
; Swap Source and Destination
SwapTfrSD lda zTferS
sta zTferD
lda zTferS + 1
sta zTferD + 1
rts
Tfer16S ; 16 bit comparison
lda zTferS + 1
cmp zTferD + 1
bcc TferHi
lda zTferS
cmp zTferD
bcc TferHi ; Branch if Source < Destination
; Copy to a lower address
TferLo ldx zTferL + 1 ; Test > 255 bytes
beq @C1 ; Jump to the test for 0 length
; Copy 256-byte segments
ldy #0
@L1 lda (zTferS),Y ; First XFer Block - copies 256 bytes
sta (zTferD),Y ;
iny ;
bne @L1 ;
inc zTferS + 1 ;
inc zTferD + 1 ;
dex ;
bne @L1 ;
@C1 lda zTferL ; <256 bytes remaining?
beq @Exit
sta @TL1 + 1
; Copy remaining bytes
ldy #0
@L2 lda (zTferS),Y ;
sta (zTferD),Y ;
iny ;
@TL1 cpy #0 ;
bne @L2 ;
@Exit rts
; -----------
; Copy to a higher address
TferHi ldx zTferL + 1 ; Test > 255 bytes
beq @C2 ; Jump to the test for 0 length
dex
clc
lda zTferL
adc zTferS
sta zTFerS
txa
adc zTFerS + 1
sta zTFerS + 1
clc
lda zTferL
adc zTferD
sta zTFerD
txa
adc zTFerD + 1
sta zTFerD + 1
; Copy 256-byte segments
inx
ldy #255
@L1 lda (zTferS),Y ; First XFer Block - copies 256 bytes
sta (zTferD),Y ;
dey ;
bne @L1 ;
lda (zTferS),Y ;
sta (zTferD),Y ;
dec zTferS + 1 ;
dec zTferD + 1 ;
dex ;
bne @L1 ;
@C1 ldx zTFerL
beq @Exit
lda #0 ; Set Y to the remainder
clc
sbc zTferL
tay
; Copy remaining bytes
ldy #255
@L2 lda (zTferS),Y ;
sta (zTferD),Y ;
dey ;
dex ;
bne @L2 ;
@Exit rts
@C2 lda zTferL ; < 256 byte transfer
tax
tay
dey
jmp @L2
#EndRegion
This provides the zero-page base addresses used by other Project Files, and also lists common ZP addresses such as the 32-bit accumulators and temporary local workspaces.
Code: Select all
;* = $C100
; Workspace
; Other routines can break this up into useful spaces, eg zTW1$
zFunctionMan$ = $60 ; 20 bytes
zRecordMan$ = $74 ; 8 bytes allocated
zArrayMan$ = $7C ; 5 bytes allocated
zMemTransfer$ = $81 ; 9 bytes
zResShared$ = $8A
; 3 32-bit accumulators. 12 bytes
zA1$ = zResShared$ ; 4 bytes
zA2$ = zA1$ + 4 ; 4 bytes
zA3$ = zA2$ + 4 ; 4 bytes
; Temporary workspace for bytes. 4 bytes
zTB1$ = zA3$ + 4 ; 1 byte
zTB2$ = zTB1$ + 1 ; 1 byte
zTB3$ = zTB2$ + 1 ; 1 byte
zTB4$ = zTB3$ + 1 ; 1 byte
; Temporary workspace for words. 4 bytes
zTW1$ = zTB4$ + 1 ; 2 bytes
zTW2$ = zTW1$ + 2 ; 2 bytes
; Temporary workspace for Longs. 6 bytes
zTL1$ = zTB4$ + 1 ; 3 bytes
zTL2$ = zTL1$ + 3 ; 3 bytes
; Global Ext byte for read and write operations for which the lower 16-bits are provided.
ExtReadS$ byte 0
ExtWrite$ byte 0
; The CPU-Memory layout
; 0 = C64
; Bit 1 = SCPU + SuperRAM
; Bit 2 = SCPU + REU
; Bit 3 = SCPU
; Bit 4 = REU
CpuMem$ byte 8
; General Globals
ErrorF$ byte 0 ; Error Flag for most recent operation.
; Can be an index to an optional global error text message (useful for debugging).