Added string interning to heap. Working on type system for players, items, combat.

This commit is contained in:
Martin Haye 2015-09-17 10:03:02 -07:00
parent ce0709fbab
commit 698ce413cc
4 changed files with 143 additions and 113 deletions

3
.gitignore vendored
View File

@ -1,6 +1,9 @@
# Ignore vi swap files, MacOS extra files, etc.
*.swp
.DS_Store
*.old
*.bak
*.orig
# Ignore utility shortcuts that build and run
g

View File

@ -907,28 +907,64 @@ heapAddType: !zone
; And yes, string length $00 is valid (it's an empty string).
heapAlloc: !zone
lda heapTop
sta pTmp
sta pSrc
lda heapTop+1
sta pTmp+1
sta pSrc+1
ldy #0
txa
sta (pTmp),y ; save obj type or string len on heap
sta (pSrc),y ; save obj type or string len on heap
bpl .gotlen
lda typeLen-$80,x
.gotlen ldy pTmp+1
.gotlen ldy pSrc+1
sec ; add 1 to include type byte or len byte for strings
adc pTmp
adc pSrc
bcc +
iny
cpy heapEndPg
bcs .needgc
+ sta heapTop
sty heapTop+1
ldx pTmp ; return ptr in X=lo/Y=hi
ldy pTmp+1
retPSrc:
ldx pSrc ; return ptr in X=lo/Y=hi
ldy pSrc+1
rts
.needgc jsr inlineFatal : !text "NeedCollect",0
; Re-use existing string or allocate new and copy.
heapIntern: !zone
stx pTmp
sty pTmp+1
jsr startHeapScan
bcs .notfnd ; handle case of empty heap
.blklup bvs .nxtblk
; it's a string
inx ; +1 to compare length byte also
.stlup lda (pTmp),y ; compare string bytes until non-matching (or all bytes done)
cmp (pSrc),y
bne .nxtblk
iny
dex
bne .stlup
; found a match!
.found beq retPSrc
; advance to next heap block
.nxtblk jsr nextHeapBlk
bcc .blklup ; go process next block
.notfnd lda (pTmp),y ; get string length
pha ; save it
tax
jsr heapAlloc ; make space for it
pla ; string length back
tax
inx ; add 1 to copy length byte also
ldy #0
.cplup lda (pSrc),y ; copy the string's characters
sta (pTmp),y
iny
dex
bne .cplup
beq .found ; always taken
; Check if blk pSrc is in GC hash; optionally add it if not.
; Input : pSrc = blk to check/add
; Carry set = add if not found, clear = check only, don't add
@ -994,49 +1030,23 @@ memCheck: !zone
; Verify the integrity of the heap
heapCheck: !zone
lda #0
sta pTmp ; we'll use pTmp for scanning
lda heapStartPg
bne + ; skip check if no heap defined
.done rts
+ sta pTmp+1
.blklup lda pTmp
cmp heapTop ; have we reached current top-of-heap?
lda pTmp+1
sbc heapTop+1
bcs .done ; if so we're done
ldy #0
lda (pTmp),y
bmi .isobj
jsr startHeapScan
.blklup bcc + ; if top-of-heap, we're done
rts
+ bvs .isobj ; handle objects separately
; it's a string; check its characters
sta tmp ; save length
beq .nxtblk ; handle zero-length string
tax
.stlup iny
lda (pTmp),y
lda (pSrc),y
beq heapCorrupt ; strings shouldn't have zero bytes embedded
bmi heapCorrupt ; strings should be lo-bit ASCII
dex
bne .stlup
; advance to next heap block
.nxtblk lda tmp ; get length back
sec ; add 1 to account for type byte or string len byte
adc pTmp ; advance scan ptr
sta pTmp
bcc .blklup ; go again
inc pTmp+1
lda pTmp+1
cmp heapEndPg
bcs heapCorrupt
bcc .blklup ; always taken
.nxtblk jsr nextHeapBlk
jmp .blklup ; go again
; it's an object; check its pointers
.isobj and #$7F
tax
cpx #MAX_TYPES
bcs heapCorrupt
lda typeLen,x
sta tmp ; save length for .nxtblk to use later (and also for checking below)
lda typeTblL,x ; get type table address for this type
.isobj lda typeTblL,x ; get type table address for this type
sta .getoff+1 ; set up for pointer offset fetching
lda typeTblH,x ; hi byte too
sta .getoff+2
@ -1046,22 +1056,72 @@ heapCheck: !zone
beq .nxtblk ; zero marks end of offset table
tay
iny ; not much we can do to validate lo byte, so skip it
cpy tmp ; ensure offset is within type length
cpy reqLen ; ensure offset is within type length
beq + ; very end is ok
bcs heapCorrupt ; beyond end is not ok
+ lda (pTmp),y ; get hi byte of ptr
+ lda (pSrc),y ; get hi byte of ptr
beq .tscan ; null is ok
cmp heapStartPg ; else check if < start of heap
bcc heapCorrupt
cmp heapEndPg ; or >= than end of heap
bcc .tscan
}
; fall through to heapCorrupt...
} ; if DEBUG
heapCorrupt:
ldx pTmp
lda pTmp+1
jsr prntax
jsr inlineFatal : !text "HeapCorrupt",0
ldx pTmp
lda pTmp+1
jsr prntax
jsr inlineFatal : !text "HeapCorrupt",0
; Begin a heap scan by setting pTmp to start-of-heap, then returns
; everything as per nextHeapBlk below
startHeapScan: !zone
lda #0
ldx heapStartPg
sta pDst ; used in gc2 sweep init
stx pDst+1
jmp .pgadv
; Add tmp to pSrc, and sanity check against top of heap.
; Get current block on heap during heap scan
; Output: reqLen (and A): size of heap block
; N and Z: set according to size of heap block
; Y: 0
; C: set if end of heap reached
; V: clear if string, set if object
; X: index into type table (if block is an object),
; or string length (if block is a string)
nextHeapBlk:
lda pSrc
ldx pSrc+1
sec ; add 1 to account for type byte or string len byte
adc tmp
bcc +
inx
.pgadv stx pSrc+1
+ sta pSrc
cpx heapEndPg
bcs heapCorrupt
getHeapBlk:
ldy #0 ; so we always return a nice useful Y index
cmp heapTop ; have we reached current top-of-heap?
txa
sbc heapTop+1
bcs .done ; if so we're done
lda (pSrc),y
bmi .isobj
clv ; it's a stirng
tax
.gotlen sta reqLen
.done rts
.isobj and #$7F
tax
cpx nTypes
bcs heapCorrupt
bit monrts ; set V flag
lda typeLen,x
bvs .gotlen ; always taken
; Phase 1 of Garbage Collection: mark accessible heap blocks starting from the root block
gc1_mark: !zone
@ -1139,16 +1199,11 @@ gc3_fix:
; Phase 2 of Garbage Collection: sweep all accessible blocks together
gc2_sweep: !zone
lda #0
sta pSrc
sta pDst
lda heapStartPg
sta pSrc+1
sta pDst+1
jsr startHeapScan ; init pSrc and pDst, set reqLen to length of first block
bcs .done ; stop if heap is empty
.outer clc ; clc = do not add to hash
jsr gcHash_chk ; is this block in hash?
; note: next 20 lines or so *must* preserve the carry flag
bcc + ; if not in hash, don't set any dest addr
bcc .advSrc ; if not in hash, skip this block
lda pDst
sta gcHash_dstLo,y ; record new address
eor pSrc
@ -1156,19 +1211,9 @@ gc2_sweep: !zone
lda pDst+1
sta gcHash_dstHi,y ; in hash table
eor pSrc+1
ora tmp
sta tmp ; this will be zero iff all 16 bits of pSrc == pDst
+ ldy #0 ; index of type byte in heap block
lda (pSrc),y ; ...still need to preserve carry flag...
bpl .gotlen
and #$7F
tax
lda typeLen,x
.gotlen sta reqLen ; save len for later
bcc .advSrc ; finally act on carry (hash check flg): if block not in hash, skip block
tax ; and in index for byte-copy count
lda tmp ; check for pSrc == pDst
ora tmp ; this will be zero iff all 16 bits of pSrc == pDst
beq .advDst ; if equal, no need to copy
ldx reqLen ; index for byte-copy count
inx ; set up to copy type/len byte as well
.cplup lda (pSrc),y
sta (pDst),y
@ -1181,17 +1226,8 @@ gc2_sweep: !zone
sta pDst
bcc .advSrc
inc pDst+1
.advSrc lda pSrc ; advance source
sec ; add 1 for type/len byte
adc reqLen
sta pSrc
bcc +
inc pSrc+1
+ cmp heapTop ; end of heap yet? (lo byte check)
lda pSrc+1
sbc heapTop+1 ; (hi byte check)
bcs .done ; if not, loop again
jmp .outer
.advSrc jsr nextHeapBlk ; advance to next src block
bcc .outer ; and process it
.done lda pDst ; done sweeping, so set new heap top.
sta heapTop
lda pDst+1
@ -1417,6 +1453,9 @@ aux_dispatch:
+ cmp #HEAP_ALLOC
bne +
jmp heapAlloc
+ cmp #HEAP_INTERN
bne +
jmp heapIntern
+ cmp #HEAP_COLLECT
bne +
jmp heapCollect

View File

@ -346,7 +346,19 @@ HEAP_ALLOC = $22
; Note: strings of length zero are considered valid and supported.
;------------------------------------------------------------------------------
HEAP_COLLECT = $23
HEAP_INTERN = $23
; Input: X-reg(lo) / Y-reg(hi): PLASMA-style string in regular RAM
;
; Output: X-reg(lo) / Y-reg(hi): pointer to allocated object space
;
; Checks for existing, or allocates space for and copies, a PLASMA-style
; string starting with a length byte.
;
; If an identical string is already on the heap, returns a pointer to that.
; Else, allocates heap space and copy the string into it.
;------------------------------------------------------------------------------
HEAP_COLLECT = $24
; Input: None.
;
; Output: X-reg(lo) / Y-reg(hi): free space in heap after collection

View File

@ -31,11 +31,6 @@ const RES_NUM_EXPAND_VEC = 2
const RES_NUM_FONT_ENGINE = 3
const RES_NUM_TILE_ENGINE = 4
///////////////////////////////////////////////////////////////////////////////////////////////////
// Hardware addresses.
const keyboard = $C000
const keystrobe = $C010
///////////////////////////////////////////////////////////////////////////////////////////////////
// Memory manager definitions
@ -70,7 +65,8 @@ const FATAL_ERROR = $1F
const HEAP_SET = $20
const HEAP_ADD_TYPE = $21
const HEAP_ALLOC = $22
const HEAP_COLLECT = $23
const HEAP_INTERN = $23
const HEAP_COLLECT = $24
///////////////////////////////////////////////////////////////////////////////////////////////////
// Other constants
@ -92,32 +88,12 @@ struc Global
word players
end
const TYPE_PLAYER = $81
struc Player
byte type
word id
word nextObj
word name
byte muscle
byte quickness
word items
word health
end
const TYPE_ITEM = $82
struc Item
byte type
word id
word nextObj
word name
byte kind
word cost
end
include "playtype_consts.plh"
// Table per type, starts with length, then pointer offsets, ending with zero.
byte typeTbl_Global[] = Global, players, 0
byte typeTbl_Player[] = Player, nextObj, name, items, 0
byte typeTbl_Item[] = Item, nextObj, name, 0
include "playtype_tbls.plh"
word global // the global heap object, from which all live objects must be reachable
@ -1504,7 +1480,7 @@ end
///////////////////////////////////////////////////////////////////////////////////////////////////
// Call like this: addToList(player + items, itemToAdd)
def addToList(addTo, p)
p=>nextObj = *addTo
p=>p_nextObj = *addTo
*addTo = p
end
@ -1515,13 +1491,13 @@ def removeFromList(pList, toRemove)
p = *pList
while p and p <> toRemove
pList = p + nextObj
pList = p + p_nextObj
p = *pList
loop
if p
*pList = p=>nextObj
p=>nextObj = NULL
*pList = p=>p_nextObj
p=>p_nextObj = NULL
else
fatal("InvalUnlink")
fin