Custom Character Set
Design and load custom characters for improved visual quality and a more polished game appearance.
Phase 2 begins! The game is playable—now we make it polished. Custom characters are the foundation of distinctive Spectrum graphics.
Run It
Assemble and run:
pasmonext --sna inkwar.asm inkwar.sna

Version shows “PHASE 2 V0.1”.
Why Custom Characters?
The ROM character set is functional but generic. Every Spectrum program that uses it looks the same. Custom characters let you:
| Benefit | Example |
|---|---|
| Brand identity | Distinctive logo and UI |
| Better borders | Clean lines instead of blocky defaults |
| Special symbols | Game-specific icons |
| Visual polish | Professional appearance |
The Spectrum Character System
Characters are 8×8 pixel bitmaps stored sequentially:
Character 'A' (8 bytes):
Byte 0: %00111100 → ****
Byte 1: %01000010 → * *
Byte 2: %01000010 → * *
Byte 3: %01111110 → ******
Byte 4: %01000010 → * *
Byte 5: %01000010 → * *
Byte 6: %01000010 → * *
Byte 7: %00000000 →
The ROM charset lives at $3D00 and contains 96 printable characters (space through to ©).
Installing a Custom Charset
The approach:
- Reserve RAM for 768 bytes (96 chars × 8 bytes)
- Copy ROM charset to RAM at startup
- Modify specific characters as needed
- Point print routine at custom charset
; Reserve space for custom charset
custom_charset: defs 768, 0
install_charset:
; Copy ROM charset to our RAM area
ld hl, $3D00 ; ROM charset (space onwards)
ld de, custom_charset
ld bc, 768 ; 96 characters * 8 bytes
ldir
ret
Updated Print Routine
Our print routine now reads from the custom charset instead of ROM:
print_char:
; Calculate character data address
sub 32 ; ASCII to charset offset
ld l, a
ld h, 0
add hl, hl
add hl, hl
add hl, hl ; HL = (char-32) * 8
ld de, custom_charset
add hl, de ; HL = source address
; ... rest of display file calculation unchanged
The key change is using custom_charset instead of the ROM address.
Designing Custom Characters
For borders, consider:
Corner (top-left): Horizontal: Vertical:
%11111111 ████████ %00000000 %11000000 ██
%11000000 ██ %00000000 %11000000 ██
%11000000 ██ %11111111 ████████ %11000000 ██
%11000000 ██ %11111111 ████████ %11000000 ██
%11000000 ██ %11111111 ████████ %11000000 ██
%11000000 ██ %11111111 ████████ %11000000 ██
%11000000 ██ %00000000 %11000000 ██
%11000000 ██ %00000000 %11000000 ██
These create cleaner, more professional-looking borders than using attribute-only blocks.
Memory Considerations
The charset uses 768 bytes of RAM. On a 48K Spectrum with 32K available (above the ROM), this is acceptable. Position it carefully:
$8000 (32768) - Program start
...
custom_charset - 768 bytes for characters
board_state - 64 bytes for game state
variables - Game variables
The Complete Code
This code sample could not be loaded. The file may be missing or the path may be incorrect.
What You’ve Learnt
- Character memory - 8×8 bitmaps, 8 bytes per character
- Custom charset installation - Copy ROM to RAM, modify as needed
- Print routine update - Point at RAM charset instead of ROM
- Visual identity - Foundation for distinctive graphics
What’s Next
Unit 18 animates the cursor with colour cycling—making it easier to see and more visually engaging.