Skip to content

Hardware Scrolling

Moving the picture by changing where the chip starts drawing

Scroll registers let a video chip start drawing the picture a few pixels later, so one register write moves the whole screen. The Atari 400/800's ANTIC, the C64's VIC-II, the NES PPU, the Master System's VDP and the Amiga's bitplane DMA each did it differently, and each left the program a different amount of work when the fine scroll ran out.

A video chip with a scroll register can start drawing the picture a few pixels later than usual, so a program moves the whole screen by writing one number. On a machine without one, the same movement means copying thousands of bytes. The saving only goes so far, though. A fine scroll register covers a single character or tile, and when it runs out the program has to make a coarse move of one whole character, and draw the strip of new scenery coming in at the edge. How each machine handled that coarse step, and how much of it the program had to do by hand, is what separated them.

The Scrolling entry gives the overview and the history; this entry is about the registers themselves. The machines below appear roughly in order of release.

Machine Fine scroll Coarse scroll Hiding the new edge
Atari 400/800 (1979) HSCROL $D404, VSCROL $D405, 0–15, per display-list line Change the screen address in the display list ANTIC fetches a wider line; one spare mode line at the bottom
Commodore 64 (1982) Bits 0–2 of $D016 (X) and $D011 (Y), 0–7 Copy the screen memory 38-column and 24-row modes
NES (1983) $2005, written twice, X then Y Bits 0–1 of $2000 choose the nametable; the map is two screens wide or tall Bits 1–2 of $2001 can blank the left 8 pixels
Master System (1985) Registers 8 and 9 The scroll registers cover the whole map Register 0 bit 5 blanks the left column
Amiga (1985) BPLCON1 delay, 0–15 Move the bitplane pointers Fetch one extra word per line

Atari 400 and 800: ANTIC and the display list

Atari’s home computers have the most flexible scrolling of the 8-bit machines, because their display chip, ANTIC, runs a program. The display list tells ANTIC what to draw on each band of the screen, and any line of it can carry a Load Memory Scan (LMS) instruction with its own screen address. De Re Atari, the 1982 technical guide by Atari’s Software Development Support Group, whose scrolling chapter was written by Chris Crawford, explains that changing those addresses is a coarse scroll: add or subtract 1 to move sideways, the line length to move up or down, both to move diagonally. “Instead of manipulating an entire screenful of data, many thousands of bytes in size, a program need only manipulate two or perhaps a few dozen bytes.”

Fine scrolling takes two steps. Bits 4 and 5 of a display-list instruction enable horizontal and vertical fine scrolling for that line, and two registers hold the amount: HSCROL at $D404 in colour clocks, VSCROL at $D405 in scan lines, each with 16 positions, and when the fine scroll reaches the size of a character the program resets it to 0 and makes a coarse step. Because the scroll is enabled line by line, one part of the screen can scroll while another stays still, with no interrupt needed.

The book also names the traps. With horizontal scrolling switched on, ANTIC fetches a wider line than it shows, 48 bytes instead of 40 in the standard text mode, so screen memory has to be laid out in long lines. For vertical scrolling one mode line at the bottom is left without the scroll bit, as a buffer for new data. And the registers should only be changed during the vertical blank, because a change while ANTIC is drawing “confuses ANTIC and causes the screen to jerk”. The reward was memory: Crawford describes a scrolling map of Russia “which contains about 10 screenfuls of image” in “about 4K of RAM”. In April 1982 an advertisement in ANTIC magazine offered tutorials to anyone who had “looked with envy at … the smooth scrolling graphics window in Crawford’s Eastern Front”.

Commodore 64: fine registers, coarse copying

The VIC-II can shift the screen by 0 to 7 pixels: bits 0–2 of $D016 (53270) for X and bits 0–2 of $D011 (53265) for Y. Commodore’s Programmer’s Reference Guide says plainly that “the actual scrolling must be done by a machine language program”, because the chip always reads the text screen from the same 1,000 bytes. Clearing bit 3 of $D016 narrows the picture to 38 columns and clearing bit 3 of $D011 shortens it to 24 rows; the border then covers a column at each side or a row at the top or bottom, where new data can be put before it scrolls into view. “When scrolling LEFT, the new data should be placed on the right.”

Directions matter. A larger value in the X register moves the picture right, so a game scrolling to the left counts the register down from 7 to 0, and at the wrap back to 7 shifts every character one place left. Vertically, “when the Y scrolling register is set to 0, the first line is covered, ready for new data. When the Y scrolling register is set to 7 the last row is covered.” $D011 also holds the display-enable and bitmap-mode bits and bit 8 of the raster-interrupt line, so a program changes only the bottom three bits, and a change to the vertical scroll also moves the badlines, the lines where the VIC-II stops the processor to fetch character data (see VIC-II).

This routine scrolls the screen one pixel left each time it is called, written for the ACME assembler:

Assembly
SCREEN  = $0400

; Scroll the text screen one pixel to the left. Call once a frame.
scroll_left
        lda xscroll
        sec
        sbc #1              ; step the fine scroll: 7, 6, 5 ... 0
        and #7
        sta xscroll
        cmp #7              ; did it just wrap from 0 back to 7?
        bne set_reg
        jsr shift_left      ; yes: coarse scroll, one whole character
set_reg
        lda $d016
        and #%11110000      ; clear XSCROLL and bit 3 (38 columns)
        ora xscroll
        sta $d016
        rts

; Move every row one character left, rows 0-12 then 13-24.
shift_left
        ldx #0
-       !for row, 0, 12 {
        lda SCREEN + row*40 + 1, x
        sta SCREEN + row*40, x
        }
        inx
        cpx #39
        bne -
        ldx #0
-       !for row, 13, 24 {
        lda SCREEN + row*40 + 1, x
        sta SCREEN + row*40, x
        }
        inx
        cpx #39
        bne -
        jsr draw_right_column   ; column 39 is free for new scenery
        rts

xscroll !byte 0

draw_right_column is the game’s own routine for filling column 39 from its map. The copy is split into two loops only because a loop of 25 rows is too long for a branch instruction to reach back over. The copy is where the cost lies. By the timings in MOS Technology’s programming manual, each byte takes a 4-cycle load and a 5-cycle store, so the 975 bytes cost about 9,500 cycles with the loop overhead, nearly half of the 19,656 cycles in a PAL frame and more once the badlines take their share. Started when the beam leaves the bottom of the picture, the copy is still running when the beam starts the next frame at the top. That is why games build the shifted screen in a second buffer and switch the VIC-II over to it (double buffering), or spread the copy over several frames.

Colour memory at $D800 is the other half of the problem. It is a separate chip that cannot be switched like the screen, so Lasse Öörni (Cadaver) wrote that it “can’t be doublebuffered” and has to be shifted while the rows being changed are not on screen. Games found several ways to spend less on it:

  • One colour per block of characters, as in the Shoot-’Em-Up Construction Kit: with blocks four characters wide, only every fourth column changes colour on a one-character shift, which “can cut color memory accesses to 1/4th portion”.
  • A colour for each character code, looked up while copying. Öörni named Nobby the Aardvark, Darkman and Cool World as users and called it “abominable”, four cycles more per byte than a plain shift.
  • Splitting the shift: the upper half of colour memory while the lower half is still being drawn, and the lower half after the picture ends, which he pointed to in the Metal Warrior 2 and 3 source code as the way to stay compatible with NTSC machines and their shorter border time.

NES: nametables and PPUSCROLL

The NES PPU never needs its screen copied. Its background comes from nametables of 32×30 tiles, four of them in a 2×2 arrangement, though the console has memory for only two. The cartridge wiring decides whether the two real ones sit side by side, for sideways scrolling, or one above the other. The scroll is written to PPUSCROLL at $2005 twice, X then Y, and bits 0–1 of PPUCTRL at $2000 choose which nametable the window starts in, which works as a ninth bit of each coordinate. The coarse scroll happens in hardware: as the window moves, the PPU fetches tiles from the neighbouring nametable. The program only writes the new column or row into the nametable just out of sight. Setting bit 2 of $2000 makes each write to video memory step 32 bytes, straight down a column, so a column of 30 tiles goes out in one run during the vertical blank.

$2005 and $2006 share one latch that decides whether a write is the first or the second, and reading PPUSTATUS at $2002 resets it. Because the scroll and the video memory address also share the PPU’s internal registers, the scroll has to be written again after every vertical-blank update. A still status bar, as in Super Mario Bros., takes a change of scroll partway down the screen. The sprite 0 hit flag, bit 6 of $2002, is set when the first sprite’s solid pixels overlap the background, so a program puts sprite 0 on the bottom edge of the status bar and waits for the flag. These two fragments, for the ca65 assembler, do both halves:

Assembly
; In the NMI handler, after the vertical blank updates:
        bit PPUSTATUS       ; reset the $2005/$2006 write latch
        lda #0
        sta PPUSCROLL       ; status bar: X = 0
        sta PPUSCROLL       ;             Y = 0
        lda #%10000000      ; NMI on, nametable 0
        sta PPUCTRL
Assembly
; In the main loop, once a frame:
split:
        bit PPUSTATUS       ; BIT copies bit 6, the sprite 0 hit, into V
        bvs split           ; wait for last frame's hit to clear
@hit:   bit PPUSTATUS
        bvc @hit            ; wait for this frame's hit
        lda scroll_x
        sta PPUSCROLL       ; new X takes effect from the next line
        sta PPUSCROLL       ; mid-frame this Y write is ignored
        lda scroll_nt       ; 0 or 1: the nametable X counts from
        ora #%10000000
        sta PPUCTRL

The first wait is needed because the flag stays set until the end of the vertical blank, so a fast main loop could see last frame’s hit. Only the horizontal scroll can be changed like this: the PPU copies the new X and nametable bit into its counters at the end of each line, but ignores a mid-frame Y. Changing the vertical scroll partway down needs a sequence of writes to $2005 and $2006 timed to the horizontal blank, as the NESdev wiki documents. And if the hit never comes, because the sprite no longer overlaps any solid background, the program waits forever; the wiki notes that this “often results in a crash”. Nintendo’s MMC3 mapper removed the busy-wait by counting lines and interrupting the processor at a chosen one.

Sega Master System: two registers and three helper bits

The Master System’s video chip holds a background map of 32×28 tiles, of which 24 rows are visible, and two registers scroll it: register 8 horizontally and register 9 vertically, both through the whole map with wrap-around. Sega’s 1986 manual adds three bits to register 0 aimed squarely at games. Bit 6 keeps the top two rows of tiles still during horizontal scrolling and bit 7 keeps the right-hand eight columns still during vertical scrolling, so that, in the manual’s words, “the scores remain in place”. Bit 5 blanks the leftmost column of tiles in the border colour, hiding the tile that is only partly scrolled in.

Two details trip programmers up. Sega’s manual says that a value of 1 in register 8 “moves the background scene one pixel to the left”, but Charles MacDonald’s documentation of the chip, measured on the hardware, describes the fine part of the value as pushing the picture to the right, with the uncovered strip at the left edge “filled with the backdrop color”. And register 9 is only read once a frame: the manual says it “is latched in vertical blanking”, so a raster split with the line interrupt can change the horizontal scroll partway down the screen but not the vertical.

Amiga: pointers and a delay

The Amiga has no tile map to wrap around; its playfield is bitplanes in memory, read through a pointer for each plane. Commodore’s Hardware Reference Manual says that for vertical scrolling “all you need do” is change the starting address of the pointers by the length of one line. Nothing is copied at all.

Horizontal scrolling uses a delay register, BPLCON1 ($DFF102), which holds the picture back by 0 to 15 pixels, and the pointers move on by one word for each 16 pixels. The manual lists what else has to change. The chip fetches one extra word per line, so the data-fetch start moves 16 pixels earlier, DDFSTRT $0030 instead of $0038, which “will disable sprite 7”, and the modulo, the bytes skipped at the end of each line, is two less than for a still screen. Bits 3–0 of BPLCON1 delay playfield 1 (bitplanes 1, 3 and 5) and bits 7–4 playfield 2 (bitplanes 2, 4 and 6). With a single playfield both nibbles need the same value, $77 for a delay of 7; setting only the low nibble leaves the even planes behind. The pointers are reloaded every frame anyway, so the manual suggests letting the Copper write the new pointers and delay during the vertical blank. In dual-playfield mode the two nibbles differ, and the two playfields scroll at different speeds without the processor moving a byte, the basis of Amiga parallax scrolling.

Sources

  • Atari Software Development Support Group, De Re Atari (Atari Program Exchange, 1982), preface (Chris Crawford wrote sections 1–6) and chapter 6, “Scrolling”, pp. 6-1 to 6-8.
  • ANTIC, The Atari Resource vol. 1 no. 1, April 1982: Tricky Tutorials advertisement (Eastern Front).
  • Commodore, Commodore 64 Programmer’s Reference Guide (1983), pp. 128–129: smooth scrolling.
  • MOS Technology, MCS6500 Microcomputer Family Programming Manual (1976), instruction timings: LDA absolute,X 4 cycles (plus 1 across a page), STA absolute,X 5 cycles.
  • Christian Bauer, The MOS 6567/6569 video controller (VIC-II) and its application in the Commodore 64 (1996), section 3.4: 312 lines of 63 cycles on the PAL 6569.
  • Lasse Öörni (Cadaver), “Multidirectional scrolling and the ‘game world’”, Codebase64 (web, secondary): colour memory, per-block colour, the lookup method and the split shift.
  • NESdev Wiki, “PPU scrolling”, “PPU registers” and “Mirroring” (web, secondary): $2005, $2000, the shared latch, the sprite 0 hit and split scrolling. https://www.nesdev.org/wiki/PPU_scrolling
  • Sega, Software Reference Manual for the Sega Mark III (1986), p. 7 (scroll inhibit bits), p. 9 (left-column blanking), p. 17 (registers 8 and 9). https://segaretro.org/images/d/d6/SoftwareReferenceManualForSegaMarkIIIEU.pdf
  • Charles MacDonald, “Sega Master System VDP documentation”, SMS Power!, 2002 (web, secondary): registers $08 and $09. https://www.smspower.org/uploads/Development/msvdp-20021112.txt
  • Commodore-Amiga, Amiga Hardware Reference Manual (1985), “Moving (Scrolling) Playfields”, pp. 75–80.

See also

Checked. Verified against primary sources and signed off by a person.