Memory Management
Making the most of limited RAM
Memory management techniques enabled complex games on hardware with kilobytes of RAM, from bank switching to compression to careful data structure design.
Overview
Early computers and consoles offered kilobytes, not gigabytes, of memory. Developers created sophisticated techniques to fit games into impossibly small spaces. Bank switching accessed more ROM than addressable. Compression squeezed data smaller. Clever data structures represented complex game states in bytes. These constraints bred creativity that modern developers study for optimisation insight.
Fast facts
- C64 RAM: 64 KB (around 38 KB usable for BASIC programs).
- ZX Spectrum: 48 KB.
- NES: 2 KB RAM, variable ROM.
- Modern context: Gigabytes available but efficiency still matters.
Memory maps
Understanding available space:
| System | Total RAM | Available | Notes |
|---|---|---|---|
| C64 | 64 KB | ~38 KB | BASIC, Kernal reduce |
| ZX Spectrum 48K | 48 KB | ~41 KB | System overhead |
| NES | 2 KB | Variable | Mapper ROM expansion |
| Amiga 500 | 512 KB | ~400 KB | OS requirements |
Bank switching
Accessing more than addressable:
- CPU can only address limited memory.
- Switch โbanksโ of ROM/RAM into view.
- NES mappers enable large games.
- C64 could switch out BASIC/Kernal ROMs.
Example:
- NES addresses 32 KB ROM directly.
- MMC3 mapper allows 512 KB ROM.
- Switch banks as needed during play.
Data compression
Shrinking content:
| Technique | Application |
|---|---|
| Run-length encoding | Repeated patterns |
| Huffman coding | Variable-length symbols |
| LZ variants | Repeated sequences |
| Delta encoding | Similar sequential data |
| Custom formats | Game-specific optimisation |
Tile-based graphics
Memory-efficient visuals:
- Define tiles once (8x8 or 16x16 pixels).
- Reference tiles by index.
- Screen map uses one byte per tile.
- 256 tiles cover most needs.
Example: 20x12 tile screen = 240 bytes (map) + 4 KB (tile definitions).
Sprite management
Limiting active objects:
- Hardware sprites limited (8 per line on NES).
- Multiplexing shares sprites across objects.
- Object pooling reuses memory.
- Priority systems manage overflow.
Zero page on 6502
Fast memory access:
- First 256 bytes of memory.
- Faster addressing modes.
- Store frequently used variables.
- Precious resource, allocate carefully.
Self-modifying code
Code as data:
- Change instructions during runtime.
- Avoid repeated calculations.
- Table lookups via modified addresses.
- Saves memory at complexity cost.
Example: unrolled loops with modified addresses.
Streaming and paging
Loading content dynamically:
- Disk-based games load sections.
- Earlier areas unloaded for new content.
- Cassette games more constrained.
- Level streaming in 3D games today.
Stack management
Limited stack space:
- Call depth limited.
- Local variables consume stack.
- Recursion dangerous.
- Global variables often necessary.
Memory allocation strategies
| Strategy | Trade-off |
|---|---|
| Static allocation | Predictable but inflexible |
| Pool allocation | Efficient reuse, fixed types |
| Dynamic allocation | Flexible but fragmentation |
Retro games typically used static allocation.
Amiga chip vs fast RAM
Amiga-specific considerations:
- Chip RAM: Accessible by custom chips (graphics, audio).
- Fast RAM: CPU only, faster access.
- Careful placement of data.
- DMA requires chip RAM.
Modern relevance
Why this matters today:
- Mobile game optimisation.
- Console memory budgets.
- Cache efficiency.
- Embedded systems.
- Understanding fundamentals.
Learning from constraints
Lessons from limited memory:
- Every byte matters.
- Data structure design is crucial.
- Trade computation for storage.
- Know your hardware.
- Constraints breed creativity.