Putting code into the PROGx banks

To get more room for code, you can create a number of extra “PROG” banks as declared in project.json. Once you've created those banks, you'll need to mark your functions and variables to indicate when they live in a non-default bank.

Functions and const variables have different markers in the form of Compiler Pragmas that should be wrapped around their definitions. See the CC65 documentation for full details, but the important parts will be explained here as well.

First, an example:

#pragma rodata-name (push, "PROG0")
 
const char longWindedDialogueText[] = "According to all known laws of aviation, there is no way a bee shou[...]";
 
#pragma rodata-name (pop)
 
#pragma code-name (push, "PROG0")
 
void mySprawlingPlayerUpdateFunction() {
 // absolutely massive switch block
 // good lord so many if-statements
 // did you really need to fully implement A-star
}
 
#pragma code-name (pop)

Here we can see that rodata-name is used for const variables, and code-name is used for actual executable code. These pragmas also take “push” and “pop” parameters so you can have things in the same file assigned to different banks.

Now, if you simply call this function or access that data without any preparation you're gonna encounter unexpected behavior or crash.

To correctly access this data you'll need to use the Bank Switching API.

#include "gt/banking.h" //Banking API
#include "gen/bank_nums.h" //Generated macros for named bank numbers
 
//no pragmas around this, it should live in the default code bank
void mySprawlingPlayerUpdateFunction_wrapper() {
 
    push_rom_bank();
 
    change_rom_bank(BANK_PROG0);
 
    mySprawlingPlayerUpdateFunction();
 
    pop_rom_bank();
}

A warning

You really don't want to call change_rom_bank or pop_rom_bank directly from code that lives in an alternate bank, since it'll disappear from under you as soon as either of those functions return. You can set up wrapper functions, also know as “trampolines” that live in the default code bank. Or just structure your code so that all your bank changes are called from the default code bank without explicitly setting up wrappers.