The series
- ZX-Sprite Introduction (this post)
- ZX-Sprite Prototype - Address decoding
- ZX-Sprite Prototype - Baremetal Pi GPIO
ZX-Sprite: An Introduction
Towards the backend of 2020 I started working on a Spectrum Next game; the game uses a couple of the cool features of Next - tilemaps and sprites. As a bit of fun, I started to adapt my emulator to support both of these; mostly because I’ve got into a bit of a nice workflow with this emulator; one that I can’t quite get from CSpect.
Whilst adding these early Next features, I also decided to add ULAplus support to the emulator to give me 64 colours.
ULAplus is implemented extremely sympathetically to the original hardware:
- requires additional hardware support
- it’s completely optional
- works entirely using I/O requests
As such, it’s a peripheral device - but one that you need to drive a screen with in order to actually use it.
Whilst adding the ULAplus support the idea struck me - wouldn’t it be cool to be able to support the Spectrum Next’s sprites in the way that ULAplus does?
And thus, ZX-Sprite was born.
What is ZX-Sprite?
ZX-Sprite is a fantasy hardware add-on that ports a modified the Spectrum Next Sprite system to the original ZX Spectrum.
As we’re targeting the 48K and 128K ZX Spectrum hardware we have to make some minor adjustments to the Spectrum Next Sprite spec, notably around the colour depth used in the sprites.
DISCLAIMER
This is not endorsed by or associated with the Spectrum Next or any of its creators in any way at all.
Concepts
Like the Spectrum Next, the system revolves around the use of patterns, sprites and in the case of ULAplus - palettes.
A pattern is the image data for a sprite. It can be used by multiple sprites on the screen. A sprite pattern is a 16x16 pixel image that has a 4-bit colour depth. The colour data always refers to a palette entry, meaning all 16 original Spectrum colours are available, or any 16 colours from one of the 4 ULAplus palettes. ZX-Sprite provides support for 64 sprite patterns (although this may increase to 128 as the concept evolves).
A sprite is a collection of attributes laid out sequentially in the hardware’s RAM. Attributes are used to define the sprite; providing its position information and which pattern it is using. The current specification allows for 64 sprites, but this may also change to 128 as the concept evolves.
Sprites are rendered onto the sprite layer. This is currently an overlay to the standard ULA screen that includes the border regions. This means that the sprite layer is theoretically a 320x256 screen, although 320x240 is more common for HDMI displays.
Interface
The interface to ZX-Sprite is very similar to that of the Spectrum Next; we use 3 IO ports to upload data to the internal memory of the hardware.
The first port $303B
is the SELECT
or ‘slot’ port, writing to this port sets the internal SLOT
register which is used by either the pattern or attribute ports.
The second port, $xx5B
is the PATT
port that is used to upload pattern data from the Spectrum to the internal hardware memory. The PATT
port will use the currently selected SLOT
; it then expects 128 bytes of data to be written to complete the full pattern. See the section on
Sprite Patterns for more information.
The third port is $xx57
- the ATTR
port. This is used to define the sprites and change their values at runtime.
Slot/Select
This is used in combination with the pattern/attribute interfaces to select the pattern or attribute to work on.
Bits | Name | Description |
---|---|---|
7 | Control | Enable to select control mode |
6-0 | S6-S0 | Pattern / Sprite number to select |
Control mode is new to ZX-Sprite and aims to provide some of the control functionality that is provided through the use of nextreg
on the Spectrum Next.
When selecting a new slot, the internal data indexes used by sprite patterns or sprite attributes are reset to zero.
Sprite Patterns
Sprite patterns are a 16x16 pixel image with a 4-bit colour depth. The data is arranged top-to-bottom, left-to-right as 16 rows of 8 bytes; each byte contains data for two pixels. This makes the size of each sprite 128 bytes
.
The value of each nibble is an index into the palette used by the sprite; thus 16 colours per sprite are possible. For the standard ULA palette, the normal colours are mapped as 0-7
and the bright colours are mapped 8-15
.
An example of a pattern:
.db $01,$19,$2A,$3B,$4C,$5D,$6E,$7F
.db $01,$19,$2A,$3B,$4C,$5D,$6E,$7F
.db $01,$19,$2A,$3B,$4C,$5D,$6E,$7F
.db $01,$19,$2A,$3B,$4C,$5D,$6E,$7F
.db $01,$19,$2A,$3B,$4C,$5D,$6E,$7F
.db $01,$19,$2A,$3B,$4C,$5D,$6E,$7F
.db $01,$19,$2A,$3B,$4C,$5D,$6E,$7F
.db $01,$19,$2A,$3B,$4C,$5D,$6E,$7F
.db $01,$19,$2A,$3B,$4C,$5D,$6E,$7F
.db $01,$19,$2A,$3B,$4C,$5D,$6E,$7F
.db $01,$19,$2A,$3B,$4C,$5D,$6E,$7F
.db $01,$19,$2A,$3B,$4C,$5D,$6E,$7F
.db $01,$19,$2A,$3B,$4C,$5D,$6E,$7F
.db $01,$19,$2A,$3B,$4C,$5D,$6E,$7F
.db $01,$19,$2A,$3B,$4C,$5D,$6E,$7F
.db $01,$19,$2A,$3B,$4C,$5D,$6E,$7F
.db $01,$19,$2A,$3B,$4C,$5D,$6E,$7F
Using the standard ULA colour palette this will produce a sprite with a colour gradient through each of the colours (including bright).
ZX-Sprite has an internal data index register that is incremented for each byte uploaded for a pattern. This register has the following rules:
- reset to 0 when a new slot is selected
- reset to 0 when the 128th byte of a pattern is written
When the data register rolls over to zero, it also increments the slot register, allowing for multiple sprite patterns to be uploaded with successive port writes.
The ULA colours are kinda ugly and won’t make for attractive sprites; so ZX-Sprite supports ULAplus palettes through use of the palette select bits in the attributes.
Sprites on the Spectrum Next can be defined with an 8-bit colour index, thus giving 256 possible colours. This makes little sense on the original Spectrum, so ZX-Sprite has no plans to include this.
Sprite Attributes
Sprites are defined as a set of attributes. At the time of writing, ZX-Sprite supports 4 attributes, but there are plans to extend this to include the optional 5th attribute similarly to the Spectrum Next. Each attribute is a single byte, but depending on its position it may contain bitpacked data.
The current attributes are as follows:
Position | Pattern | Description |
---|---|---|
0 | X7 X6 X5 X4 X3 X2 X1 X0 |
The lower 8 bits of the X position |
1 | Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 |
The Y position |
2 | -- -- P1 P0 HF VF RR X8 |
The palette and flipping attributes. |
3 | VV -- N5 N4 N3 N2 N1 N0 |
The visibility and pattern attribute |
ZX-Sprite has an internal data index register that is incremented for each attribute uploaded. This register has the following rules:
- reset to 0 when a new slot is selected
- reset to 0 when the 4th byte of an attribute is written
When the data register rolls over to zero, it also increments the slot register, allowing for multiple sprites to be modified with successive port writes.
It is likely that the 5th attribute that the Spectrum Next supports will be added to the system, allowing for relative sprites to be used.
Attribute 2
Sprite attribute #2 contains information about the palette index and flip/rotation behaviour of the sprite.
Bits | Name | Description |
---|---|---|
7-6 | – | Reserved, set to 0 |
5-4 | P1-P0 | Palette index for the ULAplus CLUT. Set to 0 if not using ULAplus. |
3 | HF | Flip the sprite horizontally |
2 | VF | Flip the sprite vertically |
1 | RR | Rotate the sprite clockwise 90 degrees |
0 | X8 | The high bit of the 9-bit X value. |
There are differences from the Spectrum Next attribute 2 here; notably bits 7 and 6 aren’t used. This is because only 4 palettes are possible when using ULAplus.
Attribute 3
Sprite attribute #3 controls the visibility of the sprite as well as the pattern it is using.
Bits | Name | Description |
---|---|---|
7 | VV | Visible - set to 1 to show the sprite |
6 | – | Reserved, set to 0 |
5-0 | N5-N0 | 6 bit sprite pattern to use |
The main difference between this attribute and the Spectrum Next is that we are currently limiting the attribute count to 4. If this changes then bit 6 will be used to enable the extended attribute.
Control Mode
The Spectrum Next has a host of nextreg
registers to control its sprite system. ZX-Sprite achieves this by allowing the SELECT
port to be put into control mode.
When enabled, Control Mode allows the use of the ATTR
port interface to upload control attributes.
Control Slot | ATTR value | Description |
---|---|---|
$80 |
E-----TB |
Global feature control |
$81 |
----PPPP |
Transparency index; the next 4-bit value written to the ATTR port will be the palette index of the transparent colour of all sprites. |
To exit control mode, a second SELECT
command must be issued without the control bit set.
Note; there are plans to add clipping and offset windows to the global control register in the future.
Global Features
The value $80
written to the SELECT
port is used to control the global features of ZX-Sprite.
The next ATTR
written after this is selected will control the features enabled.
Bits | Name | Description |
---|---|---|
7 | E | Enable ZX-Sprite |
6-2 | – | Reserved, set to 0 |
1 | T | Enable global transparency |
0 | B | Enable draw in border mode; this changes the co-ordinate system origin to begin at 0,0 of the full screen and plots sprites into the borders. When disabled, the co-ordinate system used is 0,0 of the ULA screen and the borders are not drawn in. |
The default values after reset are that all features are disabled.
Transparency Index
ZX-Sprite supports a global transparency index; meaning the palette entry matching the index will be rendered as transparent (when the global transparency feature is enabled).
This is controlled by writing the value $81
to the SELECT
port. The next attribute written controls the palette index to use. This is index 11 by default, meaning the ULA colour of BRIGHT MAGENTA is rendered transparent.
Status
Many of the features listed above are working in my own personal ZX Spectrum emulator neccy
. This hasn’t been released to the public.
As I said at the start of this post, ZX-Sprite is fantasy hardware. It doesn’t exist for real - well, kinda.
In order for ZX-Sprite to exist for real, it needs two prerequisite hardware features - the ability to show the normal ULA screen with the sprites overlayed and ULAplus palette support.
I’ve been experimenting with Raspberry Pis, CPLDs and breadboards attached to the Spectrum edge connector to achieve a partially working real-world hardware system (albeit slow).
I’ll talk about that in my next post.