Attributes
The original 48K Spectrum had a character resolution of 32 columns by 24 rows, implemented using a pixel resolution of 256 by 192 pixels. Each pixel could be individually manipulated, this was a major departure from previous ZX computers which (by default) only allowed the screen to be manipulated at the character level.
While visibility could be controlled on a pixel-by-pixel basis, colour was set in 8 by 8 pixel squares at the character level with all pixels in a particular character cell sharing the same foreground and background colours. The nature of the attribute level colour setting is what produces attribute clashes (which we’ll cover later) and what gave many spectrum games their distinctive looks.
The 48K ZX Spectrum memory map is shown below, #xxxx denotes a hexadecimal number, lengths are decimal
Start | End | Length | Description |
---|---|---|---|
#FF58 | #FFFF | 168 | Reserved |
#5CCB | #FF57 | 41,612 | Free memory |
#5CC0 | #5CCA | 11 | Reserved |
#5C00 | #5CBF | 192 | System variables |
#5B00 | #5BFF | 256 | Printer buffer |
#5800 | #5AFF | 768 | Attributes |
#4000 | #57FF | 6,144 | Pixel data |
#0000 | #3FFF | 16,384 | Basic ROM |
The spectrum’s screen memory starts in memory immediately after the spectrum rom, at address #4000 (16384d). Our 256x192 pixels are stored 8 pixels to the byte in 6,144 byes of memory (32 bytes by 192 rows). The colour attributes were stored immediately after the pixel data from address #5800 (22,628) in 768 bytes (32 x 24 character of data).
Addressing attributes
Addressing attributes is as easy as you would expect, starting at #5800 there are 32 attributes per screen row and 24 rows.
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | … | 1F | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
#5800 | ||||||||||||||||||
#5820 | ||||||||||||||||||
#5840 | X | |||||||||||||||||
#5860 | ||||||||||||||||||
… | ||||||||||||||||||
#5EAO |
To set the attributes for character (3, 2) assuming (0,0) is at the top left of the screen you write to address #5800 + ((2*32)+ 3)
or
#5843 (and X marks the spot).
Attribute values
Each block of 8x8 pixels has a single byte of attribute data packed as follows
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
F | B | P2 | P2 | P1 | I0 | I1 | I0 |
- Bit 7 if set indicates the colour flashes between the fore and back colours.
- Bit 6 if set indicates the colours are rendered bright.
- Bits 5 to 3 contain the paper (background) colour 0..7
- Bits 2 to 0 contain the ink (foreground) colour 0..7
The flash attribute alternates a cell between its foreground and background colours on a timer, it is of limited use in games.
The bright attribute makes the foreground and background colours, err…, brighter. The bright attribute nearly doubles the effective number of colours the spectrum could display. I say nearly because bright black is still black.
The colour value 0..7 is the index into the colour table, the colour values at those indexes and their bright equivalents are*:
Decimal | Binary | Colour | Normal | Bright |
---|---|---|---|---|
0 | 000 | Black | ||
1 | 001 | Blue | ||
2 | 010 | Red | ||
3 | 011 | Magenta | ||
4 | 100 | Green | ||
5 | 101 | Cyan | ||
6 | 110 | Yellow | ||
7 | 111 | White |
Putting it all together
Here is an example to how attributes work. I know we’re assembly language gurus here, but well do this with a little speccy basic.
I want to demonstrate attribute values, bright and normal colours and blacks stubborn refusal to change no matter what. We are going to set the background colour of four characters to green, the foreground of the first two to black and the second pair to red. Finally alternate between normal and bright colours between each cell.
This gives us the following four values, note since the paper is bits 5 to 3 we take the value 4 for green (100b) and shift it left 3 places to get 32 (100000b) and set those bits.
Flash | Bright | Paper | Ink | Value | Decimal | Description |
0 | 1 | 100 | 000 | 01100000 | 96 | Bright - black on green |
0 | 0 | 100 | 000 | 00100000 | 32 | Normal - black on green |
0 | 1 | 100 | 010 | 01100010 | 98 | Bright - red on green |
0 | 0 | 100 | 010 | 00100010 | 34 | Normal - red on green |
We print a word at the top left of the screen and then colour it by setting the attributes directly (address of the top left attribute is 22528.)
Which give us:
An a pretty clear demonstration of how attributes work, you can see the green background vary in colour when bright and also that while the black colour remains the same the red foreground colour does change intensity when marked as bright.
Which means there are thirty one possible colours on a spectrum screen fifteen colours that can be normal or bright and black.
Some assembly language constants and code for attributes
The shift operator <<
is used to move a value like red (010) to its paper equivalent pRed (010000). The rest of the constants values should be apparent from the rest of this post.
screen_width_pixels: .equ 256
screen_height_pixels: .equ 192
screen_width_chars: .equ 32
screen_height_chars: .equ 24
screen_start: .equ #4000
screen_size: .equ screen_width_chars * screen_height_pixels
attr_start: .equ #5800
attributes_length: .equ screen_width_chars * screen_height_chars
black: .equ %000000
blue: .equ %000001
red: .equ %000010
magenta: .equ %000011
green: .equ %000100
cyan: .equ %000101
yellow: .equ %000110
white: .equ %000111
pBlack: .equ black << 3
pBlue: .equ blue << 3
pRed: .equ red << 3
pMagenta: .equ magenta << 3
pGreen: .equ green << 3
pCyan: .equ cyan << 3
pYellow: .equ yellow << 3
pWhite: .equ white << 3
bright: .equ %1000000
The cls_attributes
sub-routine is used to set all attributes for the screen to the same value
;
; IN - A contains the attribute value to initialize the screen to
; OUT - Trashes HL, DE, BC
;
cls_attributes:
ld hl, attr_start ; start at attribute start
ld de, attr_start + 1 ; copy to next address in attributes
ld bc, attributes_length - 1 ; 'loop' attribute size minus 1 times
ld (hl), a ; initialize the first attribute
ldir ; fill the attributes
ret
Using the cls_attributes
method is as simple as:
ld a, pBlue | yellow | bright
call cls_attributes
which sets the whole screen to have a blue background and yellow text, both of which are bright.
In part two we’ll address the pixel layout of the screen.
*These colours are approximations based on science, or a close approximation to science. Which is to say guess work :-)