Working on implementing heap API.

This commit is contained in:
Martin Haye 2015-08-19 08:45:02 -07:00
parent a8ec4fd42c
commit a8a4fdefed
2 changed files with 111 additions and 66 deletions

View File

@ -80,7 +80,7 @@ relocate:
lda $D000 lda $D000
cmp #1 cmp #1
beq .gotaux beq .gotaux
.noaux jsr inlineFatal : !text "Aux mem required",0 .noaux jsr inlineFatal : !text "AuxMemReq",0
.gotaux ldy #0 .gotaux ldy #0
ldx #$D0 ldx #$D0
.pglup stx .ld+2 .pglup stx .ld+2
@ -656,35 +656,27 @@ nHeapBlks !byte 0
;------------------------------------------------------------------------------ ;------------------------------------------------------------------------------
; Heap management routines ; Heap management routines
; Set the table for the next type in order. Starts with type 0, then 1, etc. ;------------------------------------------------------------------------------
; By convention, type 0 is used for the Global object, from which all others ; Establish a new heap
; valid objects are reachable (and invalid garbage if not reachable from there). heapSet: !zone
; txa ; addr must be page-aligned
; x=ptr lo, y = ptr hi. beq +
; Tbl: type size 01-7F, then (byte) offsets of ptrs within type, then 0. .inval jmp invalParam
setTypeTbl: !zone + lda isAuxCmd
tya ; save addr hi bne .inval ; must be in main mem
ldy nTypes sec ; check for valid
cpy #MAX_TYPES jsr shared_scan
bmi + lda tSegAdrLo,y ; end must also be page-aligned
+prChr 'T' bne .inval
brk ; Good to go. Record the start and end pages
+ sta typeTblL,y ; addr hi lda pTmp+1
sta .ld+2 sta heapStartPg
txa ; addr lo sta heapTop+1
sta typeTblH,y lda tSegAdrHi,y
.ld lda $1000,x ; self-modified above: fetch length byte sta heapEndPg
sta typeLen,y ; save that too
inc nTypes ; bump type count
rts
; Clear the heap
heapReset: !zone
lda #0 lda #0
sta heapTop sta heapTop
sta nHeapBlks sta nHeapBlks
lda heapStartPg
sta heapTop
; fall through to: ; fall through to:
; Zero memory heapTop.heapEnd ; Zero memory heapTop.heapEnd
heapClr: !zone heapClr: !zone
@ -700,8 +692,32 @@ heapClr: !zone
bne .pg bne .pg
rts rts
; Allocate a block on the heap. X = $00.7F for string block, $80.FF for type $00.7F. ;------------------------------------------------------------------------------
; and yes, type $00 is valid (conventionally used for the Global Object). ; Set the table for the next type in order. Starts with type $80, then $81, etc.
; By convention, type $80 is used for the Global object, from which all other
; live objects are reachable (and invalid garbage if not reachable from there).
;
; x=ptr lo, y = ptr hi.
; Tbl: type size 01-7F, then (byte) offsets of ptrs 01-7F within type, then 0.
heapAddType: !zone
tya ; save addr hi
ldy nTypes
cpy #MAX_TYPES
bmi +
+prChr 'T'
brk
+ sta typeTblL,y ; addr hi
sta .ld+2
txa ; addr lo
sta typeTblH,y
.ld lda $1000,x ; self-modified above: fetch length byte
sta typeLen,y ; save that too
inc nTypes ; bump type count
rts
; Allocate a block on the heap. X = $00.7F for string block, $80.FF for a typed obj.
; And yes, type $80 is valid (conventionally used for the Global Object).
; And yes, string length $00 is valid (it's an empty string).
heapAlloc: !zone heapAlloc: !zone
lda heapTop lda heapTop
sta pTmp sta pTmp
@ -709,7 +725,7 @@ heapAlloc: !zone
sta pTmp+1 sta pTmp+1
txa txa
ldy #0 ldy #0
sta (pTmp),y ; save obj type on heap sta (pTmp),y ; save obj type or len on heap
bpl .gotlen bpl .gotlen
and #$7F and #$7F
tay tay
@ -724,7 +740,7 @@ heapAlloc: !zone
+ sta heapTop + sta heapTop
sty heapTop+1 sty heapTop+1
rts rts
.needgc jsr inlineFatal : !text "GcNotImpl",0 .needgc jsr inlineFatal : !text "NeedCollect",0
; Input : Carry set = add if not found, clear = check only, don't add ; Input : Carry set = add if not found, clear = check only, don't add
; Output: Y-reg = index ; Output: Y-reg = index
@ -763,7 +779,7 @@ gcHash_chk: !zone
; Verify integrity of memory manager structures ; Verify integrity of memory manager structures
memCheck: !zone memCheck: !zone
jsr heapCheck ; check heap if there is one jsr heapCheck ; heap check (if there is one)
ldx #0 ; check main bank ldx #0 ; check main bank
jsr .chk jsr .chk
ldx #1 ; then aux ldx #1 ; then aux
@ -781,46 +797,52 @@ memCheck: !zone
.done rts .done rts
; Verify the integrity of the heap ; Verify the integrity of the heap
heapCheck: !zone heapCheck:
lda #0
sta pTmp ; we'll use pTmp for scanning
lda heapStartPg lda heapStartPg
bne + ; skip check if no heap defined bne + ; skip check if no heap defined
rts .done rts
+ lda heapTop + sta pTmp+1
sta pTmp .blklup lda pTmp
lda heapTop+1 cmp heapTop ; have we reached current top-of-heap?
sta pTmp+1 lda pTmp+1
sbc heapTop+1
bcs .done ; if so we're done
ldy #0 ldy #0
.blklup lda (pTmp),y lda (pTmp),y
bmi .isobj bmi .isobj
; it's a string; check its characters ; it's a string; check its characters
pha ; save length sta tmp ; save length
beq .nxtblk ; handle zero-length string beq .nxtblk ; handle zero-length string
tax tax
.stlup lda (pTmp),y .stlup iny
beq heapCorrupt lda (pTmp),y
bmi heapCorrupt beq heapCorrupt ; strings shouldn't have zero bytes embedded
iny bmi heapCorrupt ; strings should be lo-bit ASCII
dex dex
bne .stlup bne .stlup
.nxtblk pla ; get length back ; advance to next heap block
sec .nxtblk lda tmp ; get length back
adc pTmp sec ; add 1 for type byte
adc pTmp ; advance scan ptr
sta pTmp sta pTmp
bcc .blklup bcc .blklup ; go again
inc pTmp+1 inc pTmp+1
lda pTmp+1 lda pTmp+1
cmp heapEndPg cmp heapEndPg
bcc .blklup
bcs heapCorrupt bcs heapCorrupt
bcc .blklup ; always taken
; it's an object; check its pointers
.isobj and #$7F .isobj and #$7F
tax tax
cpx #MAX_TYPES cpx #MAX_TYPES
bcs heapCorrupt bcs heapCorrupt
lda typeLen,x lda typeLen,x
pha ; save length for later sta tmp ; save length for .nxtblk to use later (and also for checking below)
lda typeTblL,x lda typeTblL,x ; get type table address for this type
sta .getoff+1 sta .getoff+1 ; set up for pointer offset fetching
lda typeTblH,x lda typeTblH,x ; hi byte too
sta .getoff+2 sta .getoff+2
ldx #0 ; type entry starts at len byte, which we immediately skip ldx #0 ; type entry starts at len byte, which we immediately skip
.tscan inx .tscan inx
@ -1024,8 +1046,9 @@ releaseSegment: !zone
;------------------------------------------------------------------------------ ;------------------------------------------------------------------------------
scanForAddr: !zone scanForAddr: !zone
; Input: X(lo)/Y(hi) - address to scan for ; Input: X(lo)/Y(hi) - address to scan for (gets stored in pTmp)
; Output: X-reg - segment found (zero if not found), N and Z set for X-reg ; Output: X-reg - segment found (zero if not found), N and Z set for X-reg
; Y-reg - next segment in chain
; carry clear if addr == seg start, set if addr != seg start ; carry clear if addr == seg start, set if addr != seg start
stx pTmp ; save target addr stx pTmp ; save target addr
sty pTmp+1 sty pTmp+1
@ -1046,11 +1069,11 @@ scanForAddr: !zone
bne .loop ; non-zero = not end of chain - loop again bne .loop ; non-zero = not end of chain - loop again
rts ; fail with X=0 rts ; fail with X=0
.found: sec ; start out assuming addr != seg start .found: sec ; start out assuming addr != seg start
lda pTmp ; compare scan address... lda pTmp ; compare scan address lo...
eor tSegAdrLo,x ; ... to seg start lo eor tSegAdrLo,x ; ... to seg start lo
bne + ; if not equal, leave carry set bne + ; if not equal, leave carry set
lda pTmp+1 ; hi byte lda pTmp+1 ; hi byte
eor tSegAdrHi,x ; to hy byte eor tSegAdrHi,x ; to hi byte
bne + ; again, if not equal, leave carry set bne + ; again, if not equal, leave carry set
clc ; addr is equal, clear carry clc ; addr is equal, clear carry
+ txa + txa
@ -1168,6 +1191,18 @@ aux_dispatch:
+ cmp #CHECK_MEM + cmp #CHECK_MEM
bne + bne +
jmp memCheck jmp memCheck
+ cmp #HEAP_SET
bne +
jmp heapSet
+ cmp #HEAP_ADD_TYPE
bne +
jmp heapAddType
+ cmp #HEAP_ALLOC
bne +
jmp heapAlloc
+ cmp #HEAP_COLLECT
bne +
jmp heapCollect
+ cmp #FATAL_ERROR + cmp #FATAL_ERROR
bne + bne +
jmp fatalError jmp fatalError
@ -1324,7 +1359,7 @@ shared_alloc:
jsr reclaim ; first time, do a reclaim pass jsr reclaim ; first time, do a reclaim pass
jmp .try ; and try again jmp .try ; and try again
.notFound: .notFound:
jmp invalAddr jmp invalParam
; target addr was specified. See if we can fulfill the request. ; target addr was specified. See if we can fulfill the request.
.gotTarget: .gotTarget:
ldx targetAddr ; all 16 bits ldx targetAddr ; all 16 bits
@ -1464,16 +1499,16 @@ coalesce: !zone
shared_scan: !zone shared_scan: !zone
php ; save carry (set to check active flg, clr to skip check) php ; save carry (set to check active flg, clr to skip check)
jsr scanForAddr ; scan for block that matches jsr scanForAddr ; scan for block that matches
beq invalAddr ; if not found, invalid beq invalParam ; if not found, invalid
bcs invalAddr ; if addr not exactly equal, invalid bcs invalParam ; if addr not exactly equal, invalid
plp plp
lda tSegType,x ; get existing flags lda tSegType,x ; get existing flags
bcc + ; optionally, skip check of active flag bcc + ; optionally, skip check of active flag
bpl invalAddr ; must be an active block bpl invalParam ; must be an active block
+ rts + rts
invalAddr: !zone invalParam: !zone
jsr inlineFatal : !text "InvalAdr", 0 jsr inlineFatal : !text "InvalParam", 0
;------------------------------------------------------------------------------ ;------------------------------------------------------------------------------
; If the resource is a module, this will locate the corresponding bytecode ; If the resource is a module, this will locate the corresponding bytecode
@ -1936,7 +1971,7 @@ disk_finishLoad: !zone
.prodosErr: .prodosErr:
jmp prodosError jmp prodosError
.addrErr: .addrErr:
jmp invalAddr jmp invalParam
.ysave: !byte 0 .ysave: !byte 0
.nFixups: !byte 0 .nFixups: !byte 0

View File

@ -272,9 +272,12 @@ HEAP_SET = $20
; Establishes a block of memory to use as a garbage collected small-object ; Establishes a block of memory to use as a garbage collected small-object
; heap. The block must be page-aligned and sized in whole pages, and ; heap. The block must be page-aligned and sized in whole pages, and
; generally should be locked first. ; generally should be locked first.
;
; Also clears the table of heap types, so HEAP_ADD_TYPE will start again
; setting the global type ($80).
;------------------------------------------------------------------------------ ;------------------------------------------------------------------------------
HEAP_TYPE = $21 HEAP_ADD_TYPE = $21
; Input: X-reg(lo) / Y-reg(hi): pointer to type table ; Input: X-reg(lo) / Y-reg(hi): pointer to type table
; ;
; Output: None ; Output: None
@ -293,7 +296,7 @@ HEAP_TYPE = $21
;------------------------------------------------------------------------------ ;------------------------------------------------------------------------------
HEAP_ALLOC = $22 HEAP_ALLOC = $22
; Input: X-reg: string length $01-7F, or type code $80-FF ; Input: X-reg: string length $00-7F, or type code $80-FF
; ;
; Output: X-reg(lo) / Y-reg(hi): pointer to allocated object space ; Output: X-reg(lo) / Y-reg(hi): pointer to allocated object space
; ;
@ -307,6 +310,13 @@ HEAP_ALLOC = $22
; ;
; By convention, the very first block allocated should be of the "Global" ; By convention, the very first block allocated should be of the "Global"
; type ($80) and all other live objects must be traceable from there. ; type ($80) and all other live objects must be traceable from there.
;
; If there's no room on the heap, an fatal error will be thrown. The system
; assumes that HEAP_COLLECT is not safe to run at any time. Rather, you
; should call it periodically when you're certain no pointers to heap
; objects (except the global object) are on the system stack.
;
; Note: strings of length zero are considered valid and supported.
;------------------------------------------------------------------------------ ;------------------------------------------------------------------------------
HEAP_COLLECT = $23 HEAP_COLLECT = $23