What format is the (Timex) Sinclair ZX Spectrum SCREEN$/.SCR file and how is the fore/background and attribute data encoded within this format?
For a standard screen, compatible with ZX Spectrum, a SCREEN$ file is 6912 bytes. It's just a dump of the screen memory.
The first 6144 bytes store the screen bitmap: 256x192 pixels, 1 bit per pixel (on or off). The layout is not linear.
- The screen bitmap is divided horizontally into three thirds: each one is 2048 bytes and store 8 text rows of 32 column each one (each character cell has 8 scans of 8 pixels each one).
- For each third, the first scan of each character cell is stored, left to right, top to bottom. Then, the second scan, then the third, until the last one, the eighth. After this, a new third is stored in the same fashion.
- Each byte stored has 8 pixels: the MSb belongs to the leftmost pixel as shown on the screen.
Following the bitmap region, the remaining 768 bytes store the attributes. There is 1 byte of attribute for each character cell. Stored left to right, top to bottom. Bits 2-0 store the foreground colour, or "ink" colour (colour assigned to "on" pixels). Bits 5-3 store the background colour or "paper" colour (colour assigned to "off" pixels). Bit 6 is bright. If set, both paper and ink colours are lighter. Bit 7 is flash. If set, the paper and ink colour swap every 640 ms to give a kind of flashing character.
To have a clue of how bitmap and attribute are stored, you can type this little BASIC program that will shows you the arrangement of pixels on the screen, by dumping part of the ROM to the screen (which will show up as random pixels and colours):
10 FOR n=0 TO 6911: POKE 16384+n,PEEK n: NEXT n
A similar program gives this result:
For each paper or ink colour, the arrangement of the three bits encode a RGB value in this order: G R B. So, colour 6 (binary 110) is green + red = yellow. The complete table is this: 0: black, 1: blue, 2: red, 3:magenta, 4: green, 5:cyan, 6:yellow, 7: white
With bright set, all these colours except black are intensified. This gives a total of 15 different colours.
The Timex 2048/2068 computers add two more screen modes, called HiColour and HiRes.
HiColour mode is entered by setting bit 1 of port $FF. It differs a little from the standard mode, explained above. HiColour mode uses an attribute region which is not 768 bytes, but 6144 bytes in size, the same as the bitmap region. The attribute region does not start following the bitmap region, but there is a gap of 1280 bytes between the end of the bitmap and the start of the attribute region. The total screen size is 12288 bytes, not including this gap.
The layout of this attribute region is the same as the bitmap region (3 thirds, the first scan is stored, then the second, etc). There is now 1 byte of attribute for each scan of 8 pixels. The format of the attribute byte is the same as in the standard mode.
On tape, some programs use two different blocks to load the bitmap region, then the attribute region (as they are not contiguous). Some others use a single block containing both regions along with 1280 byte gap between them.
The following program will show you the arrangement in this mode. It won't work on a regular Sinclair Spectrum, but in a Timex TC2048/2068.
10 OUT 255,2: FOR n=0 TO 6143: POKE 16384+n,PEEK n:POKE 24576+n,PEEK n: NEXT n
HiRes is entered by setting bit 3 of port $FF. This mode shows a screen of 512x192 pixels, using two different bitmap regions. One region is the same as in the standard and HiColour mode, and the second one is located in the same place as the attribute region of the HiColour mode.
For each scan of 16 pixels, the left most 8 pixels are stored in the first bitmap region, and the rightmost 8 pixels are stored in the second bitmap region.
Each bitmap region is arranged the same way as the other screen modes.
The foreground, background and border colour is set separately on bits 5-3 of port $FF. These three bits store the ink colour which will be applied to all "on" pixels. The 1-complement of this value will be used for both paper and border colours. In HiRes mode, the bright bit is always set and there is no flash.
The following program will show you how the HiRes mode is arranged:
10 OUT 255,4: FOR n=0 TO 6143: POKE 16384+n,PEEK n:POKE 24576+n,PEEK n: NEXT n
On tape, HiRes SCREEN$ are stored the same way as HiColour SCREEN$. I haven't seen that the global ink colour is stored too, so a separate BASIC program must provide it, along with the mode change command.
The SCR file format is effectively a raw data dump of the video memory area on the standard ZX Spectrum 48/128k.
As such, the data is divided into three 2,048 byte sections, each of which describes the pixel data for a third of the screen, from top to bottom. This is then followed by 768 bytes of attribute data information - resulting in a total of 6,912 bytes.
For those unfamiliar with the Spectrum's video architecture, on the standard Spectrums, the 256x192 display is treated as 768 8x8 pixel blocks, with each block being capable of displaying two colours. (i.e.: Each block is effectively a bitmap and associated ink and background colour combination.) By using this approach, it's possible to display the fill 256x192 resolution of screen using only 6,912 bytes.
Additionally, the Spectrum palette consists of both normal and "bright" colours, with each block capable of being set as bright. (i.e.: You can't mix normal and bright colours within a single 8x8 pixel block.) The blocks can also be set to flash, but perhaps the less said about that the better.
In more detail:
Each of the screen thirds describes a slice of the screen - the first third being horizontal lines 0 thru 63, the second being 64 thru 127 and finally 128 thru 192, hence describing the 192 horizontal lines of the Spectrum's 256x192 display.
However, rather than simply describe each row in a linear fashion (i.e.: 0 thru 63, then 64 thru 127, etc.), due to the nature of the Spectrum, the data within each third instead describes a full horizontal line across each of the 8x8 blocks in turn (i.e: horizontal line 0, 8, 16, etc.) before moving onto the next line in the row of blocks (i.e.: line 1, 9, etc.)
This will make way more sense if you've ever seen a ZX Spectrum loading screen data.
In more detail, each of the 64 lines within each third is structured into a series of 32 byte blocks as follows, each block being a line of screen data.
0 .. 31 - 256 bits of data for horizontal line 0 32 .. 63 - 256 bits of data for horizontal line 8 64 .. 95 - 256 bits of data for horizontal line 16 96 .. 127 - 256 bits of data for horizontal line 24 128 .. 159 - 256 bits of data for horizontal line 32 160 .. 191 - 256 bits of data for horizontal line 40 192 .. 223 - 256 bits of data for horizontal line 48 224 .. 255 - 256 bits of data for horizontal line 56
This is then followed by...
0 .. 31 - 256 bits of data for horizontal line 1 32 .. 63 - 256 bits of data for horizontal line 9 64 .. 95 - 256 bits of data for horizontal line 17 96 .. 127 - 256 bits of data for horizontal line 25 128 .. 159 - 256 bits of data for horizontal line 33 160 .. 191 - 256 bits of data for horizontal line 41 192 .. 223 - 256 bits of data for horizontal line 49 224 .. 255 - 256 bits of data for horizontal line 57
Once each of the thirds has been completed, this is followed by the attribute data, which is simply another array of bytes detailing the ink and paper colours as well as whether or not bright or flash is set.
Each of these bytes is constructed as follows:
0,1,2 - Ink colour 3,4,5 - Paper colour 6 - Whether or not the colours are bright or standard 7 - Whether or not the block should flash
For more information see Claus Jahn's ZX Spectrum page
A couple of things seem a little odd about the way the Spectrum display memory was arranged until you understand why it was done the way it was. The first thing is the odd division into three blocks -- this was done so that if you have a pointer to a scan line of a character block stored in a register pair (e.g. HL) you can just increment the high order byte (e.g. with an inc h instruction) to get a pointer to the next scan line of the same character block. In order for this to work, there must be exactly 256 character blocks between successive scan lines so the bitmap must be divided into groups of 256/32 = 8 rows. This simplifies (and accelerates) the code for printing a character substantially.
The other odd thing is something that I've seen commented on less frequently, and this is the unusual order of the colour components in the attributes. AFAICT the reason for this is that the designers wanted a system that would produce reasonable grayscale output if hooked up to a black & white TV. They therefore designed it so that each colour component was also associated with a brightness level, which were picked according to the human eye's sensitive for the colours, i.e. green brightest, then red, then blue. Then, by putting the brightest in the most significant bit and the darkest in the least, they could have the colours appear in numeric order of brightness when viewed on a black and white output.