Maya Grab [Day 14] – Sprites

Today we have a really interesting topic to cover – sprites. I’ve really enjoyed learning (or re-learning again) how sprites work on the C64, so this would serve also as a little tutorial how to setup everything in just a few steps in BASIC. The C64 supports eight hardware sprites which are totally independent of other graphics or text shown on the screen. They can be positioned and moved; they support collision detection with each other or with the background, they can be single or multi colored, and can be stretched (double-sized) in both X and Y directions. In single color mode, sprites have a size of 24×21 pixels. Therefore only 63 bytes are needed per sprite since only 3 bytes per row are used. Some basic instructions how to deal with sprites can be also found here.

Sprites are controlled by the VIC-II graphics chip, so setting everything up is easy as writing a couple of values to the registers which begin at address 53248 ($D000); this address is also stored in my variable “V” for faster access. Let’s setup the first sprite (sprite 0):

POKE 2043,13
        set sprite 0 data pointer
FOR I=0 TO 62: READ A: POKE 832+I,A: NEXT
        read 63 bytes from the data block and store it from address 832

Sprite data pointers are registers which tell the VIC where are those 63 bytes of sprite data located. These registers are always behind the screen area, beginning at 2040 ($07F8) for the first to 2047 ($07FF) for the last sprite. The VIC can “find” sprite data only in his current 16kb memory bank; by default everything is in the first bank; there are four banks in total since there is only 64kb of ram available.

The first POKE is setting the sprite 0 data pointer to the 13th memory block of the first bank (memory block size is 64 bytes). The loop is reading data from the data block and storing the data from address 832 which is exactly the 13th block (64*13). If you look at the C64 memory map, there is a gap of 192 bytes between addresses 828 and 1019 for the Datasette buffer. The only problem is that there is only space for three sprites and the other five must be stored somewhere in the first 16kb; tricky in BASIC since the program can easily overwrite the sprite data.

POKE 53269,1
        sprite 0 enabled

Sprites can be enabled by setting different bits of the 53269 register (0 for none, 1 for sprite 0… 255 for all eight sprites).

POKE 53248, 50
        sprite 0 x position
POKE 53249, 50
        sprite 0 y position

Sprites can be positioned by setting value pairs to addresses from 53248 for sprite 0 to 53263 for sprite 7. There is one extra register at address 53264 for storing sprite x coordinates beyond 255 pixels.

POKE 53277,1
        sprite 0 double width
POKE 53271,1
        sprite 0 double height

Sprites can be stretched (double sized) in both directions by setting different bits of the 53277 and 53271 registers (0 for none, 1 for sprite 0… 255 for all eight sprites).

POKE 53287,0
        sprite 0 color black

Sprites can be single colored by setting first four bits to addresses from 53287 for sprite 0 to 53294 for sprite 7. For multi colored sprites two additional colors are shared between all sprites.

And that’s it. With the basics covered we can take a look at the code changes:

1515 V=53248: S=54272: C=646: B=832

There is a new variable B for holding the start address of the buffer where the sprite data will be stored.

5125 GOSUB 55100: REM INIT SPRITES

Since reading data from a data block and writing to the memory takes some time, the wait call on the title screen has been replaced by a call to initialize the sprites. Why wait, when we can do something useful while the title screen is showing.

55100 :
55105 REM – INIT SPRITES ————–
55110 :
55115 RESTORE
55120 GOSUB 56000
55125 GOSUB 57000
55130 GOSUB 58000
55135 RETURN

This routine rewinds the pointer to the data block with the command RESTORE and then calls the initialization routines for the first three sprites that are going to be used in the game, since there is no more free memory in the first bank. The original game uses four sprites in total, so the last one will be initialized later during the game, as soon as one of the currently loaded sprites is not needed anymore.

55500 :
55505 REM – DRAW SPRITE —————
55510 :
55515 POKE V+29,7
55520 POKE V+23,15
55525 POKE V+21,A
55530 RETURN
55535 :
55600 POKE V+29,6
55605 POKE V+23,14
55610 POKE V+21,1
55615 RETURN

There are two subroutines for drawing sprites. The first one draws a default scaled sprite specified in the variable A and the second one draws unscaled the first sprite.

56000 :
56005 REM – INIT GUARD —————-
56010 :
56015 POKE 2040,13
56020 FOR I=0 TO 62
56025 READ A: POKE B+I,A
56030 NEXT
56035 :
56040 POKE V+0,50
56045 POKE V+1,50
56050 POKE V+39,9
56055 RETURN
56060 :
56100 DATA   0, 28,  0,  0,127,  0
56105 DATA   0,107,  0,  0,255,128
56110 DATA   0, 99,  0,  0, 62,  0
56115 DATA   0, 28,  0,  0,255,128
56120 DATA   1,223,192,  3,111, 96
56125 DATA   6,119, 48, 12,123, 24
56130 DATA  24,125, 48,112, 82,224
56135 DATA   0,127,128,  0,127,  0
56140 DATA   0, 54,  0,  0, 99,  0
56145 DATA   0, 99,  0,  0,192,128
56150 DATA   3,128,224

Here’s how the initialization routine looks like for the first sprite. The funny thing is, when I was looking at the Maya Grab memory dump two weeks ago, I’ve seen that from the address $1DB0 to $20A8 a large amount of comma separated values is stored. I’ve suspected that this might be the data for the sprites. So I’ve wrote a simple program to read the data into four sprites and I was right. It saved me a lot of time; I don’t need to calculate all this data manually.

Program size: 27505 bytes, on disk 24892 bytes or 98 blocks
Free BASIC memory: 11406 bytes
Used BASIC commands: READ, DATA, RESTORE

Source code

* * * * *