The C64 OS Programmer's Guide is being written
This guide is being written and released and few chapters at a time. If a chapter seems to be empty, or if you click a chapter in the table of contents but it loads up Chapter 1, that's mostly likely because the chapter you've clicked doesn't exist yet.
Discussion of development topics are on-going about what to put in this guide. The discusssions are happening in the C64 OS Community Support Discord server, available to licensed C64 OS users.
C64 OS PROGRAMMER'S GUIDE
Chapter 4: Using the KERNAL → Memory (module)
General Usage
These routines are general purpose; they can be used for anything. They are offered by the KERNAL because other parts of the KERNAL need to use this functionality.
memcpy
Purpose | To copy 256-bytes of memory from one page to another. |
---|---|
Module offset | 0 |
Communication registers | A, Y |
Stack requirements | 2 bytes |
Registers affected | None |
Input parameters |
A → Source page address Y → Destination page address |
Output parameters | None |
Description: This routine is used to copy 256-bytes of page-aligned memory from one page to another. It could be used, for example, to make a copy of a file reference or some other page-sized, page-aligned, memory structure. Load the source page number into the accumulator. Load the destination page number into the Y register.
memset
Purpose | Fill a 256-byte page of memory with a byte. |
---|---|
Module offset | 3 |
Communication registers | A, Y |
Stack requirements | 2 bytes |
Registers affected | None |
Input parameters |
A → Fill byte value Y → Page address to fill |
Output parameters | None |
Description: This routine is used to fill a page with a given byte. It is called automatically by pgalloc to clear the previous contents of the page. It can be used to pre-zero a page before reading data into it, such that the data, if less than 256 bytes, is guaranteed to be null terminated. Load the page address to fill into the Y register and the byte to fill the page with into the accumulator.
memfree
Purpose | Count the number of unallocated memory pages. |
---|---|
Module offset | 6 |
Communication registers | X |
Stack requirements | 2 bytes |
Registers affected | A, X, Y |
Input parameters | None |
Output parameters | X ← Number of free pages |
Description: This routine loops over the paged-memory allocation table and counts up all the pages that are marked as free (unallocated.) It returns the total number of free pages. The pages are not necessarily contiguous, and in fact, probably are not.
Memory Allocation
The following routines are for doing arbitrarily sized memory allocations. They require the existence of a memory pool from which to find and make the allocations. These routines are analogous to the C functions malloc and free. Realloc is available in the library mem.lib.r.
free
Purpose | Deallocate a block of memory previously allocated using malloc. |
---|---|
Module offset | 9 |
Communication registers | X, Y |
Stack requirements | 2 bytes |
Zero page usage | $31, $32 |
Registers affected | A, X, Y |
Input parameters | RegPtr → pointer to malloc'd memory. |
Output parameters | None |
Description: This routine deallocates a block of memory that was previously allocated by malloc. The RegPtr must point to the exact address that was returned by malloc. You must only call free on a block of memory that has not previously been freed.
malloc
Purpose | Allocate an arbitrarily sized block of memory. |
---|---|
Module offset | 12 |
Communication registers | A, X, Y |
Stack requirements | 6 bytes |
Zero page usage | $31, $32, $33, $34 |
Registers affected | A, X, Y |
Input parameters |
A → Memory pool (start page) RegWrd → 16-bit length to allocate |
Output parameters |
C ← Set on error RegPtr ← Pointer to start of allocated memory |
Description: This routine allocates a block of memory that has an arbitrary size, from 1 byte to multiple kilobytes. It requires a memory pool from which to make the allocation. Every call to pgalloc results in a block of one or more contiguous pages that, together, are initialized as a memory pool. When calling the malloc the first page byte of a valid memory pool must be provided in the accumulator.
A memory pool is only valid if its initialized memory pool header hasn't been overwritten, and the memory within the pool has been properly allocated with malloc rather than just being arbitrarily written to.
Free marks a previously allocated block as unallocated but leaves the allocation headers in place. Malloc starts at the beginning of the memory pool looking for an unallocated block equal or greater in size to the size being requested. If it encounters a block that is too small, it immediately attempts to merge the current block with another unallocated block following it. If the merge is successful, it checks again to see if this block is big enough, and repeatedly merges in blocks until one of two things has happened: The last merge resulted in a block big enough to use, or it was unable to do a merge because the following block is allocated. At this point it continues the search through the memory pool. When it finally finds a block big enough to use, it splits the block into the size it needs followed by a smaller block containing the remainder. Finally, if it is unable to find a block big enough to allocate, it returns with the carry set to indicate a failure.
Paged-Memory Allocation
These three routines are used for managing the paged-memory allocation system. This is a lower-level memory allocation system than malloc. The paged memory allocation uses a paged-memory allocation map, defined at memmap by //os/s/:memory.s
pgfree
Purpose | Deallocate a range of pages previously allocated using pgalloc. |
---|---|
Module offset | 15 |
Communication registers | X, Y |
Stack requirements | 4 bytes |
Zero page usage | None |
Registers affected | A, X, Y |
Input parameters |
Y → Page address to free X → Number of consecutive pages to free |
Output parameters | C ← Set on error |
Description: This routine is used to mark pages in the paged allocation table as free. It should be used to reverse the action of pgalloc, however it is less stringent than free. While free can only be used on a pointer that was previously allocated with malloc, and it always deallocates the exact size that was originally allocated, pgfree doesn't care how the pages were originally allocated or who allocated them. It just does exactly what it's told and marks X number of pages free starting at page Y.
If a block of pages is allocated with pgalloc, that block gets initialized automatically as a memory pool. If any part of that pool is subsequently freed using pgfree, the memory pool is no longer valid for use with malloc and free.
This routine calls updstat in the service module, to mark the status bar as dirty and requiring a redraw.
pgmark
Purpose | Manually mark a range of pages as allocated. |
---|---|
Module offset | 18 |
Communication registers | X, Y |
Stack requirements | 4 bytes |
Zero page usage | None |
Registers affected | A, X, Y |
Input parameters |
X → First page to mark Y → Last page to mark |
Output parameters | None |
Description: This routine is used to manually mark a range of pages as allocated by the Application. It does not do any validation checking, it just writes #mapapp to the page allocation table for all pages from Y to X, inclusive.
This routine calls updstat in the service module, to mark the status bar as dirty and requiring a redraw.
pgalloc
Purpose | Allocate a set of contiguous pages. |
---|---|
Module offset | 21 |
Communication registers | A, X, Y |
Stack requirements | 4 bytes |
Zero page usage | $31, $32 |
Registers affected | A, X, Y |
Input parameters |
A → Allocation type X → Number of contiguous pages to allocate |
Output parameters |
Y ← First allocated page address C ← Set on errror |
Description: This routine is used to allocate a contiguous range of pages. You put the allocation type in the accumulator, and the number of pages to allocate in the X register. Upon return, the carry is set if it was unable to find the requested number of contiguous free pages. Nothing is allocated on error. If the carry is clear, the Y register holds the first page address of the range that was allocated.
Pages allocated using pgalloc are automatically zeroed, clearing their previous contents, and initialized as a memory pool, suitable for use with malloc and free.
This routine calls updstat in the service module, to mark the status bar as dirty and requiring a redraw.
The available allocation types are defined by //os/s/:memory.s. One byte for each page appears in the paged-memory allocation map. The map only covers page $09 to page $a0. The pages outside of this range are either implicitly unavailable or managed in a different way.
Allocation Type | Value | Description |
---|---|---|
mapfree | $00 | Page is unallocated |
mapsys | $01 | Page is allocated by the system |
maputil | $02 | Page is allocated by a Utility |
mapapp | $ff | Page is allocated by an Application |
REU Helper Routines
These routines help with storing and retrieving page-sized blocks of memory to and from an REU. These routines honor the number of 64K banks that have been reserved for fast app switching, and abstract which banks are actually being used.
reuconf
Purpose | Configure the REU for a pgfetch or pgstash transfer. |
---|---|
Module offset | 24 |
Communication registers | A, X, Y |
Stack requirements | 2 bytes |
Zero page usage | None |
Registers affected | A, X, Y |
Input parameters |
RegPtr → REU bank and page A → Configure flags |
Output parameters |
C ← Set on no REU available A ← Number of banks available to the Application |
Description: This routine prepares the REU for a pgfetch or pgstash transfer. The RegPtr sets the bank and page numbers, these are the REU's high and mid bytes for its internal storage. The high byte gets internally offset by adding to it the number of reserved banks for fast app switching.
The only configuration flag available is reu_inc, defined in //os/s/:io_rec.s. If this is set, then after any pgstash or pgfetch the REU's internal bank and page are incremented. This allows you to set the start bank/page and then call pgfetch repeatedly to continually get the next page of data.
pgfetch
Purpose | Fetch a 256-byte page of data from an REU. |
---|---|
Module offset | 27 |
Communication registers | X, Y |
Preparatory routines | reuconf |
Stack requirements | 2 bytes |
Zero page usage | None |
Registers affected | A, X, Y |
Input parameters |
RegPtr → C64 memory buffer to fetch to |
Output parameters |
C ← Set on error |
Description: This routine fetches a 256-byte page of memory from an REU to the C64's main memory. The RegPtr passed to this routine specifies where in the C64's memory the fetched data should be put. I/O is mapped in for this transfer. The C64's memory buffer does not need to be page aligned. You must call reuconf prior to calling pgfetch. If reuconf was configured to auto increment the bank and page, then after calling pgfetch the REU's page number is incremented. If the page number rolls over from $ff to $00, then the bank number is incremented. pgfetch can be called multiple times after calling reuconf only once in preparation.
pgstash
Purpose | Stash 256-bytes from main memory to a page-aligned block of memory in an REU. |
---|---|
Module offset | 30 |
Communication registers | X, Y |
Preparatory routines | reuconf |
Stack requirements | 2 bytes |
Zero page usage | None |
Registers affected | A, X, Y |
Input parameters |
RegPtr → C64 memory buffer to stash from |
Output parameters |
C ← Set on error |
Description: This routine stashes 256-bytes from the C64's main memory to a page-aligned block of memory in the REU. The RegPtr passed to this routine specifies where in the C64's memory the stashed data should come from. I/O is mapped in for this transfer. The KERNAL and/or BASIC ROMs may or may not be mapped in for this transfer. The C64's memory source does not need to be page aligned. You must call reuconf prior to calling pgstash. If reuconf was configured to auto increment the bank and page, then after calling pgstash the REU's page number is incremented. If the page number rolls over from $ff to $00, then the bank number is incremented. pgstash can be called multiple times after calling reuconf only once in preparation.
KERNAL Modules in Alphabetical Order
KERNAL Modules in Module Lookup Table Order
Return to Using the KERNAL → KERNAL Modules
Table of Contents
This document is subject to revision updates.
Last modified: Aug 31, 2024