The last feature I want to look at on the Sega Master System in this tour is its scrolling capabilities. I’m walking in with four main questions:
- How does scrolling work at all?
- What kind of timing constraints do we face when scrolling over some larger map?
- What do the scroll-inhibitor modes buy us, and what restrictions do they place on us?
- What does it take to do more traditional raster-based split-screen scrolling, and what does that buy us?
These questions turn out to basically have one-sentence answers:
- We set scrolling values by writing VDP registers 8 and 9, which scrolls us over a 32×24 toroidal map.
- We must set the vertical scroll by the end of VBLANK or the setting will shift a frame late. Other than that we have plenty of time to do map updates even when scrolling diagonally.
- The scroll-inhibitor modes give us cheap status windows, but they only work if we strictly only scroll horizontally or vertically. We cannot scroll “into” or “out of” the status window.
- Raster interrupts are scheduled through VDP register 10 and vector through the same IRQ handler as VBLANK, which makes “parallax” scrolling effects pretty easy to write.
That said, we’re about the journey here, not just the answers, so today I’ll be putting these capabilities through their paces with some test ROMs.
Basic Scrolling
The core API here is very simple, but it includes a few surprises. The name table is 32×28 tiles, and normally we have a 31×24 area visible. We may set an X and Y scroll amount between 0 and 255 in VDP registers 8 and 9, respectively. The map wraps around, so we always have enough room out of the visible area to update the next row or column if we need to.
A thing that is unlikely to be a surprise is that the interpretation of these scrolling values are broadly the same on the Master System as on the Genesis. However, somehow I have managed to go seven years without noticing that the Sega consoles interpret scrolling values differently from the Nintendo consoles. On the Master System and Genesis:
- To scroll right, you must decrement the X scroll value.
- To scroll up, you must decrement the Y scroll value.
This is a bit inconsistent. Nintendo flips the polarity of the X value here (so that increasing X scrolls you right), which gives a clean interpretation of the scroll values: you’re setting the coordinates on the map where the top-left of the screen itself is. Meanwhile, the Commodore 64’s scrolling system is the exact opposite of Nintendo, which can be interpreted as setting the coordinates where the top-left tile in the tile map will be drawn. Sega is awkwardly splitting the difference here, but comparing the navigation code for the SNES and Genesis CCA simulations makes it clear that both companies persisted with these APIs.
For my initial test, I just filled up the whole name table with one of my artsier fonts, set the border color to blue, and broadly replicated my scrolling-navigation code from the Genesis CCA. It works well enough:

With this in place as a base, I can start experimenting with the other features the Master System offers.
(EDIT: The blue column down the left of the screen is the out-of-display border—blue now because of the status windows we’re about to add—peeking into the main screen as we disabled the left 8 pixel columns. My emulator, when removing the border, neglects to remove that part of it.)
The Scroll Inhibitor
I’ve never quite seen anything like the scroll inhibitor bits before. I extended the initial program to cycle between the basic display and two screens that each had one of the inhibitor bits set. As long as you only scroll horizontally with the horizontal scroll inhibit bit set and only scroll vertically with the vertical scroll inhibit bit set, everything works great, and the vertical scroll is something I’ve never seen on an 8-bit system before:

As the text suggests, though, if you scroll on the other axis, things go terribly wrong very quickly. The top-level status bar is only immune to horizontal scrolling, and it is affected by vertical scrolling the same as the rest of the screen, producing a comical discontinuity if you pull down the status line and move it a bit:

As you can see from the screenshot above, the letters are actually cut off at the edge of the status window; scroll immunity here is clearly being computed at the scanline level. That doesn’t happen with the vertical scroll immunity:

Here we see the immunity taking hold and being released at a level of individual tiles, which means that the perceived size of the status window can grow or shrink a little. Notice that there’s only a fractional character in the rightmost tile column there; we aren’t at a tile boundary but the scroll boundary still is.
Parallax With Raster Interrupts
The next thing I want to test is the mid-frame scanline-counting interrupts. This involves writing a value into VDP register 10—then, we’ll get an IRQ every time we display that many scanlines. This will jump to the same IRQ vector that our VBLANK interrupt does; in order to distinguish them we’ll need to read the VDP status register and then check the top bit of it. Getting this to work requires me to reorganize a bit of my core “BIOS” library, but there’s nothing particularly esoteric about it.
For my test program, I fill the screen with evenly-spaced dots and then set the interrupt to happen every 48 scanlines. This neatly divides the screen into four parts, and I then set up multiple scroll values to let them move at different rates:

Another easy way to use this would be to put a status window at the bottom of the screen, by setting a countdown of something like 160 scanlines. If we start more than halfway down the screen, we’ll only ever split once, after all.
Timing Constraints
Unlike some other consoles, the Master System doesn’t let us set a “stride” for VDP writes: if we want to write non-contiguous chunks of VRAM, we have to retarget the VRAM pointer for each new block. That’s unfortunate if we’re making a horizontally-scrolling game, because writing a new column of tiles will oblige us to retarget every byte individually. The NES regularly got in trouble if VRAM writes got too extensive, so what are we getting into here?
To test this I created another test ROM, which just horizontally scrolls a 64×32 map forever:

The logic here isn’t terribly exciting; the X scroll value is decremented every frame, and if that scroll value is a multiple of 8, we write the next column of map into the name table column that is currently leftmost on the screen. (This produces no glitch because we’re also masking out that column from our display.) With that written, we can check the Event Viewer in Mesen or Emulicious and see how much of our VBLANK period this takes up.
Not much, it turns out:

This represents an ideal case, where no tiles are flipped and all of them are pattern numbers 0-255 and palette 0—in such cases we only have to write a single byte per column instead of two. And in this case we’ve only paid 8 scanlines, even though I’m relying on my relatively slow “BIOS” routines! Better yet, if we did overrun the 40-line VBLANK period here, those routines respect the VDP timing requirements sufficiently that it’s safe to keep updating graphics as the main display runs. They’re all obscured, after all, so even if they change mid-frame there should be no visible change.
The same cannot be said for sprites or the scroll values themselves, though, so I think my recommendation here would be to set the scroll values first (the vertical scroll gets latched when we finish drawing the border, so we shouldn’t dally on that one), then write the updated sprite data outside of the active display area, and then update any rows or columns in the map that need changing once all that is done.
Things get more interesting when we look at the raster-based updates. There’s some interrupt processing that needs to be done, and then I run a fair amount of generic logic to both check what kind of interrupt it is and then jump through a user vector before the scroll value was changed. Here’s the instruction trace from the first interrupt to the final I/O write that sets the scroll value:
ex af,af'
exx
in a,(VDPSTAT) ; Read status word
ld (vdp_status),a
jp .irq_cont
.irq_cont:
bit 7,a
jr nz,.vblnk
ld hl,(rastervec) ; Load raster IRQ handler
ld a,h ; Is it zero?
or l
call nz,1F ; If not, call it
1 jp (hl)
hirq: ld hl,(pscroll)
ld a,(hl)
inc hl
ld (pscroll),hl
ld l,a
ld h,8
rst set_vdp_register
set_vdp_register:
ld a,l
out (VDPADDR),a
ld a,h
or $80
jr read_vblk.set_vdp_register_cont
.set_vdp_register_cont:
out (VDPADDR),a
Checking the event viewer here is quite interesting:

The actual scroll update happens smack in the middle of the next scanline’s HBLANK period. That’s about as well-timed as one could hope for, here, but it does mean that for a tile-level system like I’m using here it’s probably best to have a small buffer zone between each scrolling lane to make sure that slight changes in timing don’t corrupt the display.
I didn’t do anything special here, and nothing at all like the aggressive design work the Commodore 64 demanded. Things just kind of worked out.
Head to Head With the Competition
The Master System’s scrolling system is pretty comfortable, and it’s also noticeably much more comfortable than the other systems I’ve used from this era. It’s also meaningfully limited in ways that I think we see addressed in the generation that comes after it. I haven’t really done a comprehensive survey of scrolling techniques the way I have with things like graphics displays and spritework, but I nevertheless have a passing familiarity with many of them. We may dispense with its predecessors quickly:
- The Atari 2600 barely understands the concept of a “display” at all, because the fundamental unit of graphics it works with is the scanline instead of the frame. However, this means that scanline-smooth vertical scrolling is extremely achievable on this platform because you can just roll your various graphical elements up or down the screen from frame to frame. Activision’s River Raid is an excellent example of such a system.
- The TMS9918A-based systems offer no hardware support for scrolling at all. Character-level scrolling is still possible—in Graphics I mode, there’s plenty of spare VRAM for double-buffering your nametables or even setting up far wider swap-chains. The most impressive demonstration I’ve seen is the ColecoVision port of River Raid, which repeatedly redefines character graphics to get a smoothly-animated vertically-scrolling display.
- The Atari 800 and Atari 5200 offer a very limited ability to smoothly scroll the screen horizontally and vertically. They are intentionally limited to scroll amounts that map to a single byte of graphics data; the idea for scrolling over larger areas is that ANTIC’s display list would be adjusted for every graphics row to allow for displaying a window into a logical display that is far larger than the full screen. Vertical scrolling is easier for reasons similar to the 2600; while our display has expanded from a scanline to a frame, ANTIC allows us very fine control over the memory locations where graphics start. I took advantage of that in my Atari 800 port of Simulated Evolution.
- The Commodore 64 is more limited in this regard, because it has no equivalent to ANTIC. Its smooth-scrolling system can only move 8 pixels in any direction, and the “video matrix” (what the VIC-II calls its nametables or tilemaps) must be kilobyte-aligned. The developer is expected to swap the video matrix every 8 frames during a continuous scroll. The timing for this gets incredibly tight, because Color RAM cannot be buffered in this way and also cannot be fully refreshed during VBLANK. I believe the technique for this is to start overwriting Color RAM on the previous frame, making sure that your memory writes never catch up to the raster beam as it finishes scanning out the frame; however, I have not replicated the technique so I do not have full confidence in this.
The Master System has two contemporaries worth considering: the NES and the Amiga 1000. The Amiga was a vastly more expensive computer system, with the Original Chip Set offering a design very like the Atari’s ANTIC but enormously more flexible and powerful. It uses similar techniques to allow bitmap scrolling over wider areas, and can hardware-accelerate many of the operations needed for the coarse-scrolling steps as well. To do so, however, it is relying on hardware that was over five times as expensive, including hundreds of kilobytes of video RAM to the Sega’s 16.
The NES is a more interesting case, because as a system it is mostly weaker on paper, yet paradoxically often more powerful as a result. Its one apparent advantage is twice as much nametable space—however, the stricter timing requirements on the NES combined with palettes assigned in 16×16-pixel units meant that 8-directional scrolling was very difficult and almost guaranteed some kind of visible “seam.” The Master System’s tile-level palette selection and VRAM buffering system mean that programming this kind of free-scrolling system is far more straightforward there.
Furthermore, the NES’s built-in ability to manage split-screen displays is minimal—developers are expected to spinlock on a sprite collision flag to decide when to split, and that flag stays latched all frame so you can only split the screen once—but by the middle of its lifecycle the MMC3 chip granted a similar raster-interrupt system. Even without that, since the Nintendo consoles generally did not shield the hardware from software interference the way TI or Sega hardware did, this meant that some level of vertical-scrolling splitscreen display was also meaningfully possible. Early attempts (as in The Legend of Zelda) had some quirks, but thanks to the greater exposure of the hardware, later titles such as Rockman 6 were able to control mid-screen vertical scroll changes as smoothly as the 16-bit systems. Both the SNES and the Genesis have mechanisms that basically allow us to change the vertical scroll level however we please from scanline to scanline, producing intuitively correct results. I relied on that for title screen effects on both the SNES and the Genesis.
The 16-bit SNES and Genesis are also a better comparison point for the Master System’s “scroll-inhibitor” system. These were effective but basically hardcoded and extremely fragile—you could really only use them if they were exactly the thing you wanted, and for vertical-scrolling games in particular your options were pretty bleak. Both the SNES and the Genesis solve this problem by offering multiple independently scrollable background layers, with the Genesis in particular having a special non-scrollable “window” layer attached to one or two of the corners. These dedicated layers provide the “status window” effect pretty readily and by giving each layer its own name table and scrolling controls, the fragility of the Master System’s offerings evaporates. The Game Boy offers a Genesis-like window layer as well despite not otherwise having multiple scroll layers.
Overall this places the Master System very firmly between its predecessors and its successors—it offers a much cleaner programming model and several experimental features that ultimately end up nailing down the possibility space harder than we’d like. Nevertheless, its successors basically generalized and improved those designs rather than double down on the approaches of its predecessors or competitors.
The End of the Road
I’ve seen what I came to see, with the Sega Master System, now. I first approached it for completeness’ sake—it’s the the system between the SG-1000 and the Mega Drive, and it used a successor to the TMS9918A VDP, so I owed it at least a cursory glance—and now I’m pretty comfortable saying I’ve put the machine through its paces. The only parts of the hardware left are non-controller input devices and cartridge sizes larger than 48KB, both of which are well-documented in the manuals I link in the Platform Guide.
As a system, it is quite pleasant: it’s markedly more powerful than its peers at the time, while also offering a more direct, what-you-see-is-what-you-get sort of programming interface that removes most of the sharp corners. It’s clearly an artifact of its era, and it’s not hard to see it taking concrete steps away from the world of the SG-1000 and even the Famicom, and towards the world of the Mega Drive, the SNES, and the Game Boy. It wasn’t trying to be the best (and most expensive) that consumer tech could provide in 1985, but it also ends up pretty forward-looking. I’m not at all surprised that it seems to have a very dedicated and prolific homebrew community to this day.