1
0
mirror of https://github.com/dschmenk/PLASMA.git synced 2026-03-12 01:41:40 +00:00
Files
PLASMA/src/libsrc/memmgr.pla
2018-04-17 16:15:50 -07:00

739 lines
19 KiB
Plaintext
Executable File

//
// Handle based swapping memory manager
//
include "inc/cmdsys.plh"
include "inc/fileio.plh"
struc t_initdata
word volptr
word freeblks
word ramvol
word bestvol
word ramfree
word bestfree
word filecnt
word catentry
byte catref
byte firstblk
byte entrylen
byte entriesblk
byte swapstrlen
byte[4] volparms
byte[] volinfo
byte[] catalog
end
word initdata
//
// Access bytes within a word
//
struc t_word
byte lsb
byte msb
end
//
// Alloced memory block structure
//
struc t_memblk
word blksiz
byte blkref
byte blklok
end
//
// Free memory block structure
//
struc t_freblk
word fresiz
word frenxt
end
//
// Block size
//
const MAX_BLK_SIZE = $2010
const MAX_BLK_MASK = $1FFF
const MIN_BLK_SIZE = $04
const MIN_BLK_MASK = $03
//
// Block states
//
const HMEM_ADDR = $FFFC
const HMEM_SIZE = $FFFC
const HMEM_STATE = $03
const HMEM_MOVEABLE = $00 // Many dependencies on this being $00
const HMEM_AVAIL = $01
const HMEM_LOCKED = $02
const HMEM_SWAPPED = $03
//
// Block flags
//
const HMEM_ACCESSED = $80
//
// Handle modifier
//
const HMOD = $4321
//
// Handle tables/lists
//
const PG_SIZE = 512
const PG_ENTRIES = 256
const PG_TBL_SIZE = 8
word sysbuf
word[PG_TBL_SIZE] hpgtbl // Handle page table
word pooladdr
word poolsize
word frelst // Free list
byte sweeppg, sweepen
byte[64] swapvol = "/" // Swap volume
byte swapdir = "/SWAP/"
byte hexchar = '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
//
// DEBUG
//
//byte swapinstr = "Swap in:"
//byte swapoutstr = "Swap out:"
//byte getblkstr = "Get block = "
//byte allocpgstr = "Alloc page:"
//
//def putln
// return putc($0D)
//end
//def putb(hexb)
// return call($FDDA, hexb, 0, 0, 0)
//end
//def puth(hex)
// return call($F941, hex >> 8, hex, 0, 0)
//end
//
// Fill block filename
//
def strcharadd(str, chr)#0
^str = ^str + 1
str->.[^str] = chr
end
def swapfile(filestr, hmem)#0
memcpy(filestr, @swapvol, swapvol + 1)
strcharadd(filestr, 'H')
strcharadd(filestr, hexchar[(hmem >> 12) & $0F])
strcharadd(filestr, hexchar[(hmem >> 8) & $0F])
strcharadd(filestr, hexchar[(hmem >> 4) & $0F])
strcharadd(filestr, hexchar[ hmem & $0F])
end
//
// Find exact/best free memory match
//
def unlink(freblk, freprv)
//
// Unlink free block
//
if freprv
freprv=>frenxt = freblk=>frenxt
else
frelst = freblk=>frenxt
fin
return freblk
end
def unfre(freblk, freprv, size)
word shrink
if freblk=>fresiz == size
//
// Unlink free block
//
unlink(freblk, freprv)
elsif freblk=>fresiz > size
//
// Shrink free block
//
shrink = freblk + size
if freprv
freprv=>frenxt = shrink
else
frelst = shrink
fin
shrink=>fresiz = freblk=>fresiz - size
shrink=>frenxt = freblk=>frenxt
else
freblk = 0
fin
return freblk
end
def addfre(freblk)#0
word srch
//freblk=>fresiz = freblk=>blksiz
if frelst and frelst < freblk
srch = frelst
while srch=>frenxt
if srch=>frenxt > freblk
//
// Insert into list
//
freblk=>frenxt = srch=>frenxt
srch=>frenxt = freblk
return
fin
srch = srch=>frenxt
loop
//
// Add to end of list
//
freblk=>frenxt = 0
srch=>frenxt = freblk
else
//
// Add to beginning of list
//
freblk=>frenxt = frelst
frelst = freblk
fin
end
//
// Memory coallesce/compaction/swap out routines
//
def coallesce
word srch, combined
combined = 0
srch = frelst
while srch and srch=>frenxt
if srch + srch=>fresiz == srch=>frenxt
//
// Combine adjacent free space
//
//putc('C');putc(' ');puth(srch);putc('+');puth(srch=>frenxt);putc(' ')
srch=>fresiz = srch=>fresiz + srch=>frenxt=>fresiz
srch=>frenxt = srch=>frenxt=>frenxt
combined = 1
else
srch = srch=>frenxt
fin
loop
return combined
end
def compact
word page, entry, memblk, moveblk, size, srch, prev
byte moved
moved = 0
for page = PG_TBL_SIZE - 1 downto 0
if hpgtbl[page]
for entry = 255 downto 0
if hpgtbl:[page, entry].lsb & HMEM_STATE == HMEM_MOVEABLE
memblk = hpgtbl:[page, entry]
size = memblk=>blksiz
moveblk = 0
prev = 0
srch = frelst
while srch and srch < memblk
if srch=>fresiz >= size
moveblk = unfre(srch, prev, size)
break
fin
prev = srch
srch = srch=>frenxt
loop
if moveblk
//putc('M');putc(' ');puth(moveblk);putc('=');puth(memblk);putc(' ')
memcpy(moveblk, memblk, size)
hpgtbl:[page, entry] = moveblk
addfre(memblk)
moved = 1
fin
fin
next
else
break
fin
next
if moved
coallesce
fin
return moved
end
//
// Swap out memory block
//
def swapout(accessed)
byte[64] filename
byte ref
word page, entry, memblk, hmem, size
byte swapped
swapped = 0
for page = PG_TBL_SIZE - 1 downto 0
if hpgtbl[page]
for entry = 255 downto 0
if hpgtbl:[page, entry].lsb & HMEM_STATE == HMEM_MOVEABLE
memblk = hpgtbl:[page, entry]
if not (memblk->blklok & accessed)
//
// Swap this block out
//
size = memblk=>blksiz
hmem.lsb = page
hmem.msb = entry
swapfile(@filename, hmem)
fileio:create(@filename, $00, size) // embed size in aux type
//puts(@swapoutstr);puts(@filename);putc('@');puth(memblk);putc(':');puth(size);putln
ref = fileio:open(@filename)
if ref
//
// Write it out
//
if fileio:write(ref, memblk, size) == size
//
// Zero size in page table flags swapin to read from disk
//
hpgtbl:[page, entry] = HMEM_SWAPPED
addfre(memblk)
swapped = 1
fin
fileio:close(ref)
fin
fin
fin
next
else
break
fin
next
if swapped
coallesce
compact
fin
return swapped
end
//
// Find a memory block
//
def findexact(size)
word srch, prev
srch = frelst
prev = 0
while srch
if srch=>fresiz == size
//putc('E')
return unlink(srch, prev)
fin
prev = srch
srch = srch=>frenxt
loop
return 0
end
def findbest(size)
word srch, prev
prev = 0
srch = frelst
while srch
if srch=>fresiz >= size
//putc('B')
return unfre(srch, prev, size)
fin
prev = srch
srch = srch=>frenxt
loop
return 0
end
def findblk(size)
word addr
if size > MAX_BLK_SIZE; return 0; fin
addr = findexact(size)
if !addr
coallesce
addr = findexact(size)
if !addr
compact
addr = findbest(size)
if !addr
swapout(HMEM_ACCESSED)
addr = findbest(size)
if !addr
swapout(0)
addr = findbest(size)
fin
fin
fin
fin
//
// Fill in the block
//
//puts(@getblkstr);puth(addr);putc(':');puth(size);putln
if addr
addr=>blksiz = size
addr->blkref = 1
addr->blklok = 0
fin
return addr
end
//
// Swap in a memory block (or allocate an uninitialized block)
//
def swapin(hmem)
byte[64] filename
byte[15] info
byte ref
word memblk, size
size = hpgtbl:[hmem.lsb, hmem.msb] & HMEM_SIZE
if size
//
// This was just uninitialized memory, don't bother reading from file
//
memblk = findblk(size)
else
//
// Swap this block back in
//
swapfile(@filename, hmem)
fileio:getfileinfo(@filename, @info)
size = info:2 // Size encoded in aux type
memblk = findblk(size)
//puts(@swapinstr);puts(@filename);putc('@');puth(memblk);putc(':');puth(size);putln
if memblk
//
// Read it in
//
ref = fileio:open(@filename)
if ref
fileio:read(ref, memblk, size)
fileio:close(ref)
fileio:destroy(@filename)
fin
fin
fin
if memblk
hpgtbl:[hmem.lsb, hmem.msb] = memblk
fin
return memblk
end
//
// Incrementally clear the ACCESSED BIT
//
export def sweep#0
word memblk
if hpgtbl[sweeppg]
memblk = hpgtbl:[sweeppg, sweepen]
when memblk.lsb & HMEM_STATE
is HMEM_MOVEABLE
is HMEM_LOCKED
memblk = memblk & HMEM_ADDR
if memblk->blklok & HMEM_ACCESSED
memblk->blklok = memblk->blklok & $7F
fin
wend
if sweepen == 255
sweepen = 0
if sweeppg == 0
sweeppg = PG_TBL_SIZE - 1
else
sweeppg = sweeppg - 1
fin
else
sweepen = sweepen + 1
fin
else
sweeppg = PG_TBL_SIZE - 1
fin
end
//
// Set end of memory pool
//
export def brk(addr)
word heapalign, brkblk, brksiz, srch
//
// Check if addr is too high or low
//
if isuge(addr, @heapalign); return 0; fin
if isule(addr, heapmark); return 0; fin
if not pooladdr
//
// Allocate the memory pool
//
heapalign = (heapmark | MIN_BLK_MASK) + 1
brksiz = addr - heapalign
if isult(brksiz, MAX_BLK_SIZE); return 0; fin // Not enough heap
poolsize = addr - heapmark
pooladdr = heapalloc(poolsize)
if pooladdr
frelst = heapalign
frelst=>fresiz = brksiz
frelst=>frenxt = 0
else
poolsize = 0
fin
else
//
// Can we extend the memory pool?
//
if pooladdr + poolsize == heapmark
brksiz = addr - heapmark
brkblk = heapalloc(brksiz)
if brkblk
//
// Add block to end of free list
//
poolsize = poolsize + brksiz
brkblk=>fresiz = brksiz
brkblk=>frenxt = 0
if frelst
srch = frelst
while srch=>frenxt; srch = srch=>frenxt; loop
srch=>frenxt = brkblk
else
frelst = brkblk
fin
coallesce // combine adjacent free space
fin
fin
fin
return poolsize
end
export def sbrk(size)
return brk((heapmark | MIN_BLK_MASK) + 1 + size + MIN_BLK_SIZE)
end
//
// Return the amount of free memory available, after garbage
// collection, swaps
//
export def hmemFre(lptr)
word srch, free, largest
coallesce
while compact; loop
free = 0
largest = 0
srch = frelst
while srch
//putc('F'); putc(' '); puth(srch); putc(':'); puth(srch=>fresiz); putln
free = free + srch=>fresiz
if srch=>fresiz > largest
largest = srch=>fresiz
fin
srch = srch=>frenxt
loop
if lptr
*lptr = largest - MIN_BLK_SIZE
fin
return free - MIN_BLK_SIZE
end
//
// Allocate memory block
//
export def hmemNew(size)
word page, entry, hnew, memblk
//
// First, find a free handle
//
for page = PG_TBL_SIZE - 1 downto 0
if !hpgtbl[page]
//
// Allocate a new page table
//
hpgtbl[page] = heapalloc(PG_SIZE)
memset(hpgtbl[page], HMEM_AVAIL, PG_SIZE)
//puts(@allocpgstr);puth(hpgtbl[page]);putln
//
// Check if we need to allocate the memory pool
//
if not pooladdr
//
// Allocate 3/4 of available heap on 128K machine, 1/2 on 64K machine
//
poolsize = (heapavail >> 1) & $7FFF
if MACHID & $30 == $30
poolsize = poolsize + (poolsize >> 1)
fin
if isult(poolsize, MAX_BLK_SIZE)
poolsize = MAX_BLK_SIZE
fin
sbrk(poolsize)
fin
fin
for entry = 255 downto 0
if hpgtbl:[page, entry].lsb == HMEM_AVAIL
//
// Reserve handle as swapped out block
// Nonzero size will flag swapin to not read from disk
//
//putc('N');putc(' ');putb(entry);putb(page);putc('@')
size = ((size + t_memblk) | MIN_BLK_MASK) + 1
hpgtbl:[page, entry] = size | HMEM_SWAPPED
hnew.lsb = page
hnew.msb = entry
return hnew
fin
next
next
return 0
end
//
// Lock memory block in place
//
export def hmemLock(hmem)
word memblk
memblk = hpgtbl:[hmem.lsb, hmem.msb]
when memblk.lsb & HMEM_STATE
is HMEM_SWAPPED
memblk = swapin(hmem)
if not memblk; return 0; fin
is HMEM_MOVEABLE
hpgtbl:[hmem.lsb, hmem.msb] = memblk | HMEM_LOCKED
is HMEM_LOCKED
memblk = memblk & HMEM_ADDR
memblk->blklok = (memblk->blklok + 1) | HMEM_ACCESSED
//putc('L');putc(' ');puth(hmem);putc('@');puth(memblk);putln
return memblk + t_memblk
wend
return 0
end
//
// Unlock memory block
//
export def hmemUnlock(hmem)#0
byte lock
word memblk
memblk = hpgtbl:[hmem.lsb, hmem.msb]
if memblk.lsb & HMEM_STATE == HMEM_LOCKED
memblk = memblk & HMEM_ADDR
lock = memblk->blklok - 1
if lock == HMEM_ACCESSED
//
// Block is now moveable
//
hpgtbl:[hmem.lsb, hmem.msb] = memblk
fin
memblk->blklok = lock
//putc('U');putc(' ');puth(hmem);putln
fin
end
//
// Increment reference count
//
export def hmemRef(hmem)#0
word memblk
memblk = hpgtbl:[hmem.lsb, hmem.msb]
when memblk.lsb & HMEM_STATE
is HMEM_SWAPPED
memblk = swapin(hmem)
is HMEM_LOCKED
is HMEM_MOVEABLE
memblk = memblk & HMEM_ADDR
if not memblk; return; fin
memblk->blkref = memblk->blkref + 1
wend
end
//
// Decrement reference count
//
export def hmemDel(hmem)#0
byte ref
word memblk
memblk = hpgtbl:[hmem.lsb, hmem.msb]
when memblk & HMEM_STATE
is HMEM_SWAPPED
memblk = swapin(hmem)
is HMEM_LOCKED
is HMEM_MOVEABLE
memblk = memblk & HMEM_ADDR
if not memblk; return; fin
ref = memblk->blkref - 1
if ref == 0
//
// No more references, free block
//
hpgtbl:[hmem.lsb, hmem.msb] = HMEM_AVAIL
addfre(memblk)
else
memblk->blkref = ref
fin
break
wend
end
//
// Initialization:
//
// Search for best swap volume
//
// !!! Does this work on Apple ///???
//
sysbuf = $0800 // heapallocalign(1024, 8, 0)
initdata = heapalloc(t_initdata) // Use data on heap for initialization
initdata=>volparms.0 = 2
initdata=>volparms.1 = 0
initdata=>volparms:2 = sysbuf
syscall($C5, @initdata=>volparms)
initdata=>volptr = sysbuf
initdata=>ramfree = 0
initdata=>bestfree = 0
for sweepen = 0 to 15
^initdata=>volptr = ^initdata=>volptr & $0F
if ^initdata=>volptr
memcpy(@swapvol + 2, initdata=>volptr + 1, ^initdata=>volptr)
swapvol = ^initdata=>volptr + 1
fileio:getfileinfo(@swapvol, @initdata->volinfo)
initdata=>freeblks = initdata=>volinfo:2 - initdata=>volinfo:5
if initdata=>volptr:1 == $522F and initdata=>volptr:3 == $4D41 // '/RAM'
if isugt(initdata=>freeblks, initdata=>ramfree)
initdata=>ramvol = initdata=>volptr
initdata=>ramfree = initdata=>freeblks
fin
elsif isugt(initdata=>freeblks, initdata=>bestfree)
initdata=>bestvol = initdata=>volptr
initdata=>bestfree = initdata=>freeblks
fin
fin
initdata=>volptr = initdata=>volptr + 16
next
if isugt(initdata=>ramfree, $40) or isugt(initdata=>ramfree, initdata=>bestfree) // RAMdrive greater than 64K? Use it
initdata=>bestvol = initdata=>ramvol
initdata=>bestfree = initdata=>ramfree
fin
memcpy(@swapvol + 2, initdata=>bestvol + 1, ^initdata=>bestvol)
swapvol = ^initdata=>bestvol + 1
memcpy(@swapvol + swapvol + 1, @swapdir + 1, swapdir)
swapvol = swapvol + swapdir
fileio:create(@swapvol, $0F, $0000) // Create the swap directory
//
// Clear out left over swap files
//
initdata->catref = fileio:open(@swapvol)
initdata->firstblk = 1
initdata->swapstrlen = swapvol
repeat
if fileio:read(initdata->catref, @initdata->catalog, 512) == 512
initdata=>catentry = @initdata->catalog.4
if initdata->firstblk
initdata->entrylen = initdata->catalog.$23
initdata->entriesblk = initdata->catalog.$24
initdata=>filecnt = initdata=>catalog:$25
initdata=>catentry = initdata=>catentry + initdata->entrylen
fin
for sweepen = initdata->firstblk to initdata->entriesblk
^initdata=>catentry = ^initdata=>catentry & $0F
if ^initdata=>catentry
memcpy(@swapvol + swapvol + 1, initdata=>catentry + 1, ^initdata=>catentry)
swapvol = swapvol + ^initdata=>catentry
fileio:destroy(@swapvol)
swapvol = initdata->swapstrlen
fin
initdata=>catentry = initdata=>catentry + initdata->entrylen
next
initdata->firstblk = 0
else
initdata->filecnt = 0
fin
until !initdata->filecnt
fileio:close(initdata->catref)
heaprelease(initdata)
//puts(@swapvol); putln
done