Hi all,
below a piece of software for manage a dynamic local heap in 65C816 system.
The local heap will be entirely contained in a bank of 64k (the size of this local heap will be < $FFFF).
The starting of the heap is in variable hdrptr in page 0 and is not necessary to point to address $0000 of the bank.
Any idea for a global heap in which memory blocks (limited to 64k) may occupy 2 adjacent banks?
(Sorry, but the code is commented in italian)
Code:
; assembled with avocet 65C816 assembler
;----------------------------------------------------------
; Local Heap - only in one bank (max. 64K)
;----------------------------------------------------------
;
; Il blocco di heap locale viene gestito mediante un header ed una lista
; collegata di blocchi free.
; Struttura header del blocco heap:
;
; WORD bsize -> dimensione memoria libera nello heap
; WORD bnext -> puntatore al prossimo blocco libero
;
; in fase di inizializzazione bsize = tot. mem. heap - 4 e bnext = hdr + 4
;
; I blocchi free sono contenuti in una lista collegata con la seguente
; struttura:
;
; WORD bsize -> dimensione blocco + 1 (dispari -> blocco free)
; WORD bnext -> next blocco free (= 0 se e' l'ultimo)
;
; I blocchi allocati (non-free) hanno la seguente struttura:
;
; WORD bsize -> dimensione blocco (sempre pari)
; data -> blocco effettivo allocato di dimensione bsize
;
;
; Variabili pagina 0:
.PAGE0
nsize .DW ; dimensione blocco da allocare
bsize .DW ; dimensione vera blocco free
splitsz .DW ; dimensione blocco splittato
bfree .DW ; puntatore blocco free
hdrptr .DW ; puntatore header heap
MINFREESIZE .EQU $0008 ; minimo valore per allocazione
FIRSTPTR .EQU $0006 ; offset primo ptr valido
HEAPHDRSIZE .EQU $0004
curptr .EQU bfree
pred .EQU splitsz
.CODE
; alloca un blocco nello heap locale
; In - A = nsize (comprensivo dei 2 bytes necessari all'header del blocco)
; Y = puntatore all'header dello heap
; DP = 0 (pagina zero)
; DBR = banco contenente lo heap
; CPU in modo 16 bit
; Out - CF = 0 - X = ptr blocco
; CF = 1 se allocazione fallita
_NewHeapBlk:
.LONGA on
.LONGI on
sta nsize
sty hdrptr
tyx ; X = hdrptr
lda !$0000,x ; A = header.bsize
cmp nsize ; bsize - nsize
bcc ?08 ; bsize < nsize -> errore (no spazio)
?01: txy ; Y = pred blocco libero
ldx !$0002,y ; X = Y.bnext -> header blocco libero
beq ?08 ; errore - finiti i blocchi liberi
lda !$0000,x ; A = X.bsize
dec a ; vera dimensione del blocco free
cmp nsize ; X.bsize - nsize
bcc ?01 ; X.bsize < nsize -> next free block
beq ?06 ; X.bsize = nsize
; CF = 1 -> X.bsize > nsize
sta bsize ; salva bsize
sbc nsize ; A = eccesso di memoria del blocco
cmp #MINFREESIZE ; eccesso di memoria permette split blocco ?
bcs ?04 ; blocco free splittabile
lda bsize ; alloca blocco intero X.bsize
bra ?06
?04: ; splitta blocco free
; A = size blocco splittato
; Y = pred blocco free
; X = blocco free
sta splitsz ; salva dimensione blocco splittato
stx bfree ; X -> blocco free
txa
clc
adc nsize
tax ; X -> blocco splittato
lda splitsz
inc a ; marca blocco free
sta !$0000,x ; bsize nuovo blocco splittato
txa ; A = blocco split
ldx bfree
sta !$0002,x ; bfree.bnext = split
lda nsize ; nsize
?06: ; toglie dalla lista free il blocco X
; A = size blocco free
; Y = pred blocco free
; X = blocco free
sta !$0000,x ; X.bsize nuovo blocco allocato
lda !$0002,x ; X.bnext
sta !$0002,y ; Y.bnext = X.bnext
ldy hdrptr
sec
lda !$0000,y ; header.bsize
sbc !$0000,x ; header.bsize - X.bsize
sta !$0000,y
inx
inx ; X -> campo data
clc ; allocazione OK
rts
?08:
_hErr:
sec
ldy hdrptr
ldx #0 ; return NULL
rts
; ritorna dimensione di un blocco allocato
; In - X = ptr blocco allocato
; Y = puntatore all'header dello heap
; DP = 0 (pagina zero)
; DBR = banco contenente lo heap
; CPU in modo 16 bit
; Out - CF = 0 - A = size blocco - X = ptr header blocco
; CF = 1 se ptr X non valido (heap corrotto ?)
_SizeHeapBlk:
cpx #FIRSTPTR
bcc _hErr ; ptr non valido !
dex
dex ; X -> header blocco
lda !$0000,y ; size heap
sec
sbc #HEAPHDRSIZE ; toglie lunghezza header heap
sta bsize ; bsize = dimensione corrente
sty hdrptr
iny ; add 4
iny
iny
iny ; Y = first ptr blocco
?04: sty curptr
lda !$0000,y ; size blocco
cpx curptr ; confronta X con curptr
bcc _hErr ; X < curptr -> errore !
beq ?08 ; X = curptr -> trovato ?
; qui CF = 1 !
and #$FFFE ; maschera off bit 0
sta nsize ; dimensione blocco corrente
lda bsize
sbc nsize ; update dimensione heap
bcc _hErr ; overflow -> errore !
beq _hErr ; ptr X non trovato !
sta bsize
tya
clc
adc nsize
tay ; Y = next block
bra ?04 ; loop
?08: bit #$0001 ; test blocco free
bne _hErr ; errore - blocco free
clc
ldy hdrptr
_?01: rts
; rilascia un blocco allocato
; In - X = ptr blocco allocato
; Y = puntatore all'header dello heap
; DP = 0 (pagina zero)
; DBR = banco contenente lo heap
; CPU in modo 16 bit
; Out - CF = 0 - A = size blocco - X = ptr header blocco
; CF = 1 se ptr X non valido (heap corrotto ?)
_RlsHeapBlk:
jsr _SizeHeapBlk
bcs _?01 ; errore - esce
; punto di ingresso nel caso il test del puntatore sia stato fatto
; In - X = ptr blocco fisico (header blocco)
; Y = puntatore all'header dello heap
; A = size blocco
; CF = 0
_RlsHeapBlk2:
sta bsize ; size blocco
adc !$0000,y ; update size heap
sta !$0000,y
stx curptr ; salva curptr
tyx ; X = PRED
ldy !$0000,x ; Y = SCAN
beq ?06 ; SCAN = NULL
?02: cpy curptr ; test
beq ?03 ; SCAN = CURPTR
bcs ?06 ; SCAN > CURPTR
?03: tyx ; X = PRED = SCAN
ldy !$0002,x ; Y = PRED.bnext
bne ?02 ; loop SCAN
?06: ; X = PRED // Y = SUCC // X < curptr < Y
; inserisce il blocco nella lista free
tya ; A = SUCC
stx pred ; salva PRED
ldx curptr
sta !$0002,x ; curptr.bnext = SUCC
txa ; A = CURPTR
ldx pred ; X = PRED
sta !$0002,x ; pred.bnext = curptr
tya ; A = SUCC
beq ?08 ; last block
; verifica ora se esiste un blocco free adiacente a destra
sec
sbc curptr ; A = SUCC - curptr
cmp bsize ; se adiacente a destra -> A = 0
bne ?08 ; no free block adiacente a destra
; fonde blocco curptr con blocco SUCC
lda !$0000,y ; SUCC.bsize
dec a ; SUCC blocco free !
clc
adc bsize ; update size blocco fuso
sta bsize
ldy curptr ; Y = CURPTR
lda !$0002,x ; A = pred.bnext
sta !$0002,y ; curptr.bnext = A = pred.bnext
?08: ; X = PRED // X < curptr
; verifica se esiste un blocco free adiacente a sinistra
cpx hdrptr ; PRED = primo blocco heap ?
beq ?10 ; si - non esiste blocco adiacente a destra
ldy !$0000,x ; Y = PRED.bsize
dey ; blocco free !
stx pred
tya ; A = PRED.bsize
clc
adc pred ; A = PRED.bsize + PRED
cmp curptr ; se curptr = A blocco adiacente
bne ?10 ; non esiste blocco free adiacente a destra
; fonde blocco curptr con blocco PRED
tya ; A = PRED.bsize
clc
adc bsize
sta bsize ; nuova dimensione blocco free fuso
ldx curptr ; X = curptr
lda !$0002,x ; A = curptr.bnext
ldx pred ; X = PRED
sta !$0002,x ; PRED.bnext = curptr.bnext
stx curptr ; curptr = PRED (blocco fuso)
?10: ; marca il blocco curptr come free
lda bsize
inc a
ldx curptr
sta !$0000,x ; curptr.bsize = bsize + 1
clc ; OK
rts
_________________
http://65xx.unet.bz/ - Hardware & Software 65XX family