User Tools

Site Tools


base:20-pixel_sprite_interleave

This is an old revision of the document!


Sprite Interleave Techniques

By Raistlin/Genesis Project Original idea and help from Christopher Jam for 20-pixel interleave technique

Intro

When using large arrays of sprites, eg. an 8×10 array, it can be tricky to do this without having gaps or problems. Annoyingly, C64 sprites are 21 pixels tall - so it's not possible to have this array without at least one row of sprites being around a bad line - where there simply aren't enough cycles to update all the sprite values.

This becomes especially problematic when you're dealing with varying D011 - eg. in the intro sequence of “The Dive” where we have bitmap upscroller behind a sprite array.

There are a few techniques available to help with these sprite arrays - and each has their pros/cons that I'll go into later.

16-pixel Sprite Interleave

Here we simply change $D018 at the right point, just before a badline, in order to update all sprites at the same time.

20-pixel Interleave

Here we update all of the sprite values around each 20th line of the screen - just after the 19th line out of every 20 - and we “fix” the tearing that occurs via a data trick.

To understand this technique, you need to understand what changing the sprite value means if you're part way through a sprite. If you change from sprite 64 to 72, for example, on the 6th line of a sprite, the sprite will continue to be drawn from the 6th line - but it will now be the 6th line of sprite 72 rather than 64.

The 20px technique, then, fixes the “glitch” you would normally see from changing the sprite value “too soon” by making sure that the equivalent line of each sprite is the same.

When the technique is working, roughly, you should have:- Y=[ 0, 20] .. lines 0-19 of row 0 and line 20 of either row 0 or row 1 Y=[ 20, 40] .. lines 20 and 0-18 of row 1 and line 19 of either row 1 or row 2 Y=[ 40, 60] .. lines 19-20 and 0-17 of row 2 and line 18 of either row 2 or row 3 … Y=[180,200] .. lines 12-20 and 0-11 of row 9

So we have several duplicates… line 20 of row 0 should be duplicated into row 1, line 19 of row 1 into row 2, etc. And we also need to “roll” the sprite data accordingly as well.

Given all that, you still need to update all 8 sprite values in as few cycles as possible. And you need to start the update from as close as possible to the right position.

For this, it's worth knowing about a neat little trick.

The most basic way to update the sprite values would of course be:

      ldx #$40
      stx ScreenAddr + $3f8 + 0
      inx
      stx ScreenAddr + $3f8 + 1
      ...
      inx
      stx ScreenAddr + $3f8 + 7

What matters to us here is the cycle count between our first write to the sprite values and our last. So that's 7×2 + 7×4 = 42. To reduce this, we can use the illegal opcode, SAX:-

      LDA #64 + 4
      LDX #fb
      SAX ScreenAddr + $3f8 + 0; <-- SAX will write A&X .. ie. 64 in this case (we mask out bit 2)
      STA ScreenAddr + $3f8 + 4
      INX
      SAX ScreenAddr + $3f8 + 1
      ...
      INX
      SAX ScreenAddr + $3f8 + 3
      STA ScreenAddr + $3f8 + 7

Here we have 7×4 + 3×2 = 34 cycles. So we've shaved off 8 - which of course can be very important with this sort of code.

Pros/Cons

As we don't need 2 screen buffers, this means:-

  • PRO: we save screen buffer memory;
  • PRO: in many cases we can simplify screen drawing code.

Compared to the alternative 16-pixel interleave technique (updating $D018):-

  • PRO: we only “waste” 1 pixel line of sprite data instead of 5.
  • CON: we need to duplicate some data - which can have a performance impact if the sprites are being drawn to in realtime
base/20-pixel_sprite_interleave.1602764130.txt.gz · Last modified: 2020-10-15 14:15 by raistlin