There's a number of ways to update a sprite + color, DoDraw does it in 26
lda #SPRITEHEIGHT dcp SpriteTemp bcs DoDraw lda #0 .byte $2C DoDraw lda (GfxPtr),Y sta GRP0 ;+18 cycles lda (ColorPtr),y sta COLUP0
For Stay Frosty I used a mask table (which wastes a bunch of ROM space with 0s) and was able to get that down to 21 cycles
LDA (SpritePtr),y ; 5 AND (SpriteMask),y ; 5 STA GRP0 ; 3 LDA (ColorPtr),y ; 5 STA COLUP0 ; 3DPC uses just 14 cycles
LDA DF0DATAW ; 4 STA GRP0 ; 3 LDA DF1DATA ; 4 STA COLUP0 ; 3
At first glance using the DPC looked straightforward, it wasn't. I ended up downloading the source to Stella to see how DPC cartridge routines were implemented. The trick was instead of using GraphicDataPosition to prep the pointers, you need to use (2047 - GraphicDataPosition). There are 8 pointers that are set via DFxLOW and DFxHIGH (x = 0-7). Pointers 5, 6 and 7 appear to be used for music support, so if you've opted to have the DPC generate music this pointers might not be available for graphic data usage.
; set the DataStream Pointer 0 for the graphic data lda #<(2047 - GraphicDataPosition + HowFarDownScreen) sta DF0LOW lda #>(2047 - GraphicDataPosition + HowFarDownScreen) sta DF0HIGH ; set the DataStream Pointer 1 for the color data lda #<(2047 - ColorDataPosition + HowFarDownScreen) sta DF1LOW lda #>(2047 - ColorDataPosition + HowFarDownScreen) sta DF1HIGH
One the DataStream pointers are configured, just use LDA DFxDATA (x=0-7) to retrieve the data. Each access will automatically advance the data stream.
For sprite data you can set where in the data stream the sprite really exists, then use LDA DFxDATAW to read with the mask already applied.
; set the MASK for DataStream 0 lda #<(2047 - GraphicDataPosition) sta DF0TOP lda #<(2047 - GraphicDataPosition - ImageHeight) sta DF0BOTIn the demo move the joystick to position Frosty on the screen and hit Fire to switch between 6 images for Frosty. I forced 1 of the images to cross a page boundary and everything still works as expected.