Eric Ball Wrote
On the 7800 the screen is broken up into "zones" of 1-16 scanlines (typically 8 or 16), based on the 3 byte DLL header. The DLL header contains a 16 bit address pointer to the display list for the zone.
The display list is made up of a sequence of 4 or 5 byte sprite headers ended by a 2 byte null header. Each direct sprite header contains a 16 bit address pointer to the graphics data drawn on the last scanline of the zone. (Indirect sprites, or tiles, are a little different.)
Graphics data is laid out "upside down" with the last scanline on the lowest address and each scanline on a separate page. e.g. for a 2 byte x 16 line sprite
$e000-$e001 bottom line
$e100-$e101 2nd last line
...
$ee00-$ee01 2nd line
$ef00-$ef01 top line
Horizontal motion on the 7800 is easy 'cause each sprite header contains a 1 byte horizontal position (though this means only 160 onscreen positions even in 320 modes). Vertical motion requires a little more work. Assume we are using 16 line sprites and zones. If the sprite is "on grid" then we have the following
line 0-15 DLL -> sprite header -> $e000
Now say that the sprite moves down one scanline, then the sprite is overlaps two zones and we need the following
line 0-15 DLL -> sprite header -> $e100
line 16-31 DLL -> sprite header -> $d100
Some of this seems counter-intuitive, but remember that the sprite header points to the bottom of the sprite. So the top scanline of the second zone will draw the graphics stored at $d100 + 15*256 = $e000 or the last scanline of the sprite. So the sprite has moved down one scanline.
This is also where "Holey DMA" comes into play. In the above example the top scanline of the first zone will draw the graphics stored at $e100 + 15*256 = $f000. In the DLL there are two flags, one which treats odd 4K segment of ROM as full of zeros. So when that flag is set, graphics reads from $dxxx and $fxxx will be treated as zeros. Thus we can store code and non-graphics data in those segments.Â