Header-ized mem manager, and cleaned up globals.

This commit is contained in:
Martin Haye 2014-01-20 12:14:02 -08:00
parent e05d1c247f
commit f503794c1e
3 changed files with 31 additions and 251 deletions

View File

@ -1,254 +1,23 @@
; Memory manager
; ------------------
;
; Memory is managed in variable-sized segments. In each 48kb memory bank (main and aux),
; a linked list identifies each segment there as well as usage flags to mark free, used,
; or reserved memory.
;
; Memory is marked as used as it is loaded by the loader, but the caller program
; should free it as soon as the memory is no longer in use. It is very possible that
; the memory will not be reclaimed right away and could be reinstated as in-use without
; a loading penalty.
;
; Another scenario is that free memory will be relocated to auxiliary banks
; and potentially later restored to active memory at a later time. Depending on the
; loader, this might be handled in different ways. Aux memory should be kept open
; such that memory can still be reclaimed. Extended memory (e.g. RamWorks and slinky
; ram expansions) can adopt a more linear and predictable model if they are large enough.
;
; Loading operations are performed in sets. A set is begun with the START_LOAD call, and
; subsequent QUEUE_LOAD requests are queued up. The set is then executed with
; FINISH_LOAD. The purpose of queuing the requests is to allow the disk loader to
; sort the requests in storage order and thus optimize loading speed. During the period
; between START_LOAD and FINISH_LOAD, the area in main memory from $4000 to $5FFF is
; reserved for loader operations. Therefore, if hi-res graphics on page 2 are being
; displayed, it is important to copy them to page 1 ($2000.3FFF) and switch to that
; page before queueing any loads.
;
; Additional loaders may be inserted between main/aux and the disk loader, to implement
; caching schemes in extended ram. The goal of using extended ram is to prevent future
; disk access as much as possible, because even an inefficient o(N) memory search is
; going to be faster than spinning up a disk.
;
; ----------------------------
; Segment table format in memory:
; Linked list of segments. Segments are generally indexed by the X register. There is
; one list of main memory, another list for aux mem. They are intermixed in the segment
; table space. First segment of main mem is always seg 0; first of aux mem is seg 1.
;
; tSegType,x:
; FF00tttt nnnnnnnn llllllll aaaaaaaa bbbbbbbb
; F = Flags
; 7 - Active/Inactive
; 6 - Locked (cannot be reclaimed for any reason)
; t = Type of resource (1-15, 0 is invalid)
; tSegResNum,x:
; resource number (1-255, 0 is invalid or no resource loaded)
; tSegAdrLo,x and tSegAdrHi,x:
; address of segment in memory
; tSegLink,x:
; link to next segment
;
; Essentially there are three distinct lists. Main mem (starts at seg 0), aux mem
; (starts at seg 1), and the unused list (starts at segment stored in unusedSeg).
;
; There is an extra link associated with each of (main, aux) mem. It's indexed by the
; Y register (y=0 is main, y=1 is aux):
;
; scanStart,y:
; segment of last successful allocation. Used to quickly pick up for next
; allocation.
;
; -------------------------
; Page file format on disk:
; File must be named: GAME.PART.nnn where nnn is the partition number (0-15)
; File Header:
; bytes 0-1: Total # of bytes in header (lo, hi)
; bytes 2-n: Table of repeated resource entries
; Resource entry:
; byte 0: resource type (1-15), 0 for end of table
; byte 1: resource number (1-255)
; bytes 2-3: number of bytes in resource (lo, hi)
; The remainder of the file is the data for the resources, in order of their
; table appearance.
;
startMemMgr = $800
mainLoader = $803
auxLoader = $806
; See detailed description in mem.i
;------------------------------------------------------------------------------
; Command codes
;------------------------------------------------------------------------------
RESET_MEMORY = $10
; Input: None
;
; Output: None
;
; Mark all memory as inactive, except the following areas in main memory
; which are always locked:
;
; 0000.01FF: Zero page and stack
; 0200.02FF: Input buffer and/or scratch space
; 0300.03FF: System vectors, scratch space
; 0400.07FF: Text display
; 0800.0xFF: The memory manager and its page table
; 2000.5FFF: Hi-res graphics display buffers
; BF00.BFFF: ProDOS system page
;
; Note that this does *not* destroy the contents of memory, so for instance
; future RECALL_MEMORY commands may be able to re-use the existing contents
; of memory if they haven't been reallocated to something else.
;
; This command is acted upon and then passed on to chained loaders.
;------------------------------------------------------------------------------
REQUEST_MEMORY = $11
; Input: X-reg(lo) / Y-reg(hi) - number of bytes to allocate
;
; Output: X-reg(lo) / Y-reg(hi) - address allocated
;
; Allocate a segment in the memory space of this loader. If there
; isn't a large enough continguous memory segment available, the system
; will be halted immediately with HALT_MEMORY.
;
; Normally this command chooses the location of the memory area; if you
; want to force it to use a particular location, use SET_MEM_TARGET first.
;
; To allocate main memory, call the main memory loader. To allocate aux
; mem, call that loader instead.
;
; This command is acted upon immediately and chained loaders are not called.
;------------------------------------------------------------------------------
LOCK_MEMORY = $12
; Input: X-reg(lo) / Y-reg(hi) - address of segment to lock
;
; Output: None
;
; Locks a previously requested or loaded segment of memory so that it
; cannot be reclaimed by RESET_MEMORY.
;
; This command is acted upon immediately and chained loaders are not called.
;------------------------------------------------------------------------------
UNLOCK_MEMORY = $13
; Input: X-reg(lo) / Y-reg(hi) - address of segment to unlock (must be start
; of a memory area that was previously locked)
;
; Output: None
;
; Mark a segment of memory as unlocked, so it can be reclaimed by
; RESET_MEMORY.
;------------------------------------------------------------------------------
SET_MEM_TARGET = $14
; Input: X-reg(lo) / Y-reg(hi) - address to target
;
; Output: None
;
; Sets the target address in memory for the next REQUEST_MEMORY or QUEUE_LOAD
; command. This will force allocation at a specific location instead
; allowing the loader to choose.
;
; This is a one-shot command, i.e. as soon as an allocation is performed,
; subsequent allocations will revert to their normal behavior.
;------------------------------------------------------------------------------
START_LOAD = $15
; Input: X-reg - disk partition number (0 for boot disk, 1-15 for others)
;
; Output: None
;
; Marks the start of a set of QUEUE_LOAD operations, that will be
; acted upon when FINISH_LOAD is finally called.
;
; The partition is recorded and passed on to chained loaders.
;------------------------------------------------------------------------------
QUEUE_LOAD = $16
; Input: X-reg - resource type
; Y-reg - resource number
;
; Output: X-reg(lo) / Y-reg (hi) - address the load will occur at
;
; This is the main entry for loading resources from disk. It queues a load
; request, allocating memory to hold the entire resource. Note that
; the load is only queued; it may not be completed until FINISH_LOAD.
;
; Normally this command chooses the location of the memory area; if you
; want to force it to use a particular location, use SET_MEM_TARGET first.
;
; Note that if the data is already in memory (in active or inactive state),
; it will be activated if necessary and its former location will be
; returned and no disk access will be queued.
;
; The request is either acted upon by this loader, or passed onto the
; next chained loader. If there is no next loader, a FATAL_ERROR is
; triggered.
;------------------------------------------------------------------------------
FINISH_LOAD = $17
; Input: None
;
; Output: None
;
; Completes all prior QUEUE_LOAD requests, clearing the queue. It's the
; last part of a START_LOAD / QUEUE_LOAD / FINISH_LOAD sequence.
;
; This command is acted upon by this loader and passed to chained loaders.
;------------------------------------------------------------------------------
FREE_MEMORY = $18
; Input: X-reg(lo) / Y-reg(hi) - address of segment to mark as free (must
; be start of a memory area that was previously requested or loaded)
;
; Output: None
;
; Mark a segment of memory as free, or rather inactive, so that it can be
; reused. This also clears the lock bit!
;------------------------------------------------------------------------------
CHAIN_LOADER = $1E
; Input: X-reg / Y-reg - pointer to loader (X=lo, Y=hi) to add to chain
;
; Output: None
;
; Add a loader to the chain just after this loader. The current next
; loader (if there is one) will be passed to the new loader with another
; CHAIN_LOADER command.
;
; The purpose of a loader chain is to insert faster devices between the
; main/aux loader (fastest) and the disk loader (slowest). Note that the
; main mem and aux mem loaders are conceptually one; a chained loader will
; always be inserted after them, not between them.
;------------------------------------------------------------------------------
FATAL_ERROR = $1F
; Input: X-reg(lo) / Y-reg(hi): message pointer
;
; Output: Never returns
;
; Switches to text mode, prints out the zero-terminated ASCII error message
; pointed to by the parameters, plus the call stack, and then halts the
; system (i.e. it waits forever, user has to press Reset).
;
; This command halts and thus never returns.
;------------------------------------------------------------------------------
; code begins here
.org $800
.include "../include/global.i"
.include "mem.i"
; constants
MAX_SEGS = 96
; zero page
reqLen = $6 ; length 2
resType = $8 ; length 1
resNum = $9 ; length 1
isAuxCommand = $A ; length 1
; zero page temporary variables
tmp = $2 ; len 2
pTmp = $4 ; len 2
reqLen = $6 ; len 2
resType = $8 ; len 1
resNum = $9 ; len 1
isAuxCommand = $A ; len 1
; memory buffers
fileBuffer = $4000 ; len $400
@ -683,7 +452,7 @@ test_load:
lda #SET_MEM_TARGET
jsr mainLoader
DEBUG_STR "Queue load."
ldx #1 ; TYPE_CODE = 1
ldx #RES_TYPE_CODE
ldy #1 ; code resource #1
lda #QUEUE_LOAD
jsr mainLoader
@ -698,7 +467,7 @@ test_load:
lda #SET_MEM_TARGET
jsr auxLoader
DEBUG_STR "Queue load."
ldx #1 ; TYPE_CODE = 1
ldx #RES_TYPE_CODE
ldy #2 ; code resource #2
lda #QUEUE_LOAD
jsr auxLoader
@ -708,10 +477,12 @@ test_load:
DEBUG_WORD tmp
DEBUG_LN
DEBUG_STR "Finish load."
ldx #0 ; close out
lda #FINISH_LOAD
jmp mainLoader
jsr mainLoader
rts
; obsolete test
; exhaustion test
DEBUG_STR "Testing memory manager."
@loop:
ldx #1
@ -1149,7 +920,8 @@ disk_queueLoad:
;------------------------------------------------------------------------------
disk_finishLoad:
lda partitionFileRef ; See if we actually queued anything (and opened the file)
stx @keepOpenChk+1 ; store flag telling us whether to keep open or close
lda partitionFileRef ; see if we actually queued anything (and opened the file)
bne :+ ; non-zero means we have work to do
rts ; nothing to do - return immediately
: sta @setMarkFileRef ; copy the file ref number to our MLI param blocks
@ -1164,7 +936,7 @@ disk_finishLoad:
jsr startHeaderScan ; start scanning the partition header
@scan:
lda (pTmp),y ; get resource type byte
beq @done ; zero = end of header
beq @keepOpenChk ; zero = end of header
bmi @load ; hi bit set -> queued for load
iny ; not set, not queued, so skip over it
iny
@ -1173,6 +945,7 @@ disk_finishLoad:
tax ; save flag ($40) to decide where to read
and #$F ; mask to get just the resource type
sta resType ; save type for later
sta (pTmp),y ; also restore the header to its pristine state
iny
lda (pTmp),y ; get resource num
iny
@ -1228,13 +1001,16 @@ disk_finishLoad:
bne @scan ; exactly 64 4-byte entries per 256 bytes
inc pTmp+1
bne @scan ; always taken
@done:
@keepOpenChk:
lda #11 ; self-modified to 0 or 1 at start of routine
beq @keepOpen
jsr mli ; now that we're done loading, we can close the partition file
.byte MLI_CLOSE
.word @closeParams
bcs @prodosErr
lda #0 ; zero out...
sta partitionFileRef ; ... the file reference so we know it's no longer open
@keepOpen:
rts
@prodosErr:
jmp prodosError

View File

@ -1,8 +1,10 @@
; Shared definitions for all modules
; Zero page temps
tmp = $2 ; len 2
pTmp = $4 ; len 2
; Zero page temporary area. Modules can feel free to use the entire space,
; but must *not* count on it being preserved when other modules are in
; control, e.g. when calling other modules, or returning to them.
zpTempStart = $2 ; 0 and 1 are reserved on c64
zpTempEnd = $1F
; Zero page monitor locations
a2l = $3E
@ -36,7 +38,7 @@ MLI_GET_EOF = $D1
MLI_SET_BUF = $D2
MLI_GET_BUF = $D3
; I/O locations
; I/O soft switches
kbd = $C000
clrAuxRd = $C002
setAuxRd = $C003

View File

@ -23,6 +23,8 @@ BLIT_OFF6 = 24
BLIT_STRIDE = 29
; Renderer zero page
tmp = $4 ; len 2
pTmp = $6 ; len 2
pDst = $8 ; len 2
pTex = $A ; len 2
pixNum = $C ; len 1