Skip to content
Techniques & Technology

Multidirectional Scrolling

Eight ways, or any way

The C64 shifts 1,000 bytes of screen memory by hand every time the hardware scroll register wraps, so multidirectional scrolling is a scheduling problem: spread the shift over frames, hide it behind a second screen, and decide whether the scroll is locked to eight directions or free.

commodore-64graphicsscrollingprogramming

The VIC-II can move the picture up to seven pixels in each axis for free. The eighth pixel is the problem: at that point every one of the 1,000 screen bytes has to be copied one position along, and possibly every one of the 1,000 colour RAM bytes too. Cadaver (Lasse Öörni), writing up the method behind his Metal Warrior games, puts the cost plainly: “Shifting 1000 bytes (the whole screen) around takes more than half of the rastertime of a frame.” Multidirectional scrolling on the C64 is the craft of paying that cost without the player seeing it.

Fast facts

  • Problem: the hardware scroll covers 0–7 pixels; the coarse shift covers the rest and costs over half a frame.
  • Answer: split the shift across frames and hide it on a second screen.
  • Two schools: eight-directional with the scroll register “centred when idle” (Turrican, Navy Seals), or free-directional at any speed up to four pixels a frame (Metal Warrior).
  • The awkward part: colour RAM has one copy and cannot be double-buffered.
  • Lineage: Jukka Tapanimäki’s C-64 Pelintekijän Opas (the Netherworld author’s game-maker’s guide), Cadaver’s rants, Malcolm Bamber’s 4-way tile scroller on Codebase64.

Why the shift is split

The screen shift cannot happen in one frame alongside a game, so it happens over several, and the number of frames available depends on the maximum scroll speed. At four pixels a frame the register wraps every two frames, so the shift can take two. The half-finished copy would be visible, so it is done on a hidden screen and the VIC-II is pointed at it once the copy is complete — see double buffering.

The copy always runs from the visible screen into the hidden one. That removes the usual worry about which way a memory move must run to avoid overwriting its own source, and it means one loop covers every direction: index the source with X and the destination with Y, and the eight directions are eight pairs of starting values.

Eight-directional: centred when idle

Look at Turrican or Navy Seals and you see that they scroll a whole character at a time, never less. Cadaver’s reading is that they park the scroll register on 3 or 4 — the middle of a character — when idle, and each scroll is a committed run of frames. For a two-pixel-per-frame scroll to the left:

Frame $D016 Work
1 4 first half of the screen shift
2 2 second half, draw the new edge
3 0 shift colour RAM, swap screens
4 6 nothing to do
5 4 loop, or stop

Scrolling right starts from 3 rather than 4, because otherwise the wrap arrives a frame early with the shift unfinished. The reset from 4 to 3 is a one-pixel hop that Cadaver calls “barely noticeable”. The price of the scheme is that it locks scrolling to the eight compass directions at fixed speeds.

Free-directional

Cadaver’s own method, which he found for himself and was unsure any commercial game had used (“Possibly Chuck Rock or X-Out”), drops the idle-centring. Any speed up to four pixels a frame, in any direction, with two kinds of frame:

  1. Add the speed to the fine-scroll values and clamp them at the end of the 0–7 range. Then precalculate next frame’s values with wrapping allowed. If nothing wraps, stop: no shift needed. If something wraps, shift the hidden screen and draw new data at the edges.
  2. Shift colour RAM, swap screens, and put the precalculated scroll values into effect.

The cost is that the shift is now always split over exactly two frames, so it is heavier per frame than the eight-way scheme. Metal Warrior 4 keeps the scroll position and speed with three bits of subpixel precision — values 0–63 rather than 0–7, speed a signed number from −32 to +32 — and separates the decision from the work: SCROLLLOGIC at the top of the frame decides whether a shift is due and updates the value sprites subtract to stay in place; SCROLLWORK at the bottom does the copying.

Colour RAM

Colour memory at $D800 has no second copy, so it has to be updated while the part being written is off the beam. On PAL there is time to shift twenty-odd rows in the vertical blank; on NTSC there is not, so the update splits — shift the top half while the bottom half is still being displayed, then the bottom half once display ends.

Two shortcuts, one good and one bad:

  • If colour is per block rather than per character — SEUCK’s model, and the choice Achim’s tile-decoding page describes as “less colourful” but cheaper — then scrolling a 4-wide block only changes every fourth column of colour RAM, and the update shrinks to a quarter. Metal Warrior 1 does this.
  • Looking up each character’s colour from a table as it is written (ldy screen,x / lda charcolortable,y / sta colormemory,x) is the method Cadaver found in Nobby the Aardvark, Darkman and Cool World. It costs four cycles a byte more than copying, “so slow that you can forget about NTSC compatibility when using it”.

Tile scrollers

Malcolm Bamber’s 4 Way Scroll on Codebase64 is the worked example for a 2×2-tile map. The raster interrupt steps the scroll register two pixels at a time, draws half a tile at each step, keeps a spare colour map and copies odd and even lines to $D800 on alternate steps, and flips between screens at 1024 and 3072. The SEUCK Redux scroller takes the opposite tack for a single-direction game: spread one shift across several frames to lower the per-frame cost.

Where objects live

Screen coordinates are fastest and wrong. Cadaver points to Turrican’s walkers, which misbehave near the screen edges because they only exist in screen space and only test the characters currently on screen. Metal Warrior 1 and 2 held 16-bit pixel positions from the map’s top-left; Metal Warrior 3 and BOFH store the block index in the high byte and the position within the 32-pixel block in the low byte, which makes the map lookup a single byte read and leaves three bits of subpixel precision for free.

See also

Not yet fact-checked. This entry was drafted by an AI and nobody has verified it. The dates, figures and technical details may be wrong. Use it to find your bearings, then confirm anything that matters against a primary source.