Memory and layouts
This page shows where the code, data and stacks of a program are in the 64 KiB of the //e. It tells how to select and change a linker layout, and how to read the memory report of the build. It also gives the cost of each part of the library.
Main memory
| Range | Used by |
|---|---|
| $0000-$007F | OS and firmware zero page |
| $0080-$0099 | cc65 zero page. Startup saves it at entry and restores it before QUIT. |
| $009A-$00FF | OS and firmware zero page |
| $0100-$013F | The ProDOS part of the hardware stack page. libtini does not change it. |
| $0140-$017F | The bank-transfer trampoline. Startup saves the original bytes and restores them at exit. |
| $0180-$01FF | Hardware stack: 128 bytes, with ProDOS interrupt dispatch |
| $0200-$03FF | OS workspace and vectors. libtini does not change them. |
| $0400-$07FF | Text page 1. Fatal messages use it. |
| $0800-$1FFF | Not used by cfg/libtini.cfg. Constants and low code in the full-game layout. |
| $2000-$BEFF | The program, its stacks, the near arena and the file buffer. See the layouts below. |
| $BF00-$BFFF | ProDOS global page and MLI entry. libtini does not change them. |
| $C000-$CFFF | I/O and slot ROM. libtini does not change slot assignments. |
| $D000-$FFFF | ProDOS in the language card, and ROM. libtini does not change them and has no language-card code. |
The two shared layouts
The two layouts make a raw ProDOS SYS file that ProDOS loads and starts at $2000. They link with the cc65 none.lib and the startup code of the project. A manifest selects one layout with config.
cfg/libtini.cfg (default) | cfg/game.cfg | |
|---|---|---|
| Used by | The diagnostic, tests, the starter | Larger games |
| Code, data, BSS | MAIN $2000-$9FFF, 32 KiB | IMAGE from $2000; BSS in WORK, up to $AEFF |
| Constants and low code | With the code | RODATA, AMEMCODE and LOWCODE run in LOW, $0800-$1FFF |
| C stack | $A000-$A7FF | $B700-$BEFF |
| Transfer staging | $A800-$A87F | $B400-$B47F |
Near arena, NEARBSS | $A900-$AEFF, 1,536 bytes | $B480-$B6FF, 640 bytes |
| Guard | $AF00-$AFFF | $AF00-$AFFF |
ProDOS file buffer, _ti_file_buffer | $B000-$B3FF | $B000-$B3FF |
In cfg/game.cfg, startup copies the low segments one time from the end of the loaded image into $0800-$1FFF. It does this before it clears BSS. The code that runs one time at entry is in the ONCE segment. This layout puts ONCE at the addresses of BSS, so the clear of BSS makes these bytes available again. For the near-pointer checks, low memory is read-only.
A layout of your own
A game can link with its own copy of a layout file. Then it can use the memory that the library does not need. STARFALL makes no file calls. Thus examples/starfall/starfall.cfg does not define _ti_file_buffer.
That layout sets the end of WORK and of writable near memory, _ti_memory_end, to $B400. In the shared layouts this end is $AF00. Thus the guard page and the buffer give 1,280 more bytes of game memory. In that game, a file call does not link, so ProDOS cannot write over its variables.
Keep the checks of the build. The build rejects each segment outside the areas that the symbols of a layout give.
Read the memory report
starfall memory: LOW $0800-$1FFF 88 free; IMAGE $2000-$BEFF 1032 free; WORK $A26C-$B3FF 1110 free; NEAR $B480-$B6FF 1 free
starter memory: MAIN $2000-$9FFF 12321 free; NEAR $A900-$AEFF 1532 free
Each entry is a linker memory area with its free bytes. build/memory.json gives the same values and the used bytes for each program. When a segment does not fit, the build stops and gives the missing bytes:
starfall does not fit: segment BSS needs 50 more bytes in memory area WORK
IMAGE is the load image. It also holds the low segments until startup copies them. Thus its free bytes show how much more the SYS file can hold. WORK holds BSS while the program runs.
The near arena
Variables in the NEARBSS segment are in the near arena of the layout, outside the main area of the program. Startup sets them to zero, as for BSS. The linker rejects more data than the arena holds. All near API calls accept pointers into the arena. The library puts no data there.
#pragma bss-name (push, "NEARBSS")
static uint8_t level[512];
static uint16_t palette[16];
#pragma bss-name (pop)
STARFALL keeps its 547-byte simulation state, star columns, animated palette and two text buffers there. They use 639 of the 640 bytes.
Where code and data go
cc65 puts code in CODE, constants in RODATA, initialized variables in DATA and the other variables in BSS. Pragmas move a part of a file to a different segment:
/* Code that runs rarely, into low memory, in the full-game layout. */
#pragma code-name (push, "LOWCODE")
static void title_option(void) { /* ... */ }
#pragma code-name (pop)
/* Keep the constants of this file in main memory, where space is available. */
#pragma rodata-name (push, "CODE")
static const uint8_t table[64] = { /* ... */ };
#pragma rodata-name (pop)
Do not put code that runs after startup in ONCE. Use OSSTATE only for the startup code.
Auxiliary memory and banks
| Range | Used by |
|---|---|
| Selector 0, all | The OS and the display. It is never a resource bank. |
| Selector 0, $2000-$9FFF | SHR pixels, scan-line bytes and palettes. ti_gfx_begin saves all 32,768 bytes. |
| Selectors 1-126, $0000-$01FF | Reserved. These are never resource bytes. |
| Granted selectors, $0200-$BFFF | 48,640 resource bytes for each bank |
| All selectors, $C000-$FFFF | I/O, ROM and language card. Not used. |
A resource continues from $BFFF of one granted bank to $0200 of the next bank. While auxiliary reads are mapped, only the page-one trampoline runs. Main memory and selector 0 come back before C code or interrupts continue. A grant of all selectors 1-126 gives 6,128,640 resource bytes.
The cost of each part of the library
A program links an archive member only when it uses one of its symbols. These sizes come from the CPU-only build. For your program, read build/<game>.map.
| Archive members | Linked by | Code and constants | State |
|---|---|---|---|
| memory, target_memory, target_hardware | All resource and graphics calls | 3,977 | 261 |
| memory_near | ti_near_move, ti_near_fill | 271 | 0 |
| memory_copy | ti_res_copy | 434 | 0 |
| error | ti_error_* | 30 | 1 |
| timing | ti_time_begin, ti_time_poll | 316 | 11 |
| timing_steps | ti_steps_* | 336 | 13 |
| graphics, target_surface | Surfaces, clear, rectangles | 1,769 | 176 |
| graphics_video, target_publish | The video session and ti_gfx_present | 1,315 | 11 |
| graphics_text, target_text, font | ti_gfx_text, ti_gfx_text_opaque | 1,711 | 44 |
| target_digits | ti_digits | 150 | 12 |
| graphics_rows, target_rows | ti_gfx_present_rows | 795 | 9 |
| graphics_dot | Lines and bitmap fonts | 595 | 136 |
| graphics_line | ti_gfx_line | 546 | 0 |
| graphics_font | ti_gfx_text_font | 925 | 0 |
| graphics_blit | Blits, sprites, maps, animation frames | 3,631 | 288 |
| scene, target_scene | Sprite scenes, without TI_SCENE_STORAGE | 2,042 | 197 |
| audio, target_audio | The audio driver and speech | 5,075 | 331 |
| audio_music | ti_music_start | 632 | 7 |
| target_song | ti_song_start | 1,120 | 195 |
| audio_envelope | ti_audio_envelope* | 713 | 0 |
| audio_numbers | ti_speech_number | 592 | 0 |
| input | Keyboard and buttons | 269 | 11 |
| input_joystick | ti_joystick_read | 1,146 | 35 |
| input_stick | ti_stick_read | 476 | 20 |
| storage, target_storage | File calls and packs | 2,752 | 236 |
The startup code adds 563 bytes. This includes 228 bytes of entry code, which the full-game layout uses again for BSS, and 98 bytes of saved OS state. The cc65 runtime routines that the program uses are added to this.
Save memory and time with cc65
- Foreground code that does not call itself can use
--static-localsin thecflagsof the manifest, as STARFALL does. This removes most C stack operations. - Keep pools as parallel byte arrays (
enemy_x[i],enemy_y[i]), not as arrays of structures. The 65C02 indexes each array with one absolute,Y operand. - Do not use 32-bit arithmetic in loops. For this reason, the library uses 16-bit arithmetic in its checks.
- The library gives memory a higher priority than speed, because each game pays for its bytes. A game can use its own memory to get more speed.