Sprite Programming

This article is intended to be a complete overview of how to design and display sprites on Amiga. It's written for OCS and for upwards compatibility with ECS and AGA (which offer border control, subpixel precision, and wider sprites).

What are Amiga sprites?

Up to 8 overlays that can each be 16 pixels wide and as tall as the display window and moved independently.

Bomb Jack Beer Edition, a game by McGeezer. Here, the player is a 15 color sprite, the P disc is a 3 color sprite, and the enemies are Blitter Objects.

Positioning

Regardless of screen mode, X and Y Positions are written in low resolution (LORES) into two control words per sprite channel.
Even LORES can be higher and wider than 256, so 9 bits are required for both. The top 8 bits (X/2) are written to SPRxPOS, with bit 0 (X&1) written to SPRxCTL - but for Start/Stop Y, the bottom 8 bits (X&255) are written to both, with their 9th bit (X&256) written to two bits in SPRxCTL.
Sprite positions also follow VHPOS, which means they are offset from bitmap coordinates. Add (DDFSTRT+8)*2 to X, and DIWSTRT to Y to match screen coordinates.
All positions are valid, except:
  • SPRxPOS X=0 hides the sprite
  • SPRxPOS X=1..4 displays the sprite in the right border
  • Y<25 (19 NTSC)
  • Y>311 (261 NTSC)

Display

Sprites can be displayed in DMA mode and non-DMA mode interchangeably.
For each valid scanline, a sprite's pixels (SPRxDATA/B) will show only if bitplane DMA is enabled and that line (VPOS) is between its Start Y and Stop Y. Sprite DMA itself (SPREN bit in DMACON) does not have to be on.

DMA behavior

Control words for all sprites are prefetched during vertical blanking, before first valid scanline 25 (19 NTSC). When VPOS=Start Y, the data words for that sprite will be fetched at HPOS $15..$34.
The control words can be updated at any time for DMA and non-DMA sprites until the first data words for that sprite is fetched. Updating them after the data words on each line have been displayed and before HPOS $15 results in a 1-line gap.

Non-DMA behavior

If Sprite DMA is off, data words can be written directly, SPRxDATA arming the sprite thus repeating both words vertically until bitplanes are turned off (e.g. by DIWSTOP) or a write to SPRxCTL disarms the sprite.

Overscan and Clipping

Sprites are automatically clipped horizontally and vertically by DIWSTRT/STOP and vertically by bitplanes being on. Bitplane DMA is activated by setting BPLCON0 >=$1000 or a write to BPLxDAT each scanline. It's deactivated by DIWSTOP, a write of $200 to BPLCON0, or a write to DMACON.
Sprites are not clipped by DDFSTRT/STOP, and can thus be displayed in the border if DIW is larger than DDF. (If an OCS Denise is used however, you must decrease DDFSTRT or write to BPLxDAT each scanline.)
Overscan in the left border (DDFSTRT<=$34) makes bitplane DMA steal cycles from sprites, resulting in fewer than 8 sprites being available, until at maximum overscan (DDFSTRT=$1c), only Sprite 0 is guaranteed. (For compatibility however, do not rely on DDF or DIW to hide or remove sprite channels.)

Initialization and Sprite Chains

Each sprite channel in the sprite engine processes a structure in the form of a chain of one or more sprites.
When writing software that takes over the hardware, once control is passed to your code from the OS, setting sprite DMA on or off for your own purposes must be avoided until correctly timed. Otherwise, the hardware cannot read control and data words correctly and will run rogue through Chip RAM (a.k.a 'rolling sprites'). (An invalid sprite structure can also cause this.)
SPREN/SPRxPT must be set during vertical blank before the first valid scanline.


Valid sprite structures

The following is pseudo-code for a sprite structure consisting of two chained sprites in the same sprite channel.
TwoHappySprites:
	dc.w SPRxPOS,SPRxCTL
	dc.w SPRxDATA,SPRxDATB
	..
	dc.w SPRxDATA,SPRxDATB
	dc.w SPRxPOS,SPRxCTL		;next sprite in chain
	dc.w SPRxDATA,SPRxDATB
	..
	dc.w SPRxDATA,SPRxDATB
	dc.w 0,0			;end of chain

To validate the structure, sprites in vertically increasing order, check that Start Y and Stop Y matches pixel data length.
Because sprite DMA only reads two words each scanline, the gap in the data for the control words translates directly to a 1 pixel gap vertically between the sprites when displayed.
For dynamic sprite generation, first point them to a valid blank sprite structure, and switch pointers when the structures are initialized.
BlankSprite:
	dc.w $1905,$1a00	;1px high sprite in the top-leftmost valid position
	dc.w 0,0		;blank pixel data
	dc.w 0,0		;standard end of chain control word pair.

(There are alternate and shorter blank sprite structures that will not be shared here for compatibility. Furthermore, 0,0 are invalid starting control words which may work on some or all chipsets; validating this is left to each coder.)
After the valid sprite structure has been written to Chip RAM, its starting address can be written to SPRxPT. You must verify that it's ready before doing so. Blitter writes require a correct blit wait. (And in a few niche cases, also BLTPRI=1.) Cached CPUs are normally set to write through cache to Chip RAM at all times.

Sprite pairs, attached sprites, and color combination

Sprite "bitplanes" are combined in ascending address and sprite number order to map to a palette color number.
The 8 Sprite channels come in 4 pairs. Each pair can form two 2-bitplane sprites or one 4-bitplane sprite, depending on the AT bit in SPRxCTL.
Control words for the pair must be identical to map colors correctly, and to be upwards compatible with ECS.

If unattached (AT=0), each pair shares a 3-color palette: COLOR16-31 except COLOR16/20/24/28 (transparent). Attached sprites use COLOR16-31 except COLOR16 (transparent).

Priority

The sprite pairs have a fixed priority, from SPR0/1 (front) to SPR6/7 (back). The lower-numbered sprite in a pair is in front of the other. To reorder internal sprite priorities, reorder their pointers.
To change sprite priority with regards to Playfields, you must tell the hardware at which position in between the sprite pairs each Playfield should reside. It can be thought of as a sorted deck of (Sprite) cards, into which you insert two (Playfield) jokers. This is written as a bit combination to BPLCON2 at any time. Many combinations are possible; refer to the Hardware Reference Manual.
As a shorthand, a combination of $00 displays all sprites in front of bitplane data and $22, all behind. If DPF=0, only PF2 bits affect priority.


Planet Disco Balls, an intro by Antiriad. Here, the blue, red, yellow, and green balls are sprite pairs in first to last priority, each color a pair of sprite chains. The text is the front playfield, and the big ball is the back playfield, inserted in front of the green balls (SPR6/7).

Collision

In a similar way, a bit combination can be written to CLXCON at any time to detect pixel-perfect collision between sprites, and between sprites and bitplane graphics. CLXDAT can be polled at any time to determine collisions, off-loading this great burden from the CPU/Blitter.
If you poll CLXDAT at vertical blank, and you use sprite chains, you must find which sprite in the chain collided, by using vertical overlap calculations. A Copper Interrupt at every Stop Y for every channel which checks CLXCON removes the need for such calculations.
Very few Amiga games to date feature pixel-perfect collision detection, which may be a reason to explore its great benefits!
If all bitplanes are disabled in CLXCON, CLXDAT will show these bitplanes as always colliding, which is undesirable because it can mask a collision. If collision detection is unwanted, instead enable at least 1 bitplane and set all match values to 0.


Multiplexing

Part of the work is already handled by the hardware's sprite chains. Writing general-purpose multiplexing code to build them is complex, however, since it needs a custom sort with a comparator accommodating varying sprite heights, which also distributes them across the sprite channels using a many-to-many comparison with logic to make the best choice, and which switches to use the Blitter if you run out of sprite channels.
However, since sprites can be displayed and re-displayed in half a dozen different ways, there might be truly dozens of possible efficient solutions for special cases.

The Happy Sprite

Because all Amigas can render hundreds of Blitter Objects at full frame rate, its built in sprite engine tends to work best as a complement to that, if at all, rather than as a first method of moving objects.
That said, it's very powerful indeed, and if incorporated into the first design phase of a demo or game, its true capabilities can create (interactive) visuals that could be achieved in no other way.
Historically underused then, Amiga sprites are nevertheless wonderful and intriguing, and I must believe that a sprite used is a happy sprite. :)