In July 1986 a CRASH reviewer admitted that the magazine had laughed when Elite announced a Spectrum Ghosts ’n Goblins: they had “dismissed the deal as triumph of marketing over the possible”. Then Keith Burkhill’s conversion arrived, “equipped with perfectly smooth scrolling”, and it scored 95%. The surprise was fair. The ZX Spectrum has no scroll registers at all, so every pixel that moves on screen is moved by the Z80, one byte at a time. Many Spectrum games avoided the problem by changing the picture a screen at a time. Those that scrolled had to find a way to move thousands of bytes in the fraction of a frame left over from the game itself.
The budget
A 48K Spectrum frame lasts 69,888 T-states (clock cycles), a fiftieth of a second at 3.5 MHz. The bitmap is 6,144 bytes, plus 768 bytes of colour attributes. The Z80’s block copy, LDIR, takes 21 T-states for each byte it moves:
6,144 bytes × 21 T-states ≈ 129,000 T-states
69,888 T-states per frame → nearly two frames, before drawing anythingThat is the best case. The screen sits in the 16K of RAM the ULA reads to draw the picture, and while it is fetching screen bytes the ULA holds the Z80’s clock whenever the processor touches that RAM. Writes to the screen during the visible part of the frame run slower than the manual’s figures. A full-screen move every frame is out of reach, so every scrolling technique on the Spectrum is a way of moving less, or moving it faster.
Screen layout
The display file is not stored in screen order, and the manual says so: it “is rather curiously laid out, so you probably won’t want to PEEK or POKE in it”. The screen is in thirds of 64 pixel rows, and within each third come “the top scans of lines 0 to 7, then the next scans of lines 0 to 7, and so on”:
Pixel rows 0-7 of character row 0: $4000, $4100, $4200, ... $4700
Pixel rows 0-7 of character row 1: $4020, $4120, $4220, ...
...
Character row 8 (second third): $4800, $4900, ...The attributes, by contrast, “are stored line by line in the order you’d expect”: 768 bytes from $5800, 32 per row. Screen Memory explains why the ULA lays the bitmap out this way.
Two things follow for scrolling. Every 32-byte block in memory is one pixel row, so a horizontal scroll can walk the 6,144 bytes in memory order and never needs to know which row it is on. A vertical scroll has to move pixel row y to the address of row y−1, which jumps between thirds, character rows and scan lines. Andrew Hewson printed scroll routines for all four directions in Sinclair User in December 1986 and noted that “the up and down routines are a lot longer than the left/right routines because the Spectrum screen is laid out in a seemingly” illogical way.
Address calculation
The address of the first byte of pixel row y is $4000 | ((y & $C0) << 5) | ((y & $07) << 8) | ((y & $38) << 2). Bits 7–6 of y select the third (high byte +$00, +$08 or +$10). Bits 2–0 pick the pixel row within a character square (high byte +$00 to +$07). Bits 5–3 pick the character row within the third (low byte $00 to $E0 in steps of $20).
; Convert Y coordinate to screen address (column 0)
; Input: B = Y (0-191)
; Output: HL = screen address of byte 0 of that pixel row
y_to_address:
ld a, b
and %00000111 ; Y[2:0]
or $40 ; high byte base = $40
ld h, a ; H = $40 | Y[2:0]
ld a, b
and %11000000 ; Y[7:6]
rrca
rrca
rrca ; Y[7:6] >> 3 (bits 4-3 of the high byte)
or h
ld h, a ; H = $40 | Y[2:0] | (Y[7:6] >> 3)
ld a, b
and %00111000 ; Y[5:3]
rlca
rlca ; Y[5:3] << 2 (0, $20, $40, ... $E0)
ld l, a ; L = column 0 of this pixel row
retCheck: Y=0 gives $4000; Y=8 gives $4020; Y=64 gives $4800; Y=191 gives $57E0. A vertical scroll calls something like this, or steps from row to row with INC H and a correction every eighth row (the method Screen Memory works through), for every row it moves.
One-pixel horizontal scroll
Shifting a pixel row one pixel left means rotating its 32 bytes through the carry flag, starting from the right-hand end. The leftmost pixel of each byte is bit 7. RL (HL) moves bit 7 into the carry and the old carry into bit 0, so the pixel that leaves the left of byte n+1 arrives at the right of byte n:
scroll_left:
ld hl, $57FF ; last byte of the bitmap
ld c, 192 ; 192 rows, in memory order
.row:
or a ; clear carry: a blank pixel comes in at the right
ld b, 32
.byte:
rl (hl) ; bit 7 -> carry, carry -> bit 0
dec hl
djnz .byte
dec c
jr nz, .row
retThis is the shape of the 17-byte routine in Hardman and Hewson’s 40 Best Machine Code Routines for the ZX Spectrum (1982). Walking the whole bitmap backwards works because the rows are contiguous 32-byte blocks: after the 32nd byte of one row, HL is already at the last byte of the row before it in memory. A right scroll uses RR (HL) and walks each row from its left-hand end instead.
The cost is 34 T-states a byte in the inner loop (RL (HL) 15, DEC HL 6, DJNZ 13), about 213,000 T-states for the full screen with the loop overhead: three frames for a single pixel. Unrolling helps. Each row sits inside one 256-byte page, so within a row DEC L (4 T-states) can replace DEC HL, and thirty-two RL (HL) in a row need no DJNZ. That brings a row down to about 630 T-states and the screen to about 121,000, still well over a frame and a half, which is why pixel scrolling on the Spectrum usually meant a smaller window.
Character scroll
Moving whole characters is cheaper per step and much coarser. Each pixel row is 32 bytes, so an 8-pixel left scroll copies bytes 1 to 31 down to bytes 0 to 30 and refills byte 31:
; Scroll the whole screen left by 8 pixels, one pixel row at a time
scroll_char_left:
ld hl, $4001 ; source: one byte right of the destination
ld de, $4000 ; destination
ld a, 192 ; 192 pixel rows, in memory order
.row:
push af
ld bc, 31
ldir ; HL = $4020, DE = $401F
ex de, hl
ld (hl), 0 ; refill byte 31: blank here, or the new column of the map
ex de, hl
inc hl ; HL = $4021: source for the next row
inc de ; DE = $4020: destination for the next row
pop af
dec a
jr nz, .row
retEach row costs about 720 T-states, so the whole bitmap takes about 140,000: two frames. A game moving 8 pixels every two frames is scrolling at 4 pixels a frame, fast but visibly jumpy. The attributes can move with it, because an 8-pixel step is exactly one attribute square: 768 more bytes, about 16,000 T-states with LDIR.
Push-scroll
The fastest way to move memory on a Z80 is not a copy instruction at all. POP reads two bytes from where the stack pointer (SP) points into a register pair in 10 T-states; PUSH writes a pair in 11. Point SP at a source, POP several register pairs, point SP at the destination and PUSH them back, and two bytes move for about 21 T-states, roughly half the per-byte cost of LDIR (21) and well under LDI (16). The data is already in registers on the way through, so it can be masked or combined before it is written. Two details catch programmers out. PUSH decrements SP before each write, so the destination fills from its end backwards. And while SP points into the screen or a data buffer, an interrupt would push its return address there, so interrupts have to be off for the whole pass.
When Software Creations wrote the Spectrum Ghouls ’n Ghosts for U.S. Gold in 1989, Sinclair User’s “Blueprint” of the game traced the method back to Burkhill’s conversion of the original: “Ghosts & Goblins was one of the first games on the Spectrum to use a ‘push-scroll’ technique. This type of scroll routine is extremely unpleasant to even think about for too long, but does allow movement by any number of pixels in any direction.” In both games, it said, “a 2-pixel horizontal scroll combined with a 1 pixel scroll can be performed and sprites dumped on the screen in less than 2 TV frames”, which was “much smoother than an average 4-pixels-every-3-frames scroll seen in many Spectrum games”. The sequel used “the Z80 processor’s 16-bit register SP to do both the scrolling and masking the sprites over the background”. The routine itself was not published.
The magazine was frank about the price. With interrupts off, “the processor is uninterruptably tied up for most of the 2-frame game cycle, so music could only be played at a 25Hz rate”. The team kept three-channel sound effects for the game and kept “high quality 50Hz music” for the menu screens. The two-pixel step had a memory cost as well: “every sprite must then be printable at a 2-pixel resolution. On the Spectrum, this means having 4 shifted images of each sprite animation and background block in memory.” Four copies of every sprite did not fit in the 48K machine, so the game stored one and made the other three shifted copies as each new monster type appeared, with the shifting code running “simultaneously with the game, but outside the interrupt”. Even the scenery had to fit the scroll: “the design of the backgrounds is severely limited by the scrolling technique, but the continuous smooth scrolling adds so much to the gameplay that this limitation can be endured.”
Attributes
The attribute grid moves only in whole characters:
| Scroll amount | Attribute handling |
|---|---|
| 8 pixels | Scroll the attribute file too: 768 bytes, in order |
| 1–7 pixels | The colours cannot follow; each square keeps its colour while the pixels slide through it |
So a pixel-scrolling game either scrolls a single-colour area, keeps colour to bands that do not move, or accepts some colour clash. The attribute file on its own is cheap to move, 768 bytes against 6,144, and moving only the attributes gave a chunky, colourful scroll made of whole squares. Scrolling messages built that way became a demo staple: Your Sinclair was still reviewing “the obligatory large attribute scrolly” in 1993.
Window scrolling
Scrolling part of the screen scales the cost by the window. A window w bytes wide is a horizontal scroll of w bytes per pixel row, with y_to_address (or a memory-order walk, if the window is full width) finding each row’s start. Vortex’s H.A.T.E. (Gremlin Graphics, 1989) moved its diagonal playfield two pixels at a time, and Vortex’s own account in Sinclair User shows what that took: “In H.A.T.E. we used 18K solely for the scrolling. Two pixel scrolling is a fairly simple bit of programming until you see how it slows down on the screen … In fact it was the fifth re-write that finally gave us the speed we needed.”
Summary
| Approach | Cost for the full bitmap | Motion |
|---|---|---|
| Flip-screen | One redraw per screen | Instant |
| Character scroll | About 140,000 T-states, plus 768 attribute bytes | 8-pixel steps |
Pixel scroll (RL (HL)) |
About 213,000 T-states looped, 121,000 unrolled | Smooth, one pixel |
| Push-scroll | About 21 T-states per two bytes, interrupts off | Any step |
| Window scroll | Proportional to the window | Area-limited |
| Attribute-only | 768 bytes | Whole colour squares |
All of these figures come from the Z80 manual’s instruction timings and leave out the ULA’s contention delays, which only add to them.
Who scrolled
- Blue Thunder (Richard Wilcox Software, 1984): advertised in CRASH’s third issue for its “Hi Res Graphics with Pixel Scrolling over 6 screens”.
- Ghosts ’n Goblins (Elite, 1986): Keith Burkhill’s conversion, which CRASH praised for “beautifully smooth scrolling” and Sinclair User later named as one of the first Spectrum games to use push-scroll.
- Uridium (Hewson, 1986): Dominic Robinson’s conversion, whose smooth scrolling CRASH still called “almost unique” when it came back at £2.99 in 1988.
- Ghouls ’n Ghosts (U.S. Gold, 1989): Software Creations’ push-scroll, programmed by Mike Follin with graphics and maps by Ben Jackson.
- H.A.T.E. (Gremlin Graphics, 1989): Vortex’s two-pixel diagonal scroll, five rewrites and 18K of code.
Sources
- Steven Vickers, Sinclair ZX Spectrum BASIC Programming (Sinclair Research, 1982), chapter 24: the display file and attributes.
- Zilog, Z80 CPU User Manual (UM008011-0816): T-states for
LDIR,LDI,RL (HL),RR (HL),POP,PUSH,LD (HL),n,DEC HLandINC HL, and the order in whichPUSHdecrementsSP. - Chris Smith, The ZX Spectrum ULA: How to Design a Microcomputer (ZX Design and Media, 2010): 69,888 T-states per frame (p. 225); the ULA holding the Z80’s clock during screen fetches (p. 190).
- John Hardman and Andrew Hewson, 40 Best Machine Code Routines for the ZX Spectrum (Hewson Consultants, 1982), section 5, “Scroll Routines”: “Left Scroll by One Pixel”.
- Sinclair User issue 57, December 1986: Andrew Hewson’s column on pixel scrolling in four directions.
- CRASH issue 3, April 1984: Richard Wilcox Software’s Blue Thunder advertisement.
- CRASH issue 30, July 1986, pp. 28–29: Ghosts and Goblins review (Elite Systems, £8.95, author Keith Burkhill; 95%).
- CRASH issue 56, September 1988, p. 86: Uridium budget re-release review.
- Sinclair User issue 84, March 1989: Vortex on the making of H.A.T.E.
- Sinclair User issue 92, November 1989, “Blueprint”, from p. 86: the Spectrum Ghouls ’n Ghosts, push-scroll, shifted sprites, 25Hz music and credits.
- Your Sinclair issue 88, April 1993: demo reviews and “the obligatory large attribute scrolly”.