Memory

#include "libtini/memory.h"

This header gives the bank grant, checked near moves and fills, and banked resources. A resource has functions to allocate, release, read, write, fill and copy.

FunctionPurpose
ti_initGrant RamWorks banks to the library
ti_shutdownStop audio and graphics and make all resources not valid
ti_near_moveChecked memmove in near memory
ti_near_fillChecked memset in near memory
ti_res_allocAllocate a banked resource
ti_res_freeRelease a resource
ti_res_readCopy resource bytes into a near buffer
ti_res_writeCopy a near buffer into a resource
ti_res_fillFill a part of a resource with one byte
ti_res_copyCopy between two resources or in one resource

Constants

NameValueMeaning
TI_RESOURCE_SLOTS8The number of resources that can be live at the same time, the resources of the library included
TI_BANK_BYTES48,640The resource bytes in one granted bank: its range $0200-$BFFF
TI_TRANSFER_BYTES128The largest CPU burst while a bank is mapped and interrupts are masked

Rules for all calls

Functions

ti_init

ti_error ti_init(uint8_t first_bank, uint8_t bank_count);

This function grants the RamWorks selectors first_bank to first_bank + bank_count - 1 to the library for resources. With the grant, the game makes sure that no other software uses these banks, for example a RAM disk driver. libtini cannot find the owner of a bank.

The function examines the banks before it accepts the grant. It saves one byte at AUX $A000 of bank 0 and of each granted bank. It writes a different test value to each, reads all values back and restores the original bytes. This finds missing banks, and banks that are aliases of other banks or of bank 0. It does not test each byte, and it does not identify the firmware.

first_bank
The first selector, 1 to 126. Selector 0 holds the OS and the screen. The game cannot grant it.
bank_count
1 or more. The last selector must be 126 or less. F1.3.2 serves selectors 0 to 126 from its first 8 MiB PSRAM chip, thus the function rejects 127.

Returns

TI_OKThe grant is active. The capacity is bank_count * TI_BANK_BYTES.
TI_E_STATEThe library is already initialized.
TI_E_ARGUMENTZero banks, bank 0, or a range past 126.
TI_E_UNAVAILABLEThe test found a missing bank or an alias. The test bytes are restored.
/* A 320x200 canvas, the screen backup and the temporary loan for audio
 * use 97,536 bytes: three banks. */
if (ti_init(1, 3) != TI_OK) return 1;

ti_shutdown

ti_error ti_shutdown(void);

This function stops audio when it runs. Audio shutdown ends the video session first, thus ProDOS has its AUX bytes again before the library removes the interrupt handler. Then the function ends the video session and restores the saved screen bytes. It removes the optional AMEM hooks, releases all resource slots and ends the grant.

A second call does nothing and returns TI_OK. The startup code of the program calls this function when main returns.

When the function releases a slot, it increases the generation of the slot. Thus each handle from before the shutdown is not valid, also after a new ti_init in the same program run.

Returns

TI_OKThe function released all items.
TI_E_IOProDOS did not remove the audio interrupt handler. Audio is silent, but the library keeps the handler code and the other resources. Call ti_shutdown again to try again.

ti_near_move

ti_error ti_near_move(void *dst, const void *src, uint16_t length);

This function copies length bytes with memmove semantics. Ranges that overlap copy correctly in the two directions. The destination must be fully in one writable near region. The source must be fully in one readable region. With a zero length, the pointers can be NULL.

Returns

TI_OK, or TI_E_RANGE when a range goes out of its region. The function then copies nothing.

ti_near_fill

ti_error ti_near_fill(void *dst, uint8_t value, uint16_t length);

This function writes value to length bytes of writable near memory.

Returns

TI_OK, or TI_E_RANGE when the range goes out of its region.

ti_res_alloc

ti_error ti_res_alloc(uint32_t size, ti_handle *out);

This function allocates size contiguous bytes in the granted banks. It uses the first free range that is sufficiently large. It does not clear the bytes. An allocation can cross bank boundaries; the library splits transfers at these boundaries.

size
1 to the free capacity.
out
A writable near ti_handle. It receives the handle. After the pointer check, each failure stores TI_INVALID_HANDLE in it.

Returns

TI_OKThe handle is in *out.
TI_E_ARGUMENTout is not writable near memory, or size is zero.
TI_E_STATENo ti_init grant is active.
TI_E_NOMEMAll eight slots are in use, or no free range of size bytes is available.

When the generation of a slot gets to 4095, the slot stops. It does not wrap to a value that an old handle can still hold. This occurs only after a program allocates and frees one slot thousands of times.

ti_res_free

ti_error ti_res_free(ti_handle handle);

This function releases the resource. All copies of the handle become not valid. All surface, sprite, font and map views of the resource also become not valid.

Returns

TI_OK, or TI_E_HANDLE for a handle that is not live. A second free of the same handle also gives TI_E_HANDLE.

ti_res_read

ti_error ti_res_read(ti_handle handle, uint32_t offset, void *dst, uint16_t length);

This function copies length bytes from offset in the resource into the near buffer dst.

Returns

TI_OKThe function copied the bytes.
TI_E_HANDLEThe handle is not live.
TI_E_RANGEThe range goes past the end of the resource, or dst is not writable near memory for length bytes.
TI_E_IOOptional AMEM build only: a submitted accelerated transfer failed. See AMEM.

ti_res_write

ti_error ti_res_write(ti_handle handle, uint32_t offset, const void *src, uint16_t length);

This function copies length bytes from the near buffer src into the resource at offset. The source can be any readable near memory, constants included.

Returns

The same results as ti_res_read. The function also returns TI_E_RANGE when src is not readable near memory.

ti_res_fill

ti_error ti_res_fill(ti_handle handle, uint32_t offset, uint8_t value, uint32_t length);

This function writes value to length bytes of the resource. The length has 32 bits, thus one call can fill a full resource that uses many banks.

Returns

TI_OK, TI_E_HANDLE, TI_E_RANGE, or TI_E_IO in the optional AMEM build.

ti_res_copy

ti_error ti_res_copy(ti_handle dst, uint32_t dst_offset,
                     ti_handle src, uint32_t src_offset, uint32_t length);

This function copies length bytes from one resource to a different resource, or in one resource. It has memmove semantics in the two overlap directions. The CPU path moves the bytes through a 128-byte buffer in main memory. Different live allocations do not overlap, thus only a copy in one resource can overlap.

Returns

TI_OK; TI_E_HANDLE when a handle is not live; TI_E_RANGE when a range goes past the end of its resource; TI_E_IO in the optional AMEM build.

/* Move a 64-row map buffer up by one 32-byte row, in the same resource. */
ti_res_copy(map, 0, map, 32, 63UL * 32);

Example: a level buffer in a bank

#include "libtini.h"

#define LEVEL_BYTES 20000UL
static ti_handle level;
static uint8_t row[64];                 /* near work buffer */

ti_error load_level(void)
{
    ti_error e = ti_res_alloc(LEVEL_BYTES, &level);
    if (e) return e;
    e = ti_res_fill(level, 0, 0, LEVEL_BYTES);      /* new allocations are not cleared */
    if (e) return e;
    /* ... write rows from a pack or a generator ... */
    return TI_OK;
}

uint8_t tile_at(uint16_t x, uint16_t y)
{
    /* Read one full row, then use the near copy. */
    ti_res_read(level, (uint32_t)y * 64, row, sizeof(row));
    return row[x];
}