Web64 logo Web64 Documentation Web64 Native Cartridge Runtime

Web64 Native Cartridge Runtime

Version: 2026-09-28

The native cartridge runtime lets a RAM-resident Web64 C or assembly program read immutable ROM data through Standard 8K, Standard 16K, Magic Desk, and EasyFlash CRT layouts. The recommended managed resources layer names logical project resources; the expert layer selects a physical profile, bank, and ROM window. Both use native 6510 routines linked into the built program. A project is mastered and run entirely in Web64.

Start with Managed Cartridge Resources in the template browser, or open the standalone example. Its main.c and palette-probe.asm call the same logical API, and its four saved layouts boot real CRTs. Select a layout in Disk/Media → Cartridge Layouts, then use Run Cartridge (Ctrl+Alt+F5). Starting the resident target as a PRG cannot supply its ROM mapping or generated directory.

Authoring and ownership

Create a native Cartridge Layout and select its profile. With Managed resources, bind a resident Build Target, declare a directory address in ordinary RAM, and declare writable transfer ranges. Add resources from project files or target outputs. The layout owns physical placements, fill, alignment, and the immutable CRT. The application owns its buffers, display setup, interrupt policy, and any decompression after loading a packed stream.

Build Cartridge generates read-only cartridge/generated.h and cartridge/generated.inc for the bound target. They define logical IDs and stored sizes and describe the current directory location. Do not copy generated numeric IDs into saved game state or edit generated includes. Rebuilding after a layout or source change regenerates the directory and the CRT. The final artifact manifest records source-to-ROM extents; the IDE's Resource map shows the stored size and extent count.

ProfilePhysical resource spaceBoot behavior
Standard 8KOne fixed ROML windowBank-0 ROML boot.
Standard 16KFixed ROML and ROMHBank-0 ROML boot.
Magic DeskUp to 64 ROML banksBank-0 ROML boot, RAM-resident bank service.
EasyFlashUp to 64 shared-bank ROML/ROMH pairsBank-0 ROMH reset trampoline with pinned ROML resident payload.

Capacity includes boot, application, directory, other placements, alignment gaps, and fill. A resource that does not fit fails mastering. A logical resource may span physical extents of at most 8 KiB. A packed-stream resource reports its stored bytes; copy those bytes to writable RAM and call a separately chosen unpack service.

C API

Include <web64/cartridge.h> and, for managed IDs, <cartridge/generated.h>. These packed structs use little-endian 16/32-bit fields:

typedef struct {
    uint8_t resource;       /* 0 */
    uint32_t offset;        /* 1–4: logical byte offset */
    uint8_t *destination;   /* 5–6: writable RAM */
    uint16_t length;        /* 7–8 */
} web64_cart_resource_request;
typedef struct {
    uint16_t completed;     /* 0–1 */
} web64_cart_resource_result;

Call web64_cart_resources_init() once after the real cartridge boot and before managed reads. It copies and validates the generated directory at the declared RAM address. web64_cart_resource_read(&request, &result) transfers at most 256 bytes. web64_cart_resource_copy(&request, &result) accepts a 16-bit length and internally performs bounded chunks across logical extents. Each returns a status byte; result.completed reports bytes copied. A zero-length range at or before the end succeeds without changing the destination. An invalid range fails rather than truncating. A later chunk failure reports the copied prefix; it does not roll back prior bytes.

#include <web64/cartridge.h>
#include <cartridge/generated.h>

web64_cart_resource_request request;
web64_cart_resource_result result;

void load_palette(void) {
    uint8_t status = web64_cart_resources_init();
    request.resource = WEB64_CART_RESOURCE_PALETTE;
    request.offset = 0UL;
    request.destination = (uint8_t *)0x5000;
    request.length = 4;
    if (status == WEB64_CART_OK)
        status = web64_cart_resource_read(&request, &result);
    if (status == WEB64_CART_OK && result.completed == 4) {
        /* $5000–$5003 now contain the stored palette bytes. */
    }
}

The low-level physical API is for applications that deliberately own banking. web64_cart_select_bank(profile, bank) changes the selected bank, and web64_cart_set_mode(profile, mode) changes the supported mapping mode. web64_cart_read(const web64_cart_transfer *) and web64_cart_copy(const web64_cart_transfer *) use a nine-byte request: profile at 0, bank at 1, ROML/ROMH window at 2, 16-bit offset at 3–4, destination at 5–6, length at 7–8. A physical read/copy stays within one 8 KiB window and transfers at most WEB64_CART_MAX_TRANSFER (256) bytes into $0200–$7FFF. Longer or cross-window operations must be divided by the caller, or expressed as a managed logical resource. EasyFlash mapping must be explicitly valid for physical read/copy. Code that disables its own execution window must execute from RAM.

Constant groupValues
ProfilesWEB64_CART_PROFILE_STANDARD_8K 0, STANDARD_16K 1, MAGIC_DESK 2, EASYFLASH 3.
WindowsWEB64_CART_ROML 0, WEB64_CART_ROMH 1.
EasyFlash modesWEB64_CART_MODE_BOOT 0, OFF 4, ULTIMAX 5, 8K 6, 16K 7.

Assembly include and call macros

Include web64/cartridge.inc and, for managed IDs, cartridge/generated.inc. The SDK include supplies profile/status constants, packed-structure offsets, and web64_rt_prepare_* / web64_rt_call_* macros for all seven public operations. A prepare macro writes the exact-width named argument window; a call macro prepares it and executes JSR to the unchanged _web64_cart_* entry. Macro arguments are immediate assembly expressions: use the emitted label, such as _request, for C-owned globals. The macros allocate no storage. Including the file without calling an entry links no runtime service.

The standalone example's live ASM path is equivalent to:

.include "web64/cartridge.inc"
.include "cartridge/generated.inc"

asm_palette_probe:
    web64_rt_call_cart_resource_read asm_request, asm_result
    rts                         ; A holds WEB64_CART_* status

asm_request:
    .byte WEB64_CART_RESOURCE_PALETTE, 0, 0, 0, 0
    .word $5004                 ; second declared transfer buffer
    .word 4
asm_result:
    .word 0                     ; completed byte count

Initialization is web64_rt_call_cart_resources_init with no arguments. Long logical reads use web64_rt_call_cart_resource_copy request, result. The expert forms are web64_rt_call_cart_select_bank profile, bank, web64_rt_call_cart_set_mode profile, mode, web64_rt_call_cart_read request, and web64_rt_call_cart_copy request. Their corresponding web64_rt_prepare_* forms only fill the ABI windows. The public entries return status in A and may change A/X/Y and flags. The exact printable C header and ASM include list every symbol.

Status and safety

StatusValueMeaning
WEB64_CART_OK0Complete operation.
WEB64_CART_PROFILE1Profile mismatch or unsupported profile.
WEB64_CART_BANK2Invalid bank.
WEB64_CART_MODE3Invalid or unsafe mode for the requested operation.
WEB64_CART_RANGE4Logical/physical source range or bounded count is invalid.
WEB64_CART_DESTINATION5Destination or request/result storage violates declared RAM ownership.
WEB64_CART_BUSY6Prior transfer is active; ISR calls are unsupported.
WEB64_CART_NOT_INITIALIZED7Managed directory was not initialized.
WEB64_CART_RESOURCE8Unknown logical resource ID.
WEB64_CART_DIRECTORY9Invalid directory or extent.
WEB64_CART_UNSAFE10Rejected unsafe mapping state.

The resident service runs from RAM, masks IRQ only around each bounded map/copy/restore interval, restores the caller's prior IRQ state and mapping, and uses $FB–$FE as documented call scratch. It is mainline-only and non-reentrant. IRQ/NMI code must be bank-invariant during a transfer, preserve shared scratch, and never call this API. A 256-byte transfer can still exceed a raster deadline; choose a smaller read if timing matters. A managed destination must fit entirely in one declared writable range and must not overlap resident code, directory, zero page, stack, or live request/result storage. Build Cartridge checks declared ownership; guest calls check the actual request.

Raw writes to $DE00, $DE02, or CPU mapping registers bypass managed shadow state and are outside the managed guarantee. This runtime does not bank executable functions automatically, retain pointers into switched ROM, write EasyFlash storage, or turn CRT data into mutable disk files. Cartridge autostart may precede KERNAL display initialization: a program that writes Screen RAM should also establish VIC screen, charset, bank, mode, and Color RAM, as the example does.